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 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 if err != nil { | 84 if err != nil { |
85 // Retry every error. This is sad when you speci
fy an invalid hostname but | 85 // Retry every error. This is sad when you speci
fy an invalid hostname but |
86 // it's better than failing when DNS resolution
is flaky. | 86 // it's better than failing when DNS resolution
is flaky. |
87 return errorHandler(nil, transient.Tag.Apply(err
)) | 87 return errorHandler(nil, transient.Tag.Apply(err
)) |
88 } | 88 } |
89 status = resp.StatusCode | 89 status = resp.StatusCode |
90 | 90 |
91 switch { | 91 switch { |
92 case status == 408, status == 429, status >= 500: | 92 case status == 408, status == 429, status >= 500: |
93 // The HTTP status code means the request should
be retried. | 93 // The HTTP status code means the request should
be retried. |
94 » » » » err = errors.Reason("http request failed: %(text
)s (HTTP %(code)d)"). | 94 » » » » err = errors.Reason("http request failed: %s (HT
TP %d)", http.StatusText(status), status). |
95 » » » » » D("text", http.StatusText(status)).D("co
de", status).Tag(transient.Tag).Err() | 95 » » » » » Tag(transient.Tag).Err() |
96 case status == 404 && strings.HasPrefix(req.URL.Path, "/
_ah/api/"): | 96 case status == 404 && strings.HasPrefix(req.URL.Path, "/
_ah/api/"): |
97 // Endpoints occasionally return 404 on valid re
quests! | 97 // Endpoints occasionally return 404 on valid re
quests! |
98 logging.Infof(ctx, "lhttp.Do() got a Cloud Endpo
ints 404: %#v", resp.Header) | 98 logging.Infof(ctx, "lhttp.Do() got a Cloud Endpo
ints 404: %#v", resp.Header) |
99 » » » » err = errors.Reason("http request failed (endpoi
nts): %(text)s (HTTP %(code)d)"). | 99 » » » » err = errors.Reason("http request failed (endpoi
nts): %s (HTTP %d)", http.StatusText(status), status). |
100 » » » » » D("text", http.StatusText(status)).D("co
de", status).Tag(transient.Tag).Err() | 100 » » » » » Tag(transient.Tag).Err() |
101 case status >= 400: | 101 case status >= 400: |
102 // Any other failure code is a hard failure. | 102 // Any other failure code is a hard failure. |
103 err = fmt.Errorf("http request failed: %s (HTTP
%d)", http.StatusText(status), status) | 103 err = fmt.Errorf("http request failed: %s (HTTP
%d)", http.StatusText(status), status) |
104 default: | 104 default: |
105 // The handler may still return a retry.Error to
indicate that the request | 105 // The handler may still return a retry.Error to
indicate that the request |
106 // should be retried even on successful status c
ode. | 106 // should be retried even on successful status c
ode. |
107 return handler(resp) | 107 return handler(resp) |
108 } | 108 } |
109 | 109 |
110 return errorHandler(resp, err) | 110 return errorHandler(resp, err) |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
151 // Non-retriable. | 151 // Non-retriable. |
152 return fmt.Errorf("unexpected Content-Type, expected \"%
s\", got \"%s\"", jsonContentType, ct) | 152 return fmt.Errorf("unexpected Content-Type, expected \"%
s\", got \"%s\"", jsonContentType, ct) |
153 } | 153 } |
154 if out == nil { | 154 if out == nil { |
155 // The client doesn't care about the response. Still ens
ure the response | 155 // The client doesn't care about the response. Still ens
ure the response |
156 // is valid json. | 156 // is valid json. |
157 out = &map[string]interface{}{} | 157 out = &map[string]interface{}{} |
158 } | 158 } |
159 if err := json.NewDecoder(resp.Body).Decode(out); err != nil { | 159 if err := json.NewDecoder(resp.Body).Decode(out); err != nil { |
160 // Retriable. | 160 // Retriable. |
161 » » » return errors.Annotate(err).Reason("bad response %(url)s
"). | 161 » » » return errors.Annotate(err, "bad response %s", url).Tag(
transient.Tag).Err() |
162 » » » » D("url", url).Tag(transient.Tag).Err() | |
163 } | 162 } |
164 return nil | 163 return nil |
165 }, nil), nil | 164 }, nil), nil |
166 } | 165 } |
167 | 166 |
168 // 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. |
169 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) { |
170 req, err := NewRequestJSON(ctx, c, rFn, url, "GET", nil, nil, out) | 169 req, err := NewRequestJSON(ctx, c, rFn, url, "GET", nil, nil, out) |
171 if err != nil { | 170 if err != nil { |
172 return 0, err | 171 return 0, err |
173 } | 172 } |
174 return req() | 173 return req() |
175 } | 174 } |
176 | 175 |
177 // 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. |
178 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) { |
179 req, err := NewRequestJSON(ctx, c, rFn, url, "POST", headers, in, out) | 178 req, err := NewRequestJSON(ctx, c, rFn, url, "POST", headers, in, out) |
180 if err != nil { | 179 if err != nil { |
181 return 0, err | 180 return 0, err |
182 } | 181 } |
183 return req() | 182 return req() |
184 } | 183 } |
OLD | NEW |