valyala / fasthttp

Fast HTTP package for Go. Tuned for high performance. Zero memory allocations in hot paths. Up to 10x faster than net/http
MIT License
21.88k stars 1.76k forks source link

HEAD request hangs #1692

Closed NitinRamesh25 closed 10 months ago

NitinRamesh25 commented 10 months ago

I noticed that a request with method HEAD successfully gets sent to the server, but the server response isn't returned by the fasthttp client. Hence the Do method gets stuck, without giving the response.

The same works fine with net/http package.

Note:

The following are the sample code I used for testing.

fasthttp

func main() {
    req := &fasthttp.Request{}
    req.SetRequestURI("<Added the relative URL here>")

        req.Header.Add("Authorization", "...")
    req.Header.SetMethod("HEAD") // The "GET" endpoint works fine though.

    req.SetHost("<Added my host here>")
    req.URI().SetScheme("https")

    resp := &fasthttp.Response{}

    fmt.Println("Sending request")

    client := &fasthttp.Client{}
    err := client.Do(req, resp) // This method call gets stuck.
    if err != nil {
        fmt.Println("Failed do: " + err.Error())
        return
    }

    fmt.Println(resp)
}

net/http

func main() {
    req, err := http.NewRequest(http.MethodHead, "<Added the complete URL here>", nil)
    if err != nil {
        fmt.Println("Failed to create request " + err.Error())
    }

        req.Header.Add("Authorization", "...")

        fmt.Println("Sending request")

    client := &http.Client{}
    resp, err := client.Do(req) // Works fine and returns the proper response.
    if err != nil {
        fmt.Println("Failed do: " + err.Error())
        return
    }

    fmt.Println(resp)
}
erikdubbelboer commented 10 months ago

This seems to work just fine, can you provide a complete sample where it doesn't work?

func main() {
    go func() {
        if err := http.ListenAndServe(":6060", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            w.WriteHeader(http.StatusOK)
        })); err != nil {
            panic(err)
        }
    }()

    // Give the server a second to start.
    time.Sleep(time.Second)

    req := &fasthttp.Request{}
    req.SetRequestURI("http://localhost:6060")

    req.Header.Add("Authorization", "...")
    req.Header.SetMethod("HEAD")

    req.SetHost("localhost:6060")
    req.URI().SetScheme("http")

    resp := &fasthttp.Response{}

    fmt.Println("Sending request")

    client := &fasthttp.Client{}
    err := client.Do(req, resp)
    if err != nil {
        fmt.Println("Failed do: " + err.Error())
        return
    }

    fmt.Println(resp)
}
NitinRamesh25 commented 10 months ago

Looks like my API proxy was sending chunked response. But the terminating chunk wasn't sent properly, thus leading to the client to wait forever.

Even though the chunk isn't terminated properly, I could get this to work for now by setting the two fields in the Response struct fasthttp.Response{SkipBody: true, StreamBody: true}