~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/httprequest/handler.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2015 Canonical Ltd.
 
2
// Licensed under the LGPLv3, see LICENCE file for details.
 
3
 
 
4
package httprequest
 
5
 
 
6
import (
 
7
        "encoding/json"
 
8
        "io"
 
9
        "net/http"
 
10
        "reflect"
 
11
 
 
12
        "github.com/julienschmidt/httprouter"
 
13
        "gopkg.in/errgo.v1"
 
14
)
 
15
 
 
16
// ErrorMapper holds a function that can convert a Go error
 
17
// into a form that can be returned as a JSON body from an HTTP request.
 
18
//
 
19
// The httpStatus value reports the desired HTTP status.
 
20
//
 
21
// If the returned errorBody implements HeaderSetter, then
 
22
// that method will be called to add custom headers to the request.
 
23
type ErrorMapper func(err error) (httpStatus int, errorBody interface{})
 
24
 
 
25
var (
 
26
        paramsType             = reflect.TypeOf(Params{})
 
27
        errorType              = reflect.TypeOf((*error)(nil)).Elem()
 
28
        httpResponseWriterType = reflect.TypeOf((*http.ResponseWriter)(nil)).Elem()
 
29
        httpHeaderType         = reflect.TypeOf(http.Header(nil))
 
30
        httpRequestType        = reflect.TypeOf((*http.Request)(nil))
 
31
        ioCloserType           = reflect.TypeOf((*io.Closer)(nil)).Elem()
 
32
)
 
33
 
 
34
// Handle converts a function into a Handler. The argument f
 
35
// must be a function of one of the following six forms, where ArgT
 
36
// must be a struct type acceptable to Unmarshal and ResultT is a type
 
37
// that can be marshaled as JSON:
 
38
//
 
39
//      func(p Params, arg *ArgT)
 
40
//      func(p Params, arg *ArgT) error
 
41
//      func(p Params, arg *ArgT) (ResultT, error)
 
42
//
 
43
//      func(arg *ArgT)
 
44
//      func(arg *ArgT) error
 
45
//      func(arg *ArgT) (ResultT, error)
 
46
//
 
47
// When processing a call to the returned handler, the provided
 
48
// parameters are unmarshaled into a new ArgT value using Unmarshal,
 
49
// then f is called with this value. If the unmarshaling fails, f will
 
50
// not be called and the unmarshal error will be written as a JSON
 
51
// response.
 
52
//
 
53
// As an additional special case to the rules defined in Unmarshal,
 
54
// the tag on an anonymous field of type Route
 
55
// specifies the method and path to use in the HTTP request.
 
56
// It should hold two space-separated fields; the first specifies
 
57
// the HTTP method, the second the URL path to use for the request.
 
58
// If this is given, the returned handler will hold that
 
59
// method and path, otherwise they will be empty.
 
60
//
 
61
// If an error is returned from f, it is passed through the error mapper before
 
62
// writing as a JSON response.
 
63
//
 
64
// In the third form, when no error is returned, the result is written
 
65
// as a JSON response with status http.StatusOK. Also in this case,
 
66
// any calls to Params.Response.Write or Params.Response.WriteHeader
 
67
// will be ignored, as the response code and data should be defined
 
68
// entirely by the returned result and error.
 
69
//
 
70
// Handle will panic if the provided function is not in one
 
71
// of the above forms.
 
72
func (e ErrorMapper) Handle(f interface{}) Handler {
 
73
        fv := reflect.ValueOf(f)
 
74
        hf, rt, err := e.handlerFunc(fv.Type())
 
75
        if err != nil {
 
76
                panic(errgo.Notef(err, "bad handler function"))
 
77
        }
 
78
        return Handler{
 
79
                Method: rt.method,
 
80
                Path:   rt.path,
 
81
                Handle: func(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
 
82
                        hf(fv, Params{
 
83
                                Response:    w,
 
84
                                Request:     req,
 
85
                                PathVar:     p,
 
86
                                PathPattern: rt.path,
 
87
                        })
 
88
                },
 
89
        }
 
90
}
 
