feat: support bounded chunked request bodies
This commit is contained in:
@@ -53,7 +53,7 @@ func TestServerKeepAliveAndApplicationBridge(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerRejectsUnsupportedBodyAndLimits(t *testing.T) {
|
||||
func TestServerRejectsMalformedBodyAndLimits(t *testing.T) {
|
||||
application := omni.NewWithRouter(netrouter.New())
|
||||
application.POST("/body", func(ctx *omni.Context) error { return ctx.OK("ok") })
|
||||
|
||||
@@ -67,28 +67,85 @@ func TestServerRejectsUnsupportedBodyAndLimits(t *testing.T) {
|
||||
go func() { done <- server.Serve(context.Background(), listener) }()
|
||||
defer stopServer(t, server, listener, done)
|
||||
|
||||
for _, requestText := range []string{
|
||||
"POST /body HTTP/1.1\r\nHost: test\r\nTransfer-Encoding: chunked\r\n\r\n4\r\ntest\r\n0\r\n\r\n",
|
||||
"POST /body HTTP/1.1\r\nHost: test\r\nContent-Length: 5\r\n\r\ntest!",
|
||||
} {
|
||||
tests := []struct {
|
||||
request string
|
||||
status int
|
||||
}{
|
||||
{
|
||||
request: "POST /body HTTP/1.1\r\nHost: test\r\nTransfer-Encoding: chunked\r\n\r\nZ\r\nbad\r\n0\r\n\r\n",
|
||||
status: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
request: "POST /body HTTP/1.1\r\nHost: test\r\nContent-Length: 5\r\n\r\ntest!",
|
||||
status: http.StatusRequestEntityTooLarge,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
conn, err := net.Dial("tcp", listener.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, _ = io.WriteString(conn, requestText)
|
||||
_, _ = io.WriteString(conn, test.request)
|
||||
response, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: http.MethodPost})
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
t.Fatal(err)
|
||||
}
|
||||
response.Body.Close()
|
||||
if response.StatusCode != http.StatusNotImplemented && response.StatusCode != http.StatusRequestEntityTooLarge {
|
||||
t.Fatalf("unexpected protocol status %d", response.StatusCode)
|
||||
if response.StatusCode != test.status {
|
||||
t.Fatalf("expected status %d, got %d", test.status, response.StatusCode)
|
||||
}
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerChunkedBodyAndTrailers(t *testing.T) {
|
||||
application := omni.NewWithRouter(netrouter.New())
|
||||
application.POST("/body", func(ctx *omni.Context) error {
|
||||
body, err := io.ReadAll(ctx.Request().Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.OK(map[string]string{
|
||||
"body": string(body),
|
||||
"trailer": ctx.Request().Trailer.Get("X-Trace"),
|
||||
"transfer": strings.Join(ctx.Request().TransferEncoding, ","),
|
||||
})
|
||||
})
|
||||
server, listener, done := startServer(t, application)
|
||||
defer stopServer(t, server, listener, done)
|
||||
|
||||
request := "POST /body HTTP/1.1\r\nHost: test\r\nTransfer-Encoding: chunked\r\nConnection: close\r\n\r\n" +
|
||||
"4;source=wire\r\nWiki\r\n5\r\npedia\r\n0\r\nX-Trace: yes\r\n\r\n"
|
||||
response := rawResponseMethod(t, listener.Addr().String(), http.MethodPost, request)
|
||||
defer response.Body.Close()
|
||||
body, err := io.ReadAll(response.Body)
|
||||
if err != nil || response.StatusCode != http.StatusOK || !strings.Contains(string(body), `"body":"Wikipedia"`) || !strings.Contains(string(body), `"trailer":"yes"`) || !strings.Contains(string(body), `"transfer":"chunked"`) {
|
||||
t.Fatalf("unexpected chunked response: status=%d body=%s error=%v", response.StatusCode, body, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerChunkedBodyLimit(t *testing.T) {
|
||||
application := omni.NewWithRouter(netrouter.New())
|
||||
application.POST("/body", func(ctx *omni.Context) error { return ctx.OK("unexpected") })
|
||||
server := native.New(application)
|
||||
server.MaxBodyBytes = 4
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
done := make(chan error, 1)
|
||||
go func() { done <- server.Serve(context.Background(), listener) }()
|
||||
defer stopServer(t, server, listener, done)
|
||||
|
||||
request := "POST /body HTTP/1.1\r\nHost: test\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n0\r\n\r\n"
|
||||
response := rawResponseMethod(t, listener.Addr().String(), http.MethodPost, request)
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != http.StatusRequestEntityTooLarge {
|
||||
t.Fatalf("expected chunked body limit status 413, got %d", response.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerSlowBodyTimesOutBeforeHandler(t *testing.T) {
|
||||
var handlerCalls atomic.Int32
|
||||
application := omni.NewWithRouter(netrouter.New())
|
||||
@@ -210,9 +267,9 @@ func TestServerProtocolBoundaries(t *testing.T) {
|
||||
status: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "chunked",
|
||||
request: "POST /health HTTP/1.1\r\nHost: test\r\nTransfer-Encoding: chunked\r\n\r\n",
|
||||
status: http.StatusNotImplemented,
|
||||
name: "malformed chunked",
|
||||
request: "POST /health HTTP/1.1\r\nHost: test\r\nTransfer-Encoding: chunked\r\n\r\nZ\r\n\r\n",
|
||||
status: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "expectation",
|
||||
|
||||
Reference in New Issue
Block a user