func()

in garbage/nethttp.go [388:509]


func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bool) (resp *Response, err error) {
	var base *url.URL
	redirectChecker := c.CheckRedirect
	if redirectChecker == nil {
		redirectChecker = defaultCheckRedirect
	}
	var via []*Request

	if ireq.URL == nil {
		ireq.closeBody()
		return nil, errors.New("http: nil Request.URL")
	}

	var reqmu sync.Mutex // guards req
	req := ireq

	var timer *time.Timer
	var atomicWasCanceled int32 // atomic bool (1 or 0)
	var wasCanceled = alwaysFalse
	if c.Timeout > 0 {
		wasCanceled = func() bool { return atomic.LoadInt32(&atomicWasCanceled) != 0 }
		type canceler interface {
			CancelRequest(*Request)
		}
		tr, ok := c.transport().(canceler)
		if !ok {
			return nil, fmt.Errorf("net/http: Client Transport of type %T doesn't support CancelRequest; Timeout not supported", c.transport())
		}
		timer = time.AfterFunc(c.Timeout, func() {
			atomic.StoreInt32(&atomicWasCanceled, 1)
			reqmu.Lock()
			defer reqmu.Unlock()
			tr.CancelRequest(req)
		})
	}

	urlStr := ""
	redirectFailed := false
	for redirect := 0; ; redirect++ {
		if redirect != 0 {
			nreq := new(Request)
			nreq.Method = ireq.Method
			if ireq.Method == "POST" || ireq.Method == "PUT" {
				nreq.Method = "GET"
			}
			nreq.Header = make(Header)
			nreq.URL, err = base.Parse(urlStr)
			if err != nil {
				break
			}
			if len(via) > 0 {

				lastReq := via[len(via)-1]
				if ref := refererForURL(lastReq.URL, nreq.URL); ref != "" {
					nreq.Header.Set("Referer", ref)
				}

				err = redirectChecker(nreq, via)
				if err != nil {
					redirectFailed = true
					break
				}
			}
			reqmu.Lock()
			req = nreq
			reqmu.Unlock()
		}

		urlStr = req.URL.String()
		if resp, err = c.send(req); err != nil {
			if wasCanceled() {
				err = &httpError{
					err:     err.Error() + " (Client.Timeout exceeded while awaiting headers)",
					timeout: true,
				}
			}
			break
		}

		if shouldRedirect(resp.StatusCode) {
			// Read the body if small so underlying TCP connection will be re-used.
			// No need to check for errors: if it fails, Transport won't reuse it anyway.
			const maxBodySlurpSize = 2 << 10
			if resp.ContentLength == -1 || resp.ContentLength <= maxBodySlurpSize {
				io.CopyN(ioutil.Discard, resp.Body, maxBodySlurpSize)
			}
			resp.Body.Close()
			if urlStr = resp.Header.Get("Location"); urlStr == "" {
				err = fmt.Errorf("%d response missing Location header", resp.StatusCode)
				break
			}
			base = req.URL
			via = append(via, req)
			continue
		}
		if timer != nil {
			resp.Body = &cancelTimerBody{
				t:              timer,
				rc:             resp.Body,
				reqWasCanceled: wasCanceled,
			}
		}
		return resp, nil
	}

	method := ireq.Method
	urlErr := &url.Error{
		Op:  method[0:1] + strings.ToLower(method[1:]),
		URL: urlStr,
		Err: err,
	}

	if redirectFailed {

		return resp, urlErr
	}

	if resp != nil {
		resp.Body.Close()
	}
	return nil, urlErr
}