Ela
Ela

Reputation: 3412

how to catch "http: server closed idle connection" error

In my go application I am getting the following error: "http: server closed idle connection". I would like to catch it and retry my http connection if it's encountered.

I found that this error comes from the "net/http" package and furthermore from the transport implementation. In particular it's defined here I get it wrapped in url.Error but this is all I was able to find out. Do you know how I can actually catch this error?

Edit: I am using elastic search client, which in turn is using the net/http. From the client I get the above mentioned error and would like to retry my elastic search request as being transient. For now the way I am catching transient errors is:

if urlErr, ok := err.(*url.Error); ok && (urlErr.Temporary() || urlErr.Err == io.EOF) {
    return retryRequest()

}

Upvotes: 8

Views: 7617

Answers (2)

kimbo
kimbo

Reputation: 2703

As Gray Fox mentioned, Go will usually retry these requests. However, this is not the case for requests that aren't "idempotent". I was running into a few of these errors on POST requests, and after searching around I found this Github issue that says:

The HTTP Transport would normally retry those requests, but because they're POST requests they're not idempotent and Go conservatively assumes it might be unsafe to retry them.

Later in the issue someone mentioned that many banking APIs use some variant of the Idempotent-Key header to make them "safe". Stripe is a company that uses that header, and this is what their API docs say about it:

The API supports idempotency for safely retrying requests without accidentally performing the same operation twice.

So there you go, that's what the header is for.

In short, if you want Go's HTTP Transport to retry POST requests (and other non-idempotent requests), you need to include either the Idempotency-Key or X-Idempotency-Key header. That doesn't necessarily make those requests idempotent, but it will make them retry-able from net/http's perspective.

Links:

Upvotes: 4

Gray Fox
Gray Fox

Reputation: 673

Comments in net/http/transport.go say this (located here):

if err == errServerClosedIdle {
    // The server replied with io.EOF while we were trying to
    // read the response. Probably an unfortunately keep-alive
    // timeout, just as the client was writing a request.
    return true
}

Go will attempt to retry the request if it finds a non-nil http.Request.GetBody (found here), so I think it is expected to retry the request (or provide a GetBody function).

As for the leading error cause, you might want to check server's keep-alive functionality, my guess is that server is sending TCP connection reset (interpreted as io.EOF) prematurely. You might want to try disabling keep-alives and see if that changes anything.

Upvotes: 5

Related Questions