-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HEAD request returning 200 OK with no body will hang indefinitely #1824
Comments
Head requet trigger Skip Body pattern. If your heading server send chunked body(not follow the HTTP protocol, not empty body) as head response. fasthttp client must hang forever. Line 1439 in 9fe0bc2
to Line 1457 in 9fe0bc2
become below: if !resp.mustSkipBody() {
err = resp.ReadBody(r, maxBodySize)
if err != nil {
if isConnectionReset(err) {
return nil
}
return err
}
//}
if resp.Header.ContentLength() == -1 && !resp.StreamBody {
err = resp.Header.ReadTrailer(r)
if err != nil && err != io.EOF {
if isConnectionReset(err) {
return nil
}
return err
}
}
} But now, if your sever send head or other without body chunked response you never read trialer header. but I think it ok. A better approach is to have the ReadTrailer method perform the check, and if the chunked ending is not correct, it should immediately return an error rather than waiting indefinitely for the server to send a valid chunked ending byte stream. Currently, fasthttp mixes the handling of user-defined skipBody and skipBody required by the HTTP protocol, which can lead to other issues. A comprehensive fix is needed. However, the code above can temporarily solve your problem effectively. |
Test Replay:func TestClientSkipRespBodyDontReadTrailer(t *testing.T) {
server := Server{Handler: func(ctx *RequestCtx) {
_, err := ctx.WriteString(randomstring.HumanFriendlyString(1024))
ctx.Response.SetBodyStream(strings.NewReader(randomstring.HumanFriendlyString(1024)), -1)
assert.NoErr(t, err)
}}
req := AcquireRequest()
defer func() {
ReleaseRequest(req)
}()
req.SetRequestURI("http://127.0.0.1:7070")
resp := AcquireResponse()
resp.SkipBody = true
//
pcs := fasthttputil.NewPipeConns()
cliCon, serCon := pcs.Conn1(), pcs.Conn2()
err := cliCon.SetReadDeadline(time.Now().Add(time.Second*5))
assert.NoErr(t, err)
go func() {
_, err := req.WriteTo(cliCon)
assert.NoErr(t, err)
err = resp.Read(bufio.NewReader(cliCon))
assert.NoErr(t, err)
err = cliCon.Close()
assert.NoErr(t, err)
}()
//
err = server.serveConn(serCon)
if err != nil && !strings.Contains(err.Error(), "closed") {
t.Fatal(err)
}
} Result
After FIx
But this is a tmp fix. |
What is happening is that the client doesn't expect a response body for the #1825 here are some fixes for issues I found while investigating this. But it doesn't fix your main issue of a HEAD having a response. This is just invalid HTTP and we won't support this. |
Thanks you guys for thoughtful research! Now I understand problem more. There is one caveat: my code hangs even before reaching func (resp *Response) ReadLimitBody(r *bufio.Reader, maxBodySize int) error {
resp.resetSkipHeader()
err := resp.Header.Read(r) <<<<<<<<<<
if err != nil {
return err
} So even before considering |
ServerIn many aspects, fasthttp aligns with the Go official net/http package. In the current implementation of net/http, if a response is for a HEAD method, it will never include the Transfer-Encoding: chunked header. Consequently, it also won't include any Trailer headers. if w.req.Method == "HEAD" || !bodyAllowedForStatus(code) || code == StatusNoContent {
// Response has no body.
delHeader("Transfer-Encoding")
} else if hasCL { FasthttpServer: Lines 2064 to 2075 in a1db411
The fasthttp server's handling of responses that shouldn't have a body (including certain status codes or HEAD method requests) is not consistent with net/http. It simply omits the response body, while still sending everything else.This issue should be resolved soon. Clientnet/http/client: fasthttp/client: After this pull request, the behavior of the fasthttp client is largely consistent with net/http/client. It only reads the parts it should, according to the specifications, leaving any remaining parts (if any) in the connection or partially pre-read in the bufio buffer. However, handling this leftover data will be a concern for the next response parsing. By the way, if this leftover data is crafted appropriately, it could cause the parsing to hang indefinitely when treated as response headers, rather than reporting a parsing error. So, if you find that some code hangs while parsing response/request headers, it might not be an issue with the headers of the current response/request but rather leftover data from the previous one, especially with long connections. Therefore, you should debug to see exactly what data is causing the header parsing to hang. sending HEAD with Transfer-Encoding: chunked with no valid byte termination 0\r\n\r\n results in error in fasthttp? Lines 2738 to 2746 in a1db411
|
To summarise:
Debugging shows that it hangs while reading Headers (before reading Body) from connection. This is all I was able to debug with my knowledge. |
My advise would be to, either set a read timeout so bad requests like this won't hang, or to make sure the client speaks proper HTTP. |
Hi!
While working with Fiber and it's Proxy middleware https://github.com/gofiber/fiber/tree/main/middleware/proxy I've encountered weird bug.
When I am heading to
HEAD http://myproxy
, server returns:When I am trying to do the same with
client.Do()
it hangs forever hereI am not that experienced with fasthttp, but it might be related with
Transfer-Encoding: chunked
returned by the server for this HEAD request.The text was updated successfully, but these errors were encountered: