Repository navigation
Expand file tree
/
Copy patherrors_http.go
More file actions
61 lines (57 loc) · 2.11 KB
/
Copy patherrors_http.go
File metadata and controls
61 lines (57 loc) · 2.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package ragy
import (
"context"
"errors"
"fmt"
"net"
"net/http"
)
// WrapTransportError classifies errors from [http.Client.Do] and similar transports.
// [context.Canceled] is returned unchanged (caller cancellation, not a retry target).
// Other errors are wrapped with ErrUnavailable for retry policies.
func WrapTransportError(err error) error {
if err == nil {
return nil
}
if errors.Is(err, context.Canceled) {
return err
}
if errors.Is(err, context.DeadlineExceeded) {
return fmt.Errorf("%w: %w", ErrUnavailable, err)
}
var ne net.Error
if errors.As(err, &ne) && ne.Timeout() {
return fmt.Errorf("%w: %w", ErrUnavailable, err)
}
return fmt.Errorf("%w: %w", ErrUnavailable, err)
}
// ErrorFromHTTPResponse maps an HTTP status and response body snippet to a domain error.
// 4xx except 429 → ErrInvalidArgument (do not retry). 429 and 5xx → ErrUnavailable.
// Other status codes → ErrProtocol (unexpected success or redirect without following).
func ErrorFromHTTPResponse(statusCode int, prefix, body string) error {
switch {
case statusCode == http.StatusTooManyRequests:
return fmt.Errorf("%w: %s: status %d: %s", ErrUnavailable, prefix, statusCode, body)
case statusCode >= http.StatusInternalServerError:
return fmt.Errorf("%w: %s: status %d: %s", ErrUnavailable, prefix, statusCode, body)
case statusCode >= http.StatusBadRequest:
return fmt.Errorf("%w: %s: status %d: %s", ErrInvalidArgument, prefix, statusCode, body)
default:
return fmt.Errorf("%w: %s: unexpected status %d: %s", ErrProtocol, prefix, statusCode, body)
}
}
// WrapBackendError wraps database or RPC errors as ErrUnavailable when not already classified.
// [context.Canceled] is returned unchanged. Errors already matching ErrInvalidArgument,
// ErrUnavailable, or ErrProtocol are returned unchanged.
func WrapBackendError(err error, operation string) error {
if err == nil {
return nil
}
if errors.Is(err, context.Canceled) {
return err
}
if errors.Is(err, ErrInvalidArgument) || errors.Is(err, ErrUnavailable) || errors.Is(err, ErrProtocol) {
return err
}
return fmt.Errorf("%w: %s: %w", ErrUnavailable, operation, err)
}