91
 
 
92
// Handlers returns a list of handlers that will be handled by the value
 
93
// returned by the given argument, which must be a function of the form:
 
94
//
 
95
//      func(httprequest.Params) (T, error)
 
96
//
 
97
// for some type T. Each exported method defined on T defines a handler,
 
98
// and should be in one of the forms accepted by ErrorMapper.Handle.
 
99
//
 
100
// Handlers will panic if f is not of the required form, no methods are
 
101
// defined on T or any method defined on T is not suitable for Handle.
 
102
//
 
103
// When any of the returned handlers is invoked, f will be called and
 
104
// then the appropriate method will be called on the value it returns.
 
105
//
 
106
// If T implements io.Closer, its Close method will be called
 
107
// after the request is completed.
 
108
func (e ErrorMapper) Handlers(f interface{}) []Handler {
 
109
        fv := reflect.ValueOf(f)
 
110
        wt, err := checkHandlersWrapperFunc(fv)
 
111
        if err != nil {
 
112
                panic(errgo.Notef(err, "bad handler function"))
 
113
        }
 
114
        hasClose := wt.Implements(ioCloserType)
 
115
        hs := make([]Handler, 0, wt.NumMethod())
 
116
        numMethod := 0
 
117
        for i := 0; i < wt.NumMethod(); i++ {
 
118
                i := i
 
119
                m := wt.Method(i)
 
120
                if m.PkgPath != "" {
 
121
                        continue
 
122
                }
 
123
                if m.Name == "Close" {
 
124
                        if !hasClose {
 
125
                                panic(errgo.Newf("bad type for Close method (got %v want func(%v) error", m.Type, wt))
 
126
                        }
 
127
                        continue
 
128
                }
 
129
                // The type in the Method struct includes the receiver type,
 
130
                // which we don't want to look at (and we won't see when
 
131
                // we get the method from the actual value at dispatch time),
 
132
                // so we hide it.
 
133
                mt := withoutReceiver(m.Type)
 
134
                hf, rt, err := e.handlerFunc(mt)
 
135
                if err != nil {
 
136
                        panic(errgo.Notef(err, "bad type for method %s", m.Name))
 
137
                }
 
138
                if rt.method == "" || rt.path == "" {
 
139
                        panic(errgo.Notef(err, "method %s does not specify route method and path", m.Name))
 
140
                }
 
141
                handler := func(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
 
142
                        terrv := fv.Call([]reflect.Value{
 
143
                                reflect.ValueOf(Params{
 
144
                                        Response:    w,
 
145
                                        Request:     req,
 
146
                                        PathVar:     p,
 
147
                                        PathPattern: rt.path,
 
148
                                }),
 
149
                        })
 
150
                        tv, errv := terrv[0], terrv[1]
 
151
                        if !errv.IsNil() {
 
152
                                e.WriteError(w, errv.Interface().(error))
 
153
                                return
 
154
                        }
 
155
                        if hasClose {
 
156
                                defer tv.Interface().(io.Closer).Close()
 
157
                        }
 
158
                        hf(tv.Method(i), Params{
 
159
                                Response:    w,
 
160
                                Request:     req,
 
161
                                PathVar:     p,
 
162
                                PathPattern: rt.path,
 
163
                        })
 
164
 
 
165
                }
 
166
                hs = append(hs, Handler{
 
167
                        Method: rt.method,
 
168
                        Path:   rt.path,
 
169
                        Handle: handler,
 
170
                })
 
171
                numMethod++
 
172
        }
 
173
        if numMethod == 0 {
 
174
                panic(errgo.Newf("no exported methods defined on %s", wt))
 
175
        }
 
176
        return hs
 
177
}
 
