forked from arschles/gorion
/
http_util.go
63 lines (56 loc) · 1.75 KB
/
http_util.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package gorion
import (
"errors"
"net/http"
"golang.org/x/net/context"
)
var (
// ErrCancelled is returned immediately when a func returns a net.Context that is
// already cancelled.
ErrCancelled = errors.New("cancelled")
)
// HTTPDo runs the HTTP request in a goroutine and passes the response to f in
// that same goroutine. After the goroutine finishes, returns the result of f.
// If ctx.Done() receives before f returns, calls transport.CancelRequest(req) before
// before returning. Even though HTTPDo executes f in another goroutine, you
// can treat it as a synchronous call to f. Also, since HTTPDo uses client to run
// requests and transport to cancel them, client.Transport should be transport
// in most cases.
//
// Example Usage:
// type Resp struct { Num int `json:"num"` }
// var resp *Resp
// err := HttpDo(ctx, client, transport, req, func(resp *http.Response, err error) error {
// if err != nil { return err }
// defer resp.Body.Close()
//
// if err := json.NewDecoder(resp.Body).Decode(resp); err != nil {
// return err
// }
// return nil
// })
// if err != nil { return err }
// // do something with resp...
//
// This func was stolen/adapted from https://blog.golang.org/context
func HTTPDo(ctx context.Context, client *http.Client, transport *http.Transport, req *http.Request, f func(*http.Response, error) error) error {
// Run the HTTP request in a goroutine and pass the response to f.
c := make(chan error, 1)
// see if the ctx was already cancelled/timed out
select {
case <-ctx.Done():
return ErrCancelled
default:
}
go func() {
c <- f(client.Do(req))
}()
select {
case <-ctx.Done():
transport.CancelRequest(req)
<-c // Wait for f to return.
return ctx.Err()
case err := <-c:
return err
}
}