Exemple #1
0
// adapted from https://blog.golang.org/context httpDo func
func doTimeout(cli *Client, req *http.Request, timeout time.Duration) (*http.Response, error) {
	// TODO: could pass in a context from further up the chain
	// and use that instead of context.Background()
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	// new way to cancel requests
	reqCancel := make(chan struct{})
	req.Cancel = reqCancel

	type response struct {
		resp *http.Response
		err  error
	}
	c := make(chan response, 1)
	go func() {
		var r response
		r.resp, r.err = cli.cli.Do(req)
		c <- r
	}()

	select {
	case <-ctx.Done():
		// request ctx timed out.  Cancel the request by closing req.Cancel channel:
		close(reqCancel)
		// wait for request to finish
		<-c
		return nil, ctx.Err()
	case r := <-c:
		// request successful
		return r.resp, r.err
	}

}
Exemple #2
0
func (t *transport) RoundTrip(ctx context.Context, req *http.Request, l log15.Logger) (*http.Response, error) {
	// http.Transport closes the request body on a failed dial, issue #875
	req.Body = &fakeCloseReadCloser{req.Body}
	defer req.Body.(*fakeCloseReadCloser).RealClose()

	// hook up CloseNotify to cancel the request
	req.Cancel = ctx.Done()

	stickyBackend := t.getStickyBackend(req)
	backends := t.getOrderedBackends(stickyBackend, req)
	allTimeout := len(backends) > 0
	for i, backend := range backends {
		req.URL.Host = backend
		res, err := httpTransport.RoundTrip(req)
		if err == nil {
			t.setStickyBackend(res, stickyBackend)
			return res, nil
		}
		if allTimeout && !strings.Contains(err.Error(), "timeout") {
			allTimeout = false
		}
		l.Error("retriable dial error", "backend", backend, "err", err, "attempt", i)
	}
	status, err := 503, errNoBackends
	if allTimeout {
		status, err = 504, errTimeout
	}
	l.Error("request failed", "status", status, "num_backends", len(backends))
	return nil, err
}
Exemple #3
0
// makeReqCancel returns a closure that cancels the given http.Request
// when called.
func makeReqCancel(req *http.Request) func(http.RoundTripper) {
	c := make(chan struct{})
	req.Cancel = c
	return func(http.RoundTripper) {
		close(c)
	}
}
Exemple #4
0
func (t *transport) RoundTrip(ctx context.Context, req *http.Request, l log15.Logger) (*http.Response, string, error) {
	// http.Transport closes the request body on a failed dial, issue #875
	req.Body = &fakeCloseReadCloser{req.Body}
	defer req.Body.(*fakeCloseReadCloser).RealClose()

	// hook up CloseNotify to cancel the request
	req.Cancel = ctx.Done()

	rt := ctx.Value(ctxKeyRequestTracker).(RequestTracker)
	stickyBackend := t.getStickyBackend(req)
	backends := t.getOrderedBackends(stickyBackend)
	for i, backend := range backends {
		req.URL.Host = backend
		rt.TrackRequestStart(backend)
		res, err := httpTransport.RoundTrip(req)
		if err == nil {
			t.setStickyBackend(res, stickyBackend)
			return res, backend, nil
		}
		rt.TrackRequestDone(backend)
		if _, ok := err.(dialErr); !ok {
			l.Error("unretriable request error", "backend", backend, "err", err, "attempt", i)
			return nil, "", err
		}
		l.Error("retriable dial error", "backend", backend, "err", err, "attempt", i)
	}
	l.Error("request failed", "status", "503", "num_backends", len(backends))
	return nil, "", errNoBackends
}
func cancelable(client *http.Client, req *http.Request) func() {
	ch := make(chan struct{})
	req.Cancel = ch
	return func() {
		close(ch)
	}
}
Exemple #6
0
func (t *Transport) tries(req *http.Request, try uint) (*http.Response, error) {
	startTime := time.Now()
	var timer *time.Timer
	rCancler := make(chan struct{})
	req.Cancel = rCancler
	if t.RequestTimeout != 0 {
		timer = time.AfterFunc(t.RequestTimeout, func() {

			//t.CancelRequest(req)
			t.startOnce.Do(t.start)
			if bc, ok := req.Body.(*bodyCloser); ok {
				bc.timer.Stop()
			}
			close(rCancler)
			t.transport.CancelRequest(req)

		})
	}
	res, err := t.transport.RoundTrip(req)
	headerTime := time.Now()
	if err != nil {
		if timer != nil {
			timer.Stop()
		}
		var stats *Stats
		if t.Stats != nil {
			stats = &Stats{
				Request:  req,
				Response: res,
				Error:    err,
			}
			stats.Duration.Header = headerTime.Sub(startTime)
			stats.Retry.Count = try
		}

		if try < t.MaxTries && req.Method == "GET" && t.shouldRetryError(err) {
			if t.Stats != nil {
				stats.Retry.Pending = true
				t.Stats(stats)
			}
			return t.tries(req, try+1)
		}

		if t.Stats != nil {
			t.Stats(stats)
		}
		return nil, err
	}

	res.Body = &bodyCloser{
		ReadCloser: res.Body,
		timer:      timer,
		res:        res,
		transport:  t,
		startTime:  startTime,
		headerTime: headerTime,
	}
	return res, nil
}
Exemple #7
0
func requestCanceler(tr CancelableTransport, req *http.Request) func() {
	ch := make(chan struct{})
	req.Cancel = ch

	return func() {
		close(ch)
	}
}
func RequestCanceler(rt http.RoundTripper, req *http.Request) func() {
	ch := make(chan struct{})
	req.Cancel = ch

	return func() {
		close(ch)
	}
}
Exemple #9
0
func canceler(client transport.Sender, req *http.Request) func() {
	// TODO(djd): Respect any existing value of req.Cancel.
	ch := make(chan struct{})
	req.Cancel = ch

	return func() {
		close(ch)
	}
}
Exemple #10
0
func (h *Hub) do(req *http.Request) (*http.Response, error) {
	cancel := make(chan struct{})
	req.Cancel = cancel
	timer := time.AfterFunc(1*time.Second, func() { close(cancel) })
	defer timer.Stop()
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	if res.StatusCode != 200 {
		return nil, fmt.Errorf("powerview hub: %v", res.Status)
	}
	return res, nil
}
Exemple #11
0
// Wrapper around net.http.Client.Do().
// This allows a per-request timeout without setting the timeout on the Client object
// directly.
// The third parameter is the duration for the request itself.
func doHttpWithTimeout(cli *http.Client, req *http.Request, timeout time.Duration) (resp *http.Response, err error) {
	if timeout.Seconds() == 0 {
		// No timeout
		resp, err = cli.Do(req)
		return
	}

	tmoch := make(chan struct{})
	timer := time.AfterFunc(timeout, func() {
		tmoch <- struct{}{}
	})

	req.Cancel = tmoch
	resp, err = cli.Do(req)
	timer.Stop()
	return
}
Exemple #12
0
// NewBodyWatcher creates a k8s body watcher for
// a given http request
func NewBodyWatcher(req *http.Request, client *http.Client) (Watch, error) {
	stop := make(chan struct{})
	req.Cancel = stop

	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	wr := &bodyWatcher{
		results: make(chan Event),
		stop:    stop,
		req:     req,
		res:     res,
	}

	go wr.stream()
	return wr, nil
}
Exemple #13
0
// doHeaderTimeout calls c.do(req) and returns its results, or
// errHeaderTimeout if max elapses first.
func (c *Client) doHeaderTimeout(req *http.Request, max time.Duration) (res *http.Response, err error) {
	type resErr struct {
		res *http.Response
		err error
	}

	if req.Cancel != nil {
		panic("use of Request.Cancel inside the buildlet package is reserved for doHeaderTimeout")
	}
	cancelc := make(chan struct{}) // closed to abort
	req.Cancel = cancelc

	resErrc := make(chan resErr, 1)
	go func() {
		res, err := c.do(req)
		resErrc <- resErr{res, err}
	}()

	timer := time.NewTimer(max)
	defer timer.Stop()

	cleanup := func() {
		close(cancelc)
		if re := <-resErrc; re.res != nil {
			re.res.Body.Close()
		}
	}

	select {
	case re := <-resErrc:
		return re.res, re.err
	case <-c.peerDead:
		go cleanup()
		return nil, c.deadErr
	case <-timer.C:
		go cleanup()
		return nil, errHeaderTimeout
	}
}
Exemple #14
0
// Do sends an HTTP request with the provided http.Client and returns an HTTP response.
// If the client is nil, http.DefaultClient is used.
// If the context is canceled or times out, ctx.Err() will be returned.
func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
	if client == nil {
		client = http.DefaultClient
	}

	// TODO(djd): Respect any existing value of req.Cancel.
	cancel := make(chan struct{})
	req.Cancel = cancel

	type responseAndError struct {
		resp *http.Response
		err  error
	}
	result := make(chan responseAndError, 1)

	// Make local copies of test hooks closed over by goroutines below.
	// Prevents data races in tests.
	testHookDoReturned := testHookDoReturned
	testHookDidBodyClose := testHookDidBodyClose

	go func() {
		resp, err := client.Do(req)
		testHookDoReturned()
		result <- responseAndError{resp, err}
	}()

	var resp *http.Response

	select {
	case <-ctx.Done():
		testHookContextDoneBeforeHeaders()
		close(cancel)
		// Clean up after the goroutine calling client.Do:
		go func() {
			if r := <-result; r.resp != nil {
				testHookDidBodyClose()
				r.resp.Body.Close()
			}
		}()
		return nil, ctx.Err()
	case r := <-result:
		var err error
		resp, err = r.resp, r.err
		if err != nil {
			return resp, err
		}
	}

	c := make(chan struct{})
	go func() {
		select {
		case <-ctx.Done():
			close(cancel)
		case <-c:
			// The response's Body is closed.
		}
	}()
	resp.Body = &notifyingReader{resp.Body, c}

	return resp, nil
}