bareDoUntilFound has the exact same behavior as BareDo but only follows 301s, up to maxRedirects times. If it receives a 302, it will parse the Location header into a *url.URL and return that. This is useful for endpoints that return a 302 in successful cases but still might return 301s for permanen
(req *http.Request, maxRedirects int)
| 1278 | // This is useful for endpoints that return a 302 in successful cases but still might return 301s for |
| 1279 | // permanent redirections. |
| 1280 | func (c *Client) bareDoUntilFound(req *http.Request, maxRedirects int) (*url.URL, *Response, error) { |
| 1281 | response, err := c.bareDoIgnoreRedirects(req) |
| 1282 | if err != nil { |
| 1283 | var rerr *RedirectionError |
| 1284 | if errors.As(err, &rerr) { |
| 1285 | // If we receive a 302, transform potential relative locations into absolute and return it. |
| 1286 | if rerr.StatusCode == http.StatusFound { |
| 1287 | if rerr.Location == nil { |
| 1288 | return nil, nil, errInvalidLocation |
| 1289 | } |
| 1290 | newURL := c.baseURL.ResolveReference(rerr.Location) |
| 1291 | return newURL, response, nil |
| 1292 | } |
| 1293 | // If permanent redirect response is returned, follow it |
| 1294 | if maxRedirects > 0 && rerr.StatusCode == http.StatusMovedPermanently { |
| 1295 | if rerr.Location == nil { |
| 1296 | return nil, nil, errInvalidLocation |
| 1297 | } |
| 1298 | newURL := c.baseURL.ResolveReference(rerr.Location) |
| 1299 | // Refuse to follow a permanent redirect to a different host: |
| 1300 | // req.Clone preserves Authorization headers added by the auth |
| 1301 | // transport, so a cross-host target would leak credentials. |
| 1302 | if newURL.Host != c.baseURL.Host { |
| 1303 | return nil, response, fmt.Errorf("refusing to follow cross-host redirect from %q to %q", c.baseURL.Host, newURL.Host) |
| 1304 | } |
| 1305 | newRequest := req.Clone(req.Context()) |
| 1306 | newRequest.URL = newURL |
| 1307 | return c.bareDoUntilFound(newRequest, maxRedirects-1) |
| 1308 | } |
| 1309 | // If we reached the maximum amount of redirections, return an error |
| 1310 | if maxRedirects <= 0 && rerr.StatusCode == http.StatusMovedPermanently { |
| 1311 | return nil, response, fmt.Errorf("reached the maximum amount of redirections: %w", err) |
| 1312 | } |
| 1313 | return nil, response, fmt.Errorf("unexpected redirection response: %w", err) |
| 1314 | } |
| 1315 | } |
| 1316 | |
| 1317 | // If we don't receive a redirection, forward the response and potential error |
| 1318 | return nil, response, err |
| 1319 | } |
| 1320 | |
| 1321 | // Do sends an API request and returns the API response. The API response is |
| 1322 | // JSON decoded and stored in the value pointed to by v, or returned as an |