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