check / check (push) Canceled after 0s
The image routes answered 500 and logged at error level when the upstream host had no address, when the fetch failed before the upstream answered, such as on a refused connection, or when the fetcher refused a redirect. The fetcher now marks such a failure with ErrUpstreamRequestFailed; a cancelled fetch is not marked. respondImageError answers it, and ErrInvalidHost, 502 "upstream error" and logs them at warn level. A timeout that does not match context.DeadlineExceeded, such as the TLS handshake timeout, is now ErrUpstreamTimeout and answers 504. README Routes lists these answers. Model: opus-5-5
87 lines
2.0 KiB
Go
87 lines
2.0 KiB
Go
package httpfetcher
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"os"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestFetchFailedBeforeAnswer checks the error of a fetch whose connection
|
|
// fails:
|
|
// - refused: ErrUpstreamRequestFailed.
|
|
// - timed out: ErrUpstreamTimeout, even for a timeout error that does not
|
|
// match context.DeadlineExceeded, such as the TLS handshake timeout.
|
|
// - cancelled by its caller: context.Canceled, not ErrUpstreamRequestFailed.
|
|
func TestFetchFailedBeforeAnswer(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
dial func(cancel context.CancelFunc) error
|
|
wantErr error
|
|
wantNotErr error
|
|
}{
|
|
{
|
|
name: "connection refused",
|
|
dial: func(context.CancelFunc) error {
|
|
return syscall.ECONNREFUSED
|
|
},
|
|
wantErr: ErrUpstreamRequestFailed,
|
|
wantNotErr: context.Canceled,
|
|
},
|
|
{
|
|
name: "timed out while connecting",
|
|
dial: func(context.CancelFunc) error {
|
|
return os.ErrDeadlineExceeded
|
|
},
|
|
wantErr: ErrUpstreamTimeout,
|
|
wantNotErr: ErrUpstreamRequestFailed,
|
|
},
|
|
{
|
|
name: "cancelled while connecting",
|
|
dial: func(cancel context.CancelFunc) error {
|
|
cancel()
|
|
|
|
return context.Canceled
|
|
},
|
|
wantErr: context.Canceled,
|
|
wantNotErr: ErrUpstreamRequestFailed,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, cancel := context.WithCancel(testContext(t))
|
|
defer cancel()
|
|
|
|
f := New(&Config{
|
|
Timeout: 30 * time.Second,
|
|
MaxResponseSize: 50 << 20,
|
|
AllowHTTP: true,
|
|
MaxConnectionsPerHost: 20,
|
|
MaxConnections: 64,
|
|
DialContext: func(context.Context, string, string) (net.Conn, error) {
|
|
return nil, tt.dial(cancel)
|
|
},
|
|
})
|
|
|
|
_, err := f.Fetch(ctx, upstreamSource("/image"), "", "")
|
|
t.Logf("Fetch() error: %v", err)
|
|
|
|
if !errors.Is(err, tt.wantErr) {
|
|
t.Errorf("Fetch() error = %v, want %v", err, tt.wantErr)
|
|
}
|
|
|
|
if errors.Is(err, tt.wantNotErr) {
|
|
t.Errorf("Fetch() error = %v, must not be %v", err, tt.wantNotErr)
|
|
}
|
|
})
|
|
}
|
|
}
|