1package httpmock 2 3import ( 4 "bytes" 5 "encoding/json" 6 "encoding/xml" 7 "fmt" 8 "io" 9 "net/http" 10 "strconv" 11 "strings" 12) 13 14// Responder is a callback that receives and http request and returns 15// a mocked response. 16type Responder func(*http.Request) (*http.Response, error) 17 18func (r Responder) times(name string, n int, fn ...func(...interface{})) Responder { 19 count := 0 20 return func(req *http.Request) (*http.Response, error) { 21 count++ 22 if count > n { 23 err := stackTracer{ 24 err: fmt.Errorf("Responder not found for %s %s (coz %s and already called %d times)", req.Method, req.URL, name, count), 25 } 26 if len(fn) > 0 { 27 err.customFn = fn[0] 28 } 29 return nil, err 30 } 31 return r(req) 32 } 33} 34 35// Times returns a Responder callable n times before returning an 36// error. If the Responder is called more than n times and fn is 37// passed and non-nil, it acts as the fn parameter of 38// NewNotFoundResponder, allowing to dump the stack trace to localize 39// the origin of the call. 40func (r Responder) Times(n int, fn ...func(...interface{})) Responder { 41 return r.times("Times", n, fn...) 42} 43 44// Once returns a new Responder callable once before returning an 45// error. If the Responder is called 2 or more times and fn is passed 46// and non-nil, it acts as the fn parameter of NewNotFoundResponder, 47// allowing to dump the stack trace to localize the origin of the 48// call. 49func (r Responder) Once(fn ...func(...interface{})) Responder { 50 return r.times("Once", 1, fn...) 51} 52 53// Trace returns a new Responder that allow to easily trace the calls 54// of the original Responder using fn. It can be used in conjunction 55// with the testing package as in the example below with the help of 56// (*testing.T).Log method: 57// import "testing" 58// ... 59// func TestMyApp(t *testing.T) { 60// ... 61// httpmock.RegisterResponder("GET", "/foo/bar", 62// httpmock.NewStringResponder(200, "{}").Trace(t.Log), 63// ) 64func (r Responder) Trace(fn func(...interface{})) Responder { 65 return func(req *http.Request) (*http.Response, error) { 66 resp, err := r(req) 67 return resp, stackTracer{ 68 customFn: fn, 69 err: err, 70 } 71 } 72} 73 74// ResponderFromResponse wraps an *http.Response in a Responder. 75// 76// Be careful, except for responses generated by httpmock 77// (NewStringResponse and NewBytesResponse functions) for which there 78// is no problems, it is the caller responsibility to ensure the 79// response body can be read several times and concurrently if needed, 80// as it is shared among all Responder returned responses. 81// 82// For home-made responses, NewRespBodyFromString and 83// NewRespBodyFromBytes functions can be used to produce response 84// bodies that can be read several times and concurrently. 85func ResponderFromResponse(resp *http.Response) Responder { 86 return func(req *http.Request) (*http.Response, error) { 87 res := *resp 88 89 // Our stuff: generate a new io.ReadCloser instance sharing the same buffer 90 if body, ok := resp.Body.(*dummyReadCloser); ok { 91 res.Body = body.copy() 92 } 93 94 res.Request = req 95 return &res, nil 96 } 97} 98 99// NewErrorResponder creates a Responder that returns an empty request and the 100// given error. This can be used to e.g. imitate more deep http errors for the 101// client. 102func NewErrorResponder(err error) Responder { 103 return func(req *http.Request) (*http.Response, error) { 104 return nil, err 105 } 106} 107 108// NewNotFoundResponder creates a Responder typically used in 109// conjunction with RegisterNoResponder() function and testing 110// package, to be proactive when a Responder is not found. fn is 111// called with a unique string parameter containing the name of the 112// missing route and the stack trace to localize the origin of the 113// call. If fn returns (= if it does not panic), the responder returns 114// an error of the form: "Responder not found for GET http://foo.bar/path". 115// Note that fn can be nil. 116// 117// It is useful when writing tests to ensure that all routes have been 118// mocked. 119// 120// Example of use: 121// import "testing" 122// ... 123// func TestMyApp(t *testing.T) { 124// ... 125// // Calls testing.Fatal with the name of Responder-less route and 126// // the stack trace of the call. 127// httpmock.RegisterNoResponder(httpmock.NewNotFoundResponder(t.Fatal)) 128// 129// Will abort the current test and print something like: 130// transport_test.go:735: Called from net/http.Get() 131// at /go/src/github.com/jarcoal/httpmock/transport_test.go:714 132// github.com/jarcoal/httpmock.TestCheckStackTracer() 133// at /go/src/testing/testing.go:865 134// testing.tRunner() 135// at /go/src/runtime/asm_amd64.s:1337 136func NewNotFoundResponder(fn func(...interface{})) Responder { 137 return func(req *http.Request) (*http.Response, error) { 138 return nil, stackTracer{ 139 customFn: fn, 140 err: fmt.Errorf("Responder not found for %s %s", req.Method, req.URL), 141 } 142 } 143} 144 145// NewStringResponse creates an *http.Response with a body based on the given string. Also accepts 146// an http status code. 147func NewStringResponse(status int, body string) *http.Response { 148 return &http.Response{ 149 Status: strconv.Itoa(status), 150 StatusCode: status, 151 Body: NewRespBodyFromString(body), 152 Header: http.Header{}, 153 ContentLength: -1, 154 } 155} 156 157// NewStringResponder creates a Responder from a given body (as a string) and status code. 158func NewStringResponder(status int, body string) Responder { 159 return ResponderFromResponse(NewStringResponse(status, body)) 160} 161 162// NewBytesResponse creates an *http.Response with a body based on the given bytes. Also accepts 163// an http status code. 164func NewBytesResponse(status int, body []byte) *http.Response { 165 return &http.Response{ 166 Status: strconv.Itoa(status), 167 StatusCode: status, 168 Body: NewRespBodyFromBytes(body), 169 Header: http.Header{}, 170 ContentLength: -1, 171 } 172} 173 174// NewBytesResponder creates a Responder from a given body (as a byte slice) and status code. 175func NewBytesResponder(status int, body []byte) Responder { 176 return ResponderFromResponse(NewBytesResponse(status, body)) 177} 178 179// NewJsonResponse creates an *http.Response with a body that is a json encoded representation of 180// the given interface{}. Also accepts an http status code. 181func NewJsonResponse(status int, body interface{}) (*http.Response, error) { // nolint: golint 182 encoded, err := json.Marshal(body) 183 if err != nil { 184 return nil, err 185 } 186 response := NewBytesResponse(status, encoded) 187 response.Header.Set("Content-Type", "application/json") 188 return response, nil 189} 190 191// NewJsonResponder creates a Responder from a given body (as an interface{} that is encoded to 192// json) and status code. 193func NewJsonResponder(status int, body interface{}) (Responder, error) { // nolint: golint 194 resp, err := NewJsonResponse(status, body) 195 if err != nil { 196 return nil, err 197 } 198 return ResponderFromResponse(resp), nil 199} 200 201// NewJsonResponderOrPanic is like NewJsonResponder but panics in case of error. 202// 203// It simplifies the call of RegisterResponder, avoiding the use of a 204// temporary variable and an error check, and so can be used as 205// NewStringResponder or NewBytesResponder in such context: 206// RegisterResponder( 207// "GET", 208// "/test/path", 209// NewJSONResponderOrPanic(200, &MyBody), 210// ) 211func NewJsonResponderOrPanic(status int, body interface{}) Responder { // nolint: golint 212 responder, err := NewJsonResponder(status, body) 213 if err != nil { 214 panic(err) 215 } 216 return responder 217} 218 219// NewXmlResponse creates an *http.Response with a body that is an xml encoded representation 220// of the given interface{}. Also accepts an http status code. 221func NewXmlResponse(status int, body interface{}) (*http.Response, error) { // nolint: golint 222 encoded, err := xml.Marshal(body) 223 if err != nil { 224 return nil, err 225 } 226 response := NewBytesResponse(status, encoded) 227 response.Header.Set("Content-Type", "application/xml") 228 return response, nil 229} 230 231// NewXmlResponder creates a Responder from a given body (as an interface{} that is encoded to xml) 232// and status code. 233func NewXmlResponder(status int, body interface{}) (Responder, error) { // nolint: golint 234 resp, err := NewXmlResponse(status, body) 235 if err != nil { 236 return nil, err 237 } 238 return ResponderFromResponse(resp), nil 239} 240 241// NewXmlResponderOrPanic is like NewXmlResponder but panics in case of error. 242// 243// It simplifies the call of RegisterResponder, avoiding the use of a 244// temporary variable and an error check, and so can be used as 245// NewStringResponder or NewBytesResponder in such context: 246// RegisterResponder( 247// "GET", 248// "/test/path", 249// NewXmlResponderOrPanic(200, &MyBody), 250// ) 251func NewXmlResponderOrPanic(status int, body interface{}) Responder { // nolint: golint 252 responder, err := NewXmlResponder(status, body) 253 if err != nil { 254 panic(err) 255 } 256 return responder 257} 258 259// NewRespBodyFromString creates an io.ReadCloser from a string that is suitable for use as an 260// http response body. 261func NewRespBodyFromString(body string) io.ReadCloser { 262 return &dummyReadCloser{orig: body} 263} 264 265// NewRespBodyFromBytes creates an io.ReadCloser from a byte slice that is suitable for use as an 266// http response body. 267func NewRespBodyFromBytes(body []byte) io.ReadCloser { 268 return &dummyReadCloser{orig: body} 269} 270 271type dummyReadCloser struct { 272 orig interface{} // string or []byte 273 body io.ReadSeeker // instanciated on demand from orig 274} 275 276// copy returns a new instance resetting d.body to nil. 277func (d *dummyReadCloser) copy() *dummyReadCloser { 278 return &dummyReadCloser{orig: d.orig} 279} 280 281// setup ensures d.body is correctly initialized. 282func (d *dummyReadCloser) setup() { 283 if d.body == nil { 284 switch body := d.orig.(type) { 285 case string: 286 d.body = strings.NewReader(body) 287 case []byte: 288 d.body = bytes.NewReader(body) 289 } 290 } 291} 292 293func (d *dummyReadCloser) Read(p []byte) (n int, err error) { 294 d.setup() 295 n, err = d.body.Read(p) 296 if err == io.EOF { 297 d.body.Seek(0, 0) // nolint: errcheck 298 } 299 return n, err 300} 301 302func (d *dummyReadCloser) Close() error { 303 d.setup() 304 d.body.Seek(0, 0) // nolint: errcheck 305 return nil 306} 307