// DoHttpRequestWithRedirects runs a HTTP request and responds to redirects func DoHttpRequestWithRedirects(req *http.Request, via []*http.Request, useCreds bool) (*http.Response, error) { var creds auth.Creds if useCreds { c, err := auth.GetCreds(req) if err != nil { return nil, err } creds = c } res, err := doHttpRequest(req, creds) if err != nil { return res, err } if res.StatusCode == 307 { redirectTo := res.Header.Get("Location") locurl, err := url.Parse(redirectTo) if err == nil && !locurl.IsAbs() { locurl = req.URL.ResolveReference(locurl) redirectTo = locurl.String() } redirectedReq, err := NewHttpRequest(req.Method, redirectTo, nil) if err != nil { return res, errutil.Errorf(err, err.Error()) } via = append(via, req) // Avoid seeking and re-wrapping the CountingReadCloser, just get the "real" body realBody := req.Body if wrappedBody, ok := req.Body.(*CountingReadCloser); ok { realBody = wrappedBody.ReadCloser } seeker, ok := realBody.(io.Seeker) if !ok { return res, errutil.Errorf(nil, "Request body needs to be an io.Seeker to handle redirects.") } if _, err := seeker.Seek(0, 0); err != nil { return res, errutil.Error(err) } redirectedReq.Body = realBody redirectedReq.ContentLength = req.ContentLength if err = CheckRedirect(redirectedReq, via); err != nil { return res, errutil.Errorf(err, err.Error()) } return DoHttpRequestWithRedirects(redirectedReq, via, useCreds) } return res, nil }
// DoHttpRequest performs a single HTTP request func DoHttpRequest(req *http.Request, useCreds bool) (*http.Response, error) { var creds auth.Creds if useCreds { c, err := auth.GetCreds(req) if err != nil { return nil, err } creds = c } return doHttpRequest(req, creds) }
func doNTLMRequest(request *http.Request, retry bool) (*http.Response, error) { handReq, err := cloneRequest(request) if err != nil { return nil, err } res, err := NewHttpClient(config.Config, handReq.Host).Do(handReq) if err != nil && res == nil { return nil, err } //If the status is 401 then we need to re-authenticate, otherwise it was successful if res.StatusCode == 401 { creds, err := auth.GetCreds(request) if err != nil { return nil, err } negotiateReq, err := cloneRequest(request) if err != nil { return nil, err } challengeMessage, err := negotiate(negotiateReq, ntlmNegotiateMessage) if err != nil { return nil, err } challengeReq, err := cloneRequest(request) if err != nil { return nil, err } res, err := challenge(challengeReq, challengeMessage, creds) if err != nil { return nil, err } //If the status is 401 then we need to re-authenticate if res.StatusCode == 401 && retry == true { return doNTLMRequest(challengeReq, false) } auth.SaveCredentials(creds, res) return res, nil } return res, nil }
// Build implements the Lifecycle.Build function. // // HttpLifecycle in particular, builds an absolute path by parsing and then // relativizing the `schema.Path` with respsect to the `HttpLifecycle.root`. If // there was an error in determining this URL, then that error will be returned, // // After this is complete, a body is attached to the request if the // schema contained one. If a body was present, and there an error occurred while // serializing it into JSON, then that error will be returned and the // *http.Request will not be generated. // // In all cases, credentials are attached to the HTTP request as described in // the `auth` package (see github.com/github/git-lfs/auth#GetCreds). // // Finally, all of these components are combined together and the resulting // request is returned. func (l *HttpLifecycle) Build(schema *RequestSchema) (*http.Request, error) { path, err := l.absolutePath(schema.Operation, schema.Path) if err != nil { return nil, err } body, err := l.body(schema) if err != nil { return nil, err } req, err := http.NewRequest(schema.Method, path.String(), body) if err != nil { return nil, err } if _, err = auth.GetCreds(req); err != nil { return nil, err } req.URL.RawQuery = l.queryParameters(schema).Encode() return req, nil }