-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttp.go
121 lines (100 loc) · 3.27 KB
/
http.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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package resource
import (
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/textproto"
"time"
)
// HTTPClient is the method set expected of an object which can transact with an HTTP server.
// http.Client implements this interface.
type HTTPClient interface {
Do(*http.Request) (*http.Response, error)
}
type HTTPClientFunc func(*http.Request) (*http.Response, error)
func (f HTTPClientFunc) Do(request *http.Request) (*http.Response, error) {
return f(request)
}
// WithMethod decorates an HTTPClient, setting a specific HTTP method on each request
func WithMethod(method string, c HTTPClient) HTTPClient {
return HTTPClientFunc(func(request *http.Request) (*http.Response, error) {
request.Method = method
return c.Do(request)
})
}
// WithHeader decorates an HTTPClient, setting a single HTTP header name/value on each request
func WithHeader(name, value string, c HTTPClient) HTTPClient {
return HTTPClientFunc(func(request *http.Request) (*http.Response, error) {
if request.Header == nil {
request.Header = make(http.Header)
}
request.Header.Set(name, value)
return c.Do(request)
})
}
// WithHeaders decorates an HTTPClient, copying a set of headers onto each request
func WithHeaders(h http.Header, c HTTPClient) HTTPClient {
if len(h) == 0 {
return c
}
// make a safe, deep copy to use
clone := make(http.Header, len(h))
for k, v := range h {
k = textproto.CanonicalMIMEHeaderKey(k)
clone[k] = append(clone[k], v...)
}
h = clone
return HTTPClientFunc(func(request *http.Request) (*http.Response, error) {
if request.Header == nil {
request.Header = make(http.Header, len(h))
}
for k, v := range h {
request.Header[k] = v
}
return c.Do(request)
})
}
// WithClose decorates an HTTPClient, setting the Request.Close flag for each request
func WithClose(c HTTPClient) HTTPClient {
return HTTPClientFunc(func(request *http.Request) (*http.Response, error) {
request.Close = true
return c.Do(request)
})
}
// WithTimeout decorates an HTTPClient, creating a context with a timeout for each request
func WithTimeout(d time.Duration, c HTTPClient) HTTPClient {
return HTTPClientFunc(func(request *http.Request) (*http.Response, error) {
ctx, cancel := context.WithTimeout(request.Context(), d)
defer cancel()
return c.Do(request.WithContext(ctx))
})
}
type drainOnClose struct {
io.ReadCloser
}
func (doc drainOnClose) Close() error {
io.Copy(ioutil.Discard, doc.ReadCloser)
return doc.ReadCloser.Close()
}
// DrainOnClose decorates an existing io.ReadCloser such that invoking Close on the
// returned io.ReadCloser causes any remaining contents to be discarded. Used by HTTP
// resources to ensure that the HTTP response body is fully read when client code closes
// the resource's io.ReadCloser returned by Open.
func DrainOnClose(rc io.ReadCloser) io.ReadCloser {
return drainOnClose{rc}
}
// HTTPError represents a failure to obtain an HTTP resource. This error indicates that a successful
// HTTP transaction occurred with a non-2XX response code.
type HTTPError struct {
URL string
Code int
}
func (he HTTPError) Error() string {
return fmt.Sprintf("HTTP resource %s failed with status code %d", he.URL, he.Code)
}
// StatusCode is supplied to implement go-kit's StatusCoder interface
func (he HTTPError) StatusCode() int {
return he.Code
}