| OLD | NEW |
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package lhttp | 5 package lhttp |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "bytes" | 8 "bytes" |
| 9 "encoding/json" | 9 "encoding/json" |
| 10 "fmt" | 10 "fmt" |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 76 attempts++ | 76 attempts++ |
| 77 req, err := rgen() | 77 req, err := rgen() |
| 78 if err != nil { | 78 if err != nil { |
| 79 return err | 79 return err |
| 80 } | 80 } |
| 81 | 81 |
| 82 resp, err := c.Do(req) | 82 resp, err := c.Do(req) |
| 83 if err != nil { | 83 if err != nil { |
| 84 // Retry every error. This is sad when you speci
fy an invalid hostname but | 84 // Retry every error. This is sad when you speci
fy an invalid hostname but |
| 85 // it's better than failing when DNS resolution
is flaky. | 85 // it's better than failing when DNS resolution
is flaky. |
| 86 » » » » return errorHandler(nil, errors.WrapTransient(er
r)) | 86 » » » » return errorHandler(nil, retry.Tag.Apply(err)) |
| 87 } | 87 } |
| 88 status = resp.StatusCode | 88 status = resp.StatusCode |
| 89 | 89 |
| 90 switch { | 90 switch { |
| 91 case status == 408, status == 429, status >= 500: | 91 case status == 408, status == 429, status >= 500: |
| 92 // The HTTP status code means the request should
be retried. | 92 // The HTTP status code means the request should
be retried. |
| 93 » » » » err = errors.WrapTransient( | 93 » » » » err = errors.Reason("http request failed: %(text
)s (HTTP %(code)d)"). |
| 94 » » » » » fmt.Errorf("http request failed: %s (HTT
P %d)", http.StatusText(status), status)) | 94 » » » » » D("text", http.StatusText(status)).D("co
de", status).Tag(retry.Tag).Err() |
| 95 case status == 404 && strings.HasPrefix(req.URL.Path, "/
_ah/api/"): | 95 case status == 404 && strings.HasPrefix(req.URL.Path, "/
_ah/api/"): |
| 96 // Endpoints occasionally return 404 on valid re
quests! | 96 // Endpoints occasionally return 404 on valid re
quests! |
| 97 logging.Infof(ctx, "lhttp.Do() got a Cloud Endpo
ints 404: %#v", resp.Header) | 97 logging.Infof(ctx, "lhttp.Do() got a Cloud Endpo
ints 404: %#v", resp.Header) |
| 98 » » » » err = errors.WrapTransient( | 98 » » » » err = errors.Reason("http request failed (endpoi
nts): %(text)s (HTTP %(code)d)"). |
| 99 » » » » » fmt.Errorf("http request failed (endpoin
ts): %s (HTTP %d)", http.StatusText(status), status)) | 99 » » » » » D("text", http.StatusText(status)).D("co
de", status).Tag(retry.Tag).Err() |
| 100 case status >= 400: | 100 case status >= 400: |
| 101 // Any other failure code is a hard failure. | 101 // Any other failure code is a hard failure. |
| 102 err = fmt.Errorf("http request failed: %s (HTTP
%d)", http.StatusText(status), status) | 102 err = fmt.Errorf("http request failed: %s (HTTP
%d)", http.StatusText(status), status) |
| 103 default: | 103 default: |
| 104 // The handler may still return a retry.Error to
indicate that the request | 104 // The handler may still return a retry.Error to
indicate that the request |
| 105 // should be retried even on successful status c
ode. | 105 // should be retried even on successful status c
ode. |
| 106 return handler(resp) | 106 return handler(resp) |
| 107 } | 107 } |
| 108 | 108 |
| 109 return errorHandler(resp, err) | 109 return errorHandler(resp, err) |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 150 // Non-retriable. | 150 // Non-retriable. |
| 151 return fmt.Errorf("unexpected Content-Type, expected \"%
s\", got \"%s\"", jsonContentType, ct) | 151 return fmt.Errorf("unexpected Content-Type, expected \"%
s\", got \"%s\"", jsonContentType, ct) |
| 152 } | 152 } |
| 153 if out == nil { | 153 if out == nil { |
| 154 // The client doesn't care about the response. Still ens
ure the response | 154 // The client doesn't care about the response. Still ens
ure the response |
| 155 // is valid json. | 155 // is valid json. |
| 156 out = &map[string]interface{}{} | 156 out = &map[string]interface{}{} |
| 157 } | 157 } |
| 158 if err := json.NewDecoder(resp.Body).Decode(out); err != nil { | 158 if err := json.NewDecoder(resp.Body).Decode(out); err != nil { |
| 159 // Retriable. | 159 // Retriable. |
| 160 » » » return errors.WrapTransient(fmt.Errorf("bad response %s:
%s", url, err)) | 160 » » » return errors.Annotate(err).Reason("bad response %(url)s
"). |
| 161 » » » » D("url", url).Tag(retry.Tag).Err() |
| 161 } | 162 } |
| 162 return nil | 163 return nil |
| 163 }, nil), nil | 164 }, nil), nil |
| 164 } | 165 } |
| 165 | 166 |
| 166 // GetJSON is a shorthand. It returns the HTTP status code and error if any. | 167 // GetJSON is a shorthand. It returns the HTTP status code and error if any. |
| 167 func GetJSON(ctx context.Context, rFn retry.Factory, c *http.Client, url string,
out interface{}) (int, error) { | 168 func GetJSON(ctx context.Context, rFn retry.Factory, c *http.Client, url string,
out interface{}) (int, error) { |
| 168 req, err := NewRequestJSON(ctx, c, rFn, url, "GET", nil, nil, out) | 169 req, err := NewRequestJSON(ctx, c, rFn, url, "GET", nil, nil, out) |
| 169 if err != nil { | 170 if err != nil { |
| 170 return 0, err | 171 return 0, err |
| 171 } | 172 } |
| 172 return req() | 173 return req() |
| 173 } | 174 } |
| 174 | 175 |
| 175 // PostJSON is a shorthand. It returns the HTTP status code and error if any. | 176 // PostJSON is a shorthand. It returns the HTTP status code and error if any. |
| 176 func PostJSON(ctx context.Context, rFn retry.Factory, c *http.Client, url string
, headers map[string]string, in, out interface{}) (int, error) { | 177 func PostJSON(ctx context.Context, rFn retry.Factory, c *http.Client, url string
, headers map[string]string, in, out interface{}) (int, error) { |
| 177 req, err := NewRequestJSON(ctx, c, rFn, url, "POST", headers, in, out) | 178 req, err := NewRequestJSON(ctx, c, rFn, url, "POST", headers, in, out) |
| 178 if err != nil { | 179 if err != nil { |
| 179 return 0, err | 180 return 0, err |
| 180 } | 181 } |
| 181 return req() | 182 return req() |
| 182 } | 183 } |
| OLD | NEW |