178
 
 
179
func checkHandlersWrapperFunc(fv reflect.Value) (reflect.Type, error) {
 
180
        ft := fv.Type()
 
181
        if ft.Kind() != reflect.Func {
 
182
                return nil, errgo.Newf("expected function, got %v", ft)
 
183
        }
 
184
        if fv.IsNil() {
 
185
                return nil, errgo.Newf("function is nil")
 
186
        }
 
187
        if n := ft.NumIn(); n != 1 {
 
188
                return nil, errgo.Newf("got %d arguments, want 1", n)
 
189
        }
 
190
        if n := ft.NumOut(); n != 2 {
 
191
                return nil, errgo.Newf("function returns %d values, want 2", n)
 
192
        }
 
193
        if ft.In(0) != paramsType ||
 
194
                ft.Out(1) != errorType {
 
195
                return nil, errgo.Newf("invalid argument or return values, want func(httprequest.Params) (any, error), got %v", ft)
 
196
        }
 
197
        return ft.Out(0), nil
 
198
}
 
199
 
 
200
// Handler defines a HTTP handler that will handle the
 
201
// given HTTP method at the given httprouter path
 
202
type Handler struct {
 
203
        Method string
 
204
        Path   string
 
205
        Handle httprouter.Handle
 
206
}
 
207
 
 
208
func checkHandleType(t reflect.Type) (*requestType, error) {
 
209
        if t.Kind() != reflect.Func {
 
210
                return nil, errgo.New("not a function")
 
211
        }
 
212
        if n := t.NumIn(); n != 1 && n != 2 {
 
213
                return nil, errgo.Newf("has %d parameters, need 1 or 2", t.NumIn())
 
214
        }
 
215
        if t.NumOut() > 2 {
 
216
                return nil, errgo.Newf("has %d result parameters, need 0, 1 or 2", t.NumOut())
 
217
        }
 
218
        if t.NumIn() == 2 {
 
219
                if t.In(0) != paramsType {
 
220
                        return nil, errgo.Newf("first argument is %v, need httprequest.Params", t.In(0))
 
221
                }
 
222
        } else {
 
223
                if t.In(0) == paramsType {
 
224
                        return nil, errgo.Newf("no argument parameter after Params argument")
 
225
                }
 
226
        }
 
227
        pt, err := getRequestType(t.In(t.NumIn() - 1))
 
228
        if err != nil {
 
229
                return nil, errgo.Notef(err, "last argument cannot be used for Unmarshal")
 
230
        }
 
231
        if t.NumOut() > 0 {
 
232
                //      func(p Params, arg *ArgT) error
 
233
                //      func(p Params, arg *ArgT) (ResultT, error)
 
234
                if et := t.Out(t.NumOut() - 1); et != errorType {
 
235
                        return nil, errgo.Newf("final result parameter is %s, need error", et)
 
236
                }
 
237
        }
 
238
        return pt, nil
 
239
}
 
240
 
 
241
// handlerFunc returns a function that will call a function of the given type,
 
242
// unmarshaling request parameters and marshaling the response as
 
243
// appropriate.
 
244
func (e ErrorMapper) handlerFunc(ft reflect.Type) (func(fv reflect.Value, p Params), *requestType, error) {
 
245
        rt, err := checkHandleType(ft)
 
246
        if err != nil {
 
247
                return nil, nil, errgo.Mask(err)
 
248
        }
 
249
        return e.handleResult(ft, handleParams(ft, rt)), rt, nil
 
250
}
 
251
 
 
252
// handleParams handles unmarshaling the parameters to be passed to
 
253
// a function of type ft. The rt parameter describes ft (as determined by
 
254
// checkHandleType). The returned function accepts the actual function
 
255
// value to use in the call as well as the request parameters and returns
 
256
// the result value to use for marshaling.
 
