X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/bc14c62ad1528dbddc26781c5cea6a7968c93f2e..9a71dd94cb72a5fd1ed74ca71b4961de4108db02:/sdk/go/arvados/client.go diff --git a/sdk/go/arvados/client.go b/sdk/go/arvados/client.go index a5815987b1..1e2c07e867 100644 --- a/sdk/go/arvados/client.go +++ b/sdk/go/arvados/client.go @@ -20,7 +20,7 @@ import ( "strings" "time" - "git.curoverse.com/arvados.git/sdk/go/httpserver" + "git.arvados.org/arvados.git/sdk/go/httpserver" ) // A Client is an HTTP client with an API endpoint and a set of @@ -54,6 +54,9 @@ type Client struct { // arvadosclient.ArvadosClient.) KeepServiceURIs []string `json:",omitempty"` + // HTTP headers to add/override in outgoing requests. + SendHeader http.Header + dd *DiscoveryDocument ctx context.Context @@ -144,9 +147,22 @@ func (c *Client) Do(req *http.Request) (*http.Response, error) { return c.httpClient().Do(req) } +func isRedirectStatus(code int) bool { + switch code { + case http.StatusMovedPermanently, http.StatusFound, http.StatusSeeOther, http.StatusTemporaryRedirect, http.StatusPermanentRedirect: + return true + default: + return false + } +} + // DoAndDecode performs req and unmarshals the response (which must be // JSON) into dst. Use this instead of RequestAndDecode if you need // more control of the http.Request object. +// +// If the response status indicates an HTTP redirect, the Location +// header value is unmarshalled to dst as a RedirectLocation +// key/field. func (c *Client) DoAndDecode(dst interface{}, req *http.Request) error { resp, err := c.Do(req) if err != nil { @@ -157,13 +173,28 @@ func (c *Client) DoAndDecode(dst interface{}, req *http.Request) error { if err != nil { return err } - if resp.StatusCode != 200 { - return newTransactionError(req, resp, buf) - } - if dst == nil { + switch { + case resp.StatusCode == http.StatusOK && dst == nil: return nil + case resp.StatusCode == http.StatusOK: + return json.Unmarshal(buf, dst) + + // If the caller uses a client with a custom CheckRedirect + // func, Do() might return the 3xx response instead of + // following it. + case isRedirectStatus(resp.StatusCode) && dst == nil: + return nil + case isRedirectStatus(resp.StatusCode): + // Copy the redirect target URL to dst.RedirectLocation. + buf, err := json.Marshal(map[string]string{"redirect_location": resp.Header.Get("Location")}) + if err != nil { + return err + } + return json.Unmarshal(buf, dst) + + default: + return newTransactionError(req, resp, buf) } - return json.Unmarshal(buf, dst) } // Convert an arbitrary struct to url.Values. For example, @@ -250,9 +281,8 @@ func (c *Client) RequestAndDecodeContext(ctx context.Context, dst interface{}, m } if urlValues == nil { // Nothing to send - } else if method == "GET" || method == "HEAD" || body != nil { - // Must send params in query part of URL (FIXME: what - // if resulting URL is too long?) + } else if body != nil || ((method == "GET" || method == "HEAD") && len(urlValues.Encode()) < 1000) { + // Send params in query part of URL u, err := url.Parse(urlString) if err != nil { return err @@ -266,8 +296,15 @@ func (c *Client) RequestAndDecodeContext(ctx context.Context, dst interface{}, m if err != nil { return err } + if (method == "GET" || method == "HEAD") && body != nil { + req.Header.Set("X-Http-Method-Override", method) + req.Method = "POST" + } req = req.WithContext(ctx) req.Header.Set("Content-type", "application/x-www-form-urlencoded") + for k, v := range c.SendHeader { + req.Header[k] = v + } return c.DoAndDecode(dst, req) }