Giter VIP home page Giter VIP logo

Comments (8)

valyala avatar valyala commented on August 29, 2024 2

@szank, a few remarks about your code snippets:

  • there is no need in ResetBody after Reset, since Reset resets all the request and response structs including body.
  • Try using default client, i.e. call fasthttp.Do() instead of client.Do(). Default client dialer should work reasonably in the majority cases for TCP transport. If it doesn't work in your case, file a bug. Use custom DialFunc only if your client uses non-TCP transport such as unix, udp, sctp or arbitrary custom transport.
  • Use fasthttp.Get() instead of fasthttp.Do() if you need only status code and response body. fasthttp.Get hides all the complexities related to request and response setup and re-use.

I am running everything on my local machine, and there is haproxy between the client and server. I didn't see those problems if i don't connect through haproxy.

Client returns io.EOF if the server closes connection before the client reads the response. Verify whether EOF is returned for each Do call. If not, then I guess haproxy strips Connection: close header sent from the server, then notices that the server closed connection and closes the corresponding connection to fasthttp. The client automatically retries GET and HEAD (aka idempotent) requests in such cases. But I guess you send non-GET requests.

These are just my guesses. I could provide more info if you provide request and response headers sniffed on the connection. Hint you can use custom DialFunc for such sniffing. This function should wrap the connection returned from net.Dial into your struct, which overrides Read() and Write() methods. Something like the following:

type sniffConn struct {
    *net.Conn
}

func (c *sniffConn) Read(p []byte) (int, error) {
    n, err := c.Conn.Read(p)
    sniffData(p[:n])
    return n, err
}

func (c *sniffConn) Write(p []byte) (int, error) {
   n, err := c.Conn.Write(p)
   sniffData(p[:n])
   return n, err
}

func sniffDial(addr string) (net.Conn, error) {
    c, err := net.Dial("tcp", addr)
    if err != nil {
        return nil, err
    }
    return &sniffConn{c}
}

from fasthttp.

szank avatar szank commented on August 29, 2024

Wow, thanks for the long response :) I will modify my code to incorporate your tips and try again.

Not all the requests are returning EOF, and I guess you are pretty close to truth with your guesses.
I will have a closer look at that.

from fasthttp.

szank avatar szank commented on August 29, 2024

I am not using the default client because I want to limit the maximum number of connections per host.
I did snip this part of the code from the example, because it wasn't necessary to include it.

Of course I could change the global MaxConnsPerHost value, but it doesn't "smell" nice, and if I change it after I call fasthttp.Do(), the effects will vary depending if the Client was taken from the pool or initialized.

I will remove the dial func from the client, and dig into the haproxy though.

from fasthttp.

valyala avatar valyala commented on August 29, 2024

FYI, fasthttp didn't send Connection: close response header before closing the connection. This has been fixed today at 71e8c28 .

from fasthttp.

szank avatar szank commented on August 29, 2024

Hi, a small update :

there is no need in ResetBody after Reset, since Reset resets all the request and response structs including body.

func (resp *Response) resetSkipHeader() called in func (resp *Response) Reset()
and func (resp *Response) ResetBody() are identical. That's why I didn't notice that Reset also closes the body.

Use fasthttp.Get() instead of fasthttp.Do() if you need only status code and response body. fasthttp.Get() hides all the complexities related to request and response setup and re-use.

A question about the fasthttp.Do() and fasthttp.Get() : the Args struct passed to those methods contain only the HTTP query parameters ? Is there a way to specify the header fields while using those methods ?

I have used tcpdump and noticed, that sometimes after I send a request, the haproxy response with ACK, RST TCP flags. At the same time it closes the connection to the destination server.

As the golang http package Resposne struct contains the reader interface it won't return an error in such case, just the response reader interface will return EOF when tried to read from.

Now, calls like ioutil.ReadAll() will eat the EOF, and just return an empty byte slice.

I am ok with checking for the EOF error after a call to the fasthttp client Do() method, but I think returning an empty slice with the response data would be more aligned with the behaviour of the default http client.

Also, i wanted to say I didn't see any Connection: close headers there, but it seems to be fixed now :)

Regards,
Maciej

from fasthttp.

valyala avatar valyala commented on August 29, 2024

A question about the fasthttp.Do() and fasthttp.Get() : the Args struct passed to those methods contain only the HTTP query parameters ? Is there a way to specify the header fields while using those methods ?

No, use Do() if you need overriding headers and/or POST body.

I am ok with checking for the EOF error after a call to the fasthttp client Do() method, but I think returning an empty slice with the response data would be more aligned with the behaviour of the default http client.

I should think more about this

from fasthttp.

valyala avatar valyala commented on August 29, 2024

I am ok with checking for the EOF error after a call to the fasthttp client Do() method, but I think returning an empty slice with the response data would be more aligned with the behaviour of the default http client.

I returned to this issue and noticed that io.EOF may be returned from client.Do only if the provided buffered reader is closed before the first response byte read. If the first response byte is read, the returned error will differ from io.EOF.

See added tests in referenced commits for details.

So in your case the server was closing connection after returning response without explicit Connection: close response header. So the next attempt to read the next response from this connection resulted in io.EOF error.

Closing this issue.

from fasthttp.

valyala avatar valyala commented on August 29, 2024

Now the client will return more meaningful ErrConnectionClosed instead of io.EOF in such cases.

from fasthttp.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.