257
func handleParams(
 
258
        ft reflect.Type,
 
259
        rt *requestType,
 
260
) func(fv reflect.Value, p Params) ([]reflect.Value, error) {
 
261
        returnJSON := ft.NumOut() > 1
 
262
        needsParams := ft.In(0) == paramsType
 
263
        if needsParams {
 
264
                argStructType := ft.In(1).Elem()
 
265
                return func(fv reflect.Value, p Params) ([]reflect.Value, error) {
 
266
                        if err := p.Request.ParseForm(); err != nil {
 
267
                                return nil, errgo.WithCausef(err, ErrUnmarshal, "cannot parse HTTP request form")
 
268
                        }
 
269
                        if returnJSON {
 
270
                                p.Response = headerOnlyResponseWriter{p.Response.Header()}
 
271
                        }
 
272
                        argv := reflect.New(argStructType)
 
273
                        if err := unmarshal(p, argv, rt); err != nil {
 
274
                                return nil, errgo.NoteMask(err, "cannot unmarshal parameters", errgo.Is(ErrUnmarshal))
 
275
                        }
 
276
                        return fv.Call([]reflect.Value{
 
277
                                reflect.ValueOf(p),
 
278
                                argv,
 
279
                        }), nil
 
280
                }
 
281
        }
 
282
        argStructType := ft.In(0).Elem()
 
283
        return func(fv reflect.Value, p Params) ([]reflect.Value, error) {
 
284
                if err := p.Request.ParseForm(); err != nil {
 
285
                        return nil, errgo.WithCausef(err, ErrUnmarshal, "cannot parse HTTP request form")
 
286
                }
 
287
                argv := reflect.New(argStructType)
 
288
                if err := unmarshal(p, argv, rt); err != nil {
 
289
                        return nil, errgo.NoteMask(err, "cannot unmarshal parameters", errgo.Is(ErrUnmarshal))
 
290
                }
 
291
                return fv.Call([]reflect.Value{argv}), nil
 
292
        }
 
293
 
 
294
}
 
295
 
 
296
// handleResult handles the marshaling of the result values from the call to a function
 
297
// of type ft. The returned function accepts the actual function value to use in the
 
298
// call as well as the request parameters.
 
299
func (e ErrorMapper) handleResult(
 
300
        ft reflect.Type,
 
301
        f func(fv reflect.Value, p Params) ([]reflect.Value, error),
 
302
) func(fv reflect.Value, p Params) {
 
303
        switch ft.NumOut() {
 
304
        case 0:
 
305
                //      func(w http.ResponseWriter, p Params, arg *ArgT)
 
306
                return func(fv reflect.Value, p Params) {
 
307
                        _, err := f(fv, p)
 
308
                        if err != nil {
 
309
                                e.WriteError(p.Response, err)
 
310
                        }
 
311
                }
 
312
        case 1:
 
313
                //      func(w http.ResponseWriter, p Params, arg *ArgT) error
 
314
                return func(fv reflect.Value, p Params) {
 
315
                        out, err := f(fv, p)
 
316
                        if err != nil {
 
317
                                e.WriteError(p.Response, err)
 
318
                                return
 
319
                        }
 
320
                        herr := out[0].Interface()
 
321
                        if herr != nil {
 
322
                                e.WriteError(p.Response, herr.(error))
 
323
                        }
 
324
                }
 
325
        case 2:
 
326
                //      func(header http.Header, p Params, arg *ArgT) (ResultT, error)
 
327
                return func(fv reflect.Value, p Params) {
 
328
                        out, err := f(fv, p)
 
329
                        if err != nil {
 
330
                                e.WriteError(p.Response, err)
 
331
                                return
 
332
                        }
 
333
                        herr := out[1].Interface()
 
334
                        if herr != nil {
 
335
                                e.WriteError(p.Response, herr.(error))
 
336
                                return
 
337
                        }
 
338
                        err = WriteJSON(p.Response, http.StatusOK, out[0].Interface())
 
339
                        if err != nil {
 
340
                                e.WriteError(p.Response, err)
 
341
                        }
 
342
                }
 
343
        default:
 
344
                panic("unreachable")
 
345
        }
 
346
}
 
347
 
 
348
// ToHTTP converts an httprouter.Handle into an http.Handler.
 
349
// It will pass no path variables to h.
 
350
func ToHTTP(h httprouter.Handle) http.Handler {
 
351
        return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 
352
                h(w, req, nil)
 
353
        })
 
354
}
 
