Skip to content

Commit 600e473

Browse files
authored
Mark EOF errors coming from http request as downstream (#1192)
* Mark EOF errors comming from http request as downstream * Fix comment * Only http io.EOF errors are cosidered downstream * Update comment
1 parent 2c603f5 commit 600e473

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

experimental/status/status_source.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"crypto/x509"
66
"errors"
77
"fmt"
8+
"io"
89
"net"
910
"net/http"
1011
"net/url"
@@ -142,7 +143,8 @@ func IsDownstreamHTTPError(err error) bool {
142143
return IsDownstreamError(err) ||
143144
isConnectionResetOrRefusedError(err) ||
144145
isDNSNotFoundError(err) ||
145-
isTLSCertificateVerificationError(err)
146+
isTLSCertificateVerificationError(err) ||
147+
isHTTPEOFError(err)
146148
}
147149

148150
// InCancelledError returns true if err is context.Canceled or is gRPC status Canceled.
@@ -202,6 +204,20 @@ func isTLSCertificateVerificationError(err error) bool {
202204
return false
203205
}
204206

207+
// isHTTPEOFError returns true if the error is an EOF error inside of url.Error or net.OpError, indicating the connection was closed prematurely by server
208+
func isHTTPEOFError(err error) bool {
209+
var netErr *net.OpError
210+
if errors.As(err, &netErr) {
211+
return errors.Is(netErr.Err, io.EOF)
212+
}
213+
214+
var urlErr *url.Error
215+
if errors.As(err, &urlErr) {
216+
return errors.Is(urlErr.Err, io.EOF)
217+
}
218+
return false
219+
}
220+
205221
type sourceCtxKey struct{}
206222

207223
// SourceFromContext returns the source stored in the context.

experimental/status/status_source_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"crypto/x509"
66
"errors"
77
"fmt"
8+
"io"
89
"net"
910
"net/url"
1011
"os"
@@ -248,6 +249,36 @@ func TestIsDownstreamHTTPError(t *testing.T) {
248249
err: x509.UnknownAuthorityError{},
249250
expected: true,
250251
},
252+
{
253+
name: "nil error",
254+
err: nil,
255+
expected: false,
256+
},
257+
{
258+
name: "io.EOF error",
259+
err: io.EOF,
260+
expected: false,
261+
},
262+
{
263+
name: "url io.EOF error",
264+
err: &url.Error{Op: "Get", URL: "https://example.com", Err: io.EOF},
265+
expected: true,
266+
},
267+
{
268+
name: "net op io.EOF error",
269+
err: &net.OpError{Err: io.EOF},
270+
expected: true,
271+
},
272+
{
273+
name: "wrapped url io.EOF error",
274+
err: fmt.Errorf("wrapped: %w", &url.Error{Op: "Get", URL: "https://example.com", Err: io.EOF}),
275+
expected: true,
276+
},
277+
{
278+
name: "joined error with io.EOF",
279+
err: errors.Join(io.EOF, &url.Error{Op: "Get", URL: "https://example.com", Err: io.EOF}),
280+
expected: true,
281+
},
251282
}
252283
for _, tc := range tcs {
253284
t.Run(tc.name, func(t *testing.T) {

0 commit comments

Comments
 (0)