Chromium Code Reviews| Index: go/src/infra/httputil/httputil.go |
| diff --git a/go/src/infra/httputil/httputil.go b/go/src/infra/httputil/httputil.go |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c6073f1aee0b5f6e0e7069711302ccb7117f7774 |
| --- /dev/null |
| +++ b/go/src/infra/httputil/httputil.go |
| @@ -0,0 +1,56 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +// Package httputil contains HTTP utility functions. |
| +package httputil |
| + |
| +import ( |
| + "bytes" |
| + "fmt" |
| + "io/ioutil" |
| + "net/http" |
| + "time" |
| + |
| + "golang.org/x/net/context" |
| + "golang.org/x/net/context/ctxhttp" |
| + |
| + "github.com/luci/luci-go/common/clock" |
| + "github.com/luci/luci-go/common/errors" |
| + "github.com/luci/luci-go/common/logging" |
| + "github.com/luci/luci-go/common/retry" |
| +) |
| + |
| +// RetryRequest retires an HTTP request on transient errors, |
| +// calls fn on success and guarantees to close response body. |
| +// ctx deadline is limited to client's timeout. |
|
seanmccullough1
2016/06/03 18:45:33
Nice. Does this do exponential back-off?
nodir
2016/06/03 18:56:12
retry.Default does
updated docstring
|
| +func RetryRequest(ctx context.Context, client *http.Client, req *http.Request, requestBody []byte, fn func(*http.Response) error) error { |
| + if client == nil { |
| + client = http.DefaultClient |
| + } |
| + return retry.Retry( |
| + ctx, |
| + retry.TransientOnly(retry.Default), |
| + func() error { |
| + ctx := ctx |
| + if client.Timeout > 0 { |
| + clientDeadline := clock.Now(ctx).Add(client.Timeout) |
| + if deadline, ok := ctx.Deadline(); !ok || deadline.After(clientDeadline) { |
| + ctx, _ = context.WithDeadline(ctx, clientDeadline) |
| + } |
| + } |
| + req.Body = ioutil.NopCloser(bytes.NewReader(requestBody)) |
| + res, err := ctxhttp.Do(ctx, client, req) |
| + if err != nil { |
| + return errors.WrapTransient(err) |
| + } |
| + defer res.Body.Close() |
| + if res.StatusCode >= 500 { |
| + return errors.WrapTransient(fmt.Errorf("HTTP status %s", res.Status)) |
| + } |
| + return fn(res) |
| + }, |
| + func(err error, delay time.Duration) { |
| + logging.Warningf(ctx, "failed transiently, will retry in %s: %s", delay, err) |
| + }) |
| +} |