355
 
 
356
// JSONHandler is like httprouter.Handle except that it returns a
 
357
// body (to be converted to JSON) and an error.
 
358
// The Header parameter can be used to set
 
359
// custom headers on the response.
 
360
type JSONHandler func(Params) (interface{}, error)
 
361
 
 
362
// ErrorHandler is like httprouter.Handle except it returns an error
 
363
// which may be returned as the error body of the response.
 
364
// An ErrorHandler function should not itself write to the ResponseWriter
 
365
// if it returns an error.
 
366
type ErrorHandler func(Params) error
 
367
 
 
368
// HandleJSON returns a handler that writes the return value of handle
 
369
// as a JSON response. If handle returns an error, it is passed through
 
370
// the error mapper.
 
371
//
 
372
// Note that the Params argument passed to handle will not
 
373
// have its PathPattern set as that information is not available.
 
374
func (e ErrorMapper) HandleJSON(handle JSONHandler) httprouter.Handle {
 
375
        return func(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
 
376
                val, err := handle(Params{
 
377
                        Response: headerOnlyResponseWriter{w.Header()},
 
378
                        Request:  req,
 
379
                        PathVar:  p,
 
380
                })
 
381
                if err == nil {
 
382
                        if err = WriteJSON(w, http.StatusOK, val); err == nil {
 
383
                                return
 
384
                        }
 
385
                }
 
386
                e.WriteError(w, err)
 
387
        }
 
388
}
 
389
 
 
390
// HandleErrors returns a handler that passes any non-nil error returned
 
391
// by handle through the error mapper and writes it as a JSON response.
 
392
//
 
393
// Note that the Params argument passed to handle will not
 
394
// have its PathPattern set as that information is not available.
 
395
func (e ErrorMapper) HandleErrors(handle ErrorHandler) httprouter.Handle {
 
396
        return func(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
 
397
                w1 := responseWriter{
 
398
                        ResponseWriter: w,
 
399
                }
 
400
                if err := handle(Params{
 
401
                        Response: &w1,
 
402
                        Request:  req,
 
403
                        PathVar:  p,
 
404
                }); err != nil {
 
405
                        // We write the error only if the header hasn't
 
406
                        // already been written, because if it has, then
 
407
                        // we will not be able to set the appropriate error
 
408
                        // response code, and there's a danger that we
 
409
                        // may be corrupting output by appending
 
410
                        // a JSON error message to it.
 
411
                        if !w1.headerWritten {
 
412
                                e.WriteError(w, err)
 
413
                        }
 
414
                        // TODO log the error?
 
415
                }
 
416
        }
 
417
}
 
418
 
 
419
// WriteError writes an error to a ResponseWriter
 
420
// and sets the HTTP status code.
 
421
//
 
422
// It uses WriteJSON to write the error body returned from
 
423
// the ErrorMapper so it is possible to add custom
 
424
// headers to the HTTP error response by implementing
 
425
// HeaderSetter.
 
426
func (e ErrorMapper) WriteError(w http.ResponseWriter, err error) {
 
427
        status, resp := e(err)
 
428
        WriteJSON(w, status, resp)
 
429
}
 
430
 
 
431
// WriteJSON writes the given value to the ResponseWriter
 
432
// and sets the HTTP status to the given code.
 
433
//
 
434
// If val implements the HeaderSetter interface, the SetHeader
 
435
// method will be called to add additional headers to the
 
436
// HTTP response. It is called after the Content-Type header
 
437
// has been added, so can be used to override the content type
 
438
// if required.
 
439
func WriteJSON(w http.ResponseWriter, code int, val interface{}) error {
 
440
        // TODO consider marshalling directly to w using json.NewEncoder.
 
441
        // pro: this will not require a full buffer allocation.
 
442
        // con: if there's an error after the first write, it will be lost.
 
443
        data, err := json.Marshal(val)
 
444
        if err != nil {
 
445
                // TODO(rog) log an error if this fails and lose the
 
446
                // error return, because most callers will need
 
447
                // to do that anyway.
 
448
                return errgo.Mask(err)
 
449
        }
 
450
        w.Header().Set("content-type", "application/json")
 
451
        if headerSetter, ok := val.(HeaderSetter); ok {
 
452
                headerSetter.SetHeader(w.Header())
 
453
        }
 
454
        w.WriteHeader(code)
 
455
        w.Write(data)
 
456
        return nil
 
457
}
 
458
 
 
459
// HeaderSetter is the interface checked for by WriteJSON.
 
460
// If implemented on a value passed to WriteJSON, the SetHeader
 
461
// method will be called to allow it to set custom headers
 
462
// on the response.
 
463
type HeaderSetter interface {
 
464
        SetHeader(http.Header)
 
465
}
 
466
 
 
467
// CustomHeader is a type that allows a JSON value to
 
468
// set custom HTTP headers associated with the
 
469
// HTTP response.
 
470
type CustomHeader struct {
 
471
        // Body holds the JSON-marshaled body of the response.
 
472
        Body interface{}
 
473
 
 
474
        // SetHeaderFunc holds a function that will be called
 
475
        // to set any custom headers on the response.
 
476
        SetHeaderFunc func(http.Header)
 
477
}
 
478
 
 
479
// MarshalJSON implements json.Marshaler by marshaling
 
480
// h.Body.
 
481
func (h CustomHeader) MarshalJSON() ([]byte, error) {
 
482
        return json.Marshal(h.Body)
 
483
}
 
484
 
 
485
// SetHeader implements HeaderSetter by calling
 
486
// h.SetHeaderFunc.
 
487
func (h CustomHeader) SetHeader(header http.Header) {
 
488
        h.SetHeaderFunc(header)
 
489
}
 
490
 
 
491
// Ensure statically that responseWriter does implement http.Flusher.
 
492
var _ http.Flusher = (*responseWriter)(nil)
 
493
 
 
494
// responseWriter wraps http.ResponseWriter but allows us
 
495
// to find out whether any body has already been written.
 
496
type responseWriter struct {
 
497
        headerWritten bool
 
498
        http.ResponseWriter
 
499
}
 
500
 
 
501
func (w *responseWriter) Write(data []byte) (int, error) {
 
502
        w.headerWritten = true
 
503
        return w.ResponseWriter.Write(data)
 
504
}
 
505
 
 
506
func (w *responseWriter) WriteHeader(code int) {
 
507
        w.headerWritten = true
 
508
        w.ResponseWriter.WriteHeader(code)
 
509
}
 
510
 
 
511
// Flush implements http.Flusher.Flush.
 
512
func (w *responseWriter) Flush() {
 
513
        w.headerWritten = true
 
514
        if f, ok := w.ResponseWriter.(http.Flusher); ok {
 
515
                f.Flush()
 
516
        }
 
517
}
 
518
 
 
519
type headerOnlyResponseWriter struct {
 
520
        h http.Header
 
521
}
 
522
 
 
523
func (w headerOnlyResponseWriter) Header() http.Header {
 
524
        return w.h
 
525
}
 
526
 
 
527
func (w headerOnlyResponseWriter) Write([]byte) (int, error) {
 
528
        // TODO log or panic when this happens?
 
529
        return 0, errgo.New("inappropriate call to ResponseWriter.Write in JSON-returning handler")
 
530
}
 
531
 
 
532
func (w headerOnlyResponseWriter) WriteHeader(code int) {
 
533
        // TODO log or panic when this happens?
 
534
}
 
535
 
 
536
func withoutReceiver(t reflect.Type) reflect.Type {
 
537
        return withoutReceiverType{t}
 
538
}
 
539
 
 
540
type withoutReceiverType struct {
 
541
        reflect.Type
 
542
}
 
543
 
 
544
func (t withoutReceiverType) NumIn() int {
 
545
        return t.Type.NumIn() - 1
 
546
}
 
547
 
 
548
func (t withoutReceiverType) In(i int) reflect.Type {
 
549
        return t.Type.In(i + 1)
 
550
}