Transport.RoundTrip should return a non-nil for failure to obtain a response.
This commit is contained in:
parent
53f3699cf9
commit
c0593b68d9
@ -101,15 +101,7 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|||||||
resp, err := rt.RoundTrip(req)
|
resp, err := rt.RoundTrip(req)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
message := fmt.Sprintf("Error trying to reach service: '%v'", err.Error())
|
return nil, fmt.Errorf("error trying to reach service: %w", err)
|
||||||
resp = &http.Response{
|
|
||||||
Header: http.Header{},
|
|
||||||
StatusCode: http.StatusServiceUnavailable,
|
|
||||||
Body: ioutil.NopCloser(strings.NewReader(message)),
|
|
||||||
}
|
|
||||||
resp.Header.Set("Content-Type", "text/plain; charset=utf-8")
|
|
||||||
resp.Header.Set("X-Content-Type-Options", "nosniff")
|
|
||||||
return resp, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if redirect := resp.Header.Get("Location"); redirect != "" {
|
if redirect := resp.Header.Get("Location"); redirect != "" {
|
||||||
|
@ -232,6 +232,12 @@ func (h *UpgradeAwareHandler) ServeHTTP(w http.ResponseWriter, req *http.Request
|
|||||||
proxy.Transport = h.Transport
|
proxy.Transport = h.Transport
|
||||||
proxy.FlushInterval = h.FlushInterval
|
proxy.FlushInterval = h.FlushInterval
|
||||||
proxy.ErrorLog = log.New(noSuppressPanicError{}, "", log.LstdFlags)
|
proxy.ErrorLog = log.New(noSuppressPanicError{}, "", log.LstdFlags)
|
||||||
|
if h.Responder != nil {
|
||||||
|
// if an optional error interceptor/responder was provided wire it
|
||||||
|
// the custom responder might be used for providing a unified error reporting
|
||||||
|
// or supporting retry mechanisms by not sending non-fatal errors to the clients
|
||||||
|
proxy.ErrorHandler = h.Responder.Error
|
||||||
|
}
|
||||||
proxy.ServeHTTP(w, newReq)
|
proxy.ServeHTTP(w, newReq)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -901,7 +901,8 @@ func TestFlushIntervalHeaders(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyHandler := NewUpgradeAwareHandler(backendURL, nil, false, false, nil)
|
responder := &fakeResponder{t: t}
|
||||||
|
proxyHandler := NewUpgradeAwareHandler(backendURL, nil, false, false, responder)
|
||||||
|
|
||||||
frontend := httptest.NewServer(proxyHandler)
|
frontend := httptest.NewServer(proxyHandler)
|
||||||
defer frontend.Close()
|
defer frontend.Close()
|
||||||
@ -924,6 +925,53 @@ func TestFlushIntervalHeaders(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fakeRT struct {
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frt *fakeRT) RoundTrip(*http.Request) (*http.Response, error) {
|
||||||
|
return nil, frt.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestErrorPropagation checks if the default transport doesn't swallow the errors by providing a fakeResponder that intercepts and stores the error.
|
||||||
|
func TestErrorPropagation(t *testing.T) {
|
||||||
|
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
panic("unreachable")
|
||||||
|
}))
|
||||||
|
defer backend.Close()
|
||||||
|
|
||||||
|
backendURL, err := url.Parse(backend.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
responder := &fakeResponder{t: t}
|
||||||
|
expectedErr := errors.New("nasty error")
|
||||||
|
proxyHandler := NewUpgradeAwareHandler(backendURL, &fakeRT{err: expectedErr}, true, false, responder)
|
||||||
|
|
||||||
|
frontend := httptest.NewServer(proxyHandler)
|
||||||
|
defer frontend.Close()
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("GET", frontend.URL, nil)
|
||||||
|
req.Close = true
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(req.Context(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
|
||||||
|
res, err := frontend.Client().Do(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Get: %v", err)
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
if res.StatusCode != fakeStatusCode {
|
||||||
|
t.Fatalf("unexpected HTTP status code returned: %v, expected: %v", res.StatusCode, fakeStatusCode)
|
||||||
|
}
|
||||||
|
if !strings.Contains(responder.err.Error(), expectedErr.Error()) {
|
||||||
|
t.Fatalf("responder got unexpected error: %v, expected the error to contain %q", responder.err.Error(), expectedErr.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// exampleCert was generated from crypto/tls/generate_cert.go with the following command:
|
// exampleCert was generated from crypto/tls/generate_cert.go with the following command:
|
||||||
// go run generate_cert.go --rsa-bits 1024 --host example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
// go run generate_cert.go --rsa-bits 1024 --host example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
||||||
var exampleCert = []byte(`-----BEGIN CERTIFICATE-----
|
var exampleCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||||
|
@ -237,7 +237,7 @@ func (r *responder) Object(statusCode int, obj runtime.Object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *responder) Error(_ http.ResponseWriter, _ *http.Request, err error) {
|
func (r *responder) Error(_ http.ResponseWriter, _ *http.Request, err error) {
|
||||||
http.Error(r.w, err.Error(), http.StatusInternalServerError)
|
http.Error(r.w, err.Error(), http.StatusServiceUnavailable)
|
||||||
}
|
}
|
||||||
|
|
||||||
// these methods provide locked access to fields
|
// these methods provide locked access to fields
|
||||||
|
@ -561,7 +561,7 @@ func TestGetContextForNewRequest(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !strings.Contains(string(body), "Error trying to reach service: 'context deadline exceeded'") {
|
if !strings.Contains(string(body), "context deadline exceeded") {
|
||||||
t.Error(string(body))
|
t.Error(string(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user