1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the LGPLv3, see LICENCE file for details.
12
"github.com/julienschmidt/httprouter"
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.
19
// The httpStatus value reports the desired HTTP status.
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{})
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()
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:
39
// func(p Params, arg *ArgT)
40
// func(p Params, arg *ArgT) error
41
// func(p Params, arg *ArgT) (ResultT, error)
44
// func(arg *ArgT) error
45
// func(arg *ArgT) (ResultT, error)
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
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.
61
// If an error is returned from f, it is passed through the error mapper before
62
// writing as a JSON response.
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.
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())
76
panic(errgo.Notef(err, "bad handler function"))
81
Handle: func(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
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:
95
// func(httprequest.Params) (T, error)
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.
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.
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.
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)
112
panic(errgo.Notef(err, "bad handler function"))
114
hasClose := wt.Implements(ioCloserType)
115
hs := make([]Handler, 0, wt.NumMethod())
117
for i := 0; i < wt.NumMethod(); i++ {
123
if m.Name == "Close" {
125
panic(errgo.Newf("bad type for Close method (got %v want func(%v) error", m.Type, wt))
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),
133
mt := withoutReceiver(m.Type)
134
hf, rt, err := e.handlerFunc(mt)
136
panic(errgo.Notef(err, "bad type for method %s", m.Name))
138
if rt.method == "" || rt.path == "" {
139
panic(errgo.Notef(err, "method %s does not specify route method and path", m.Name))
141
handler := func(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
142
terrv := fv.Call([]reflect.Value{
143
reflect.ValueOf(Params{
147
PathPattern: rt.path,
150
tv, errv := terrv[0], terrv[1]
152
e.WriteError(w, errv.Interface().(error))
156
defer tv.Interface().(io.Closer).Close()
158
hf(tv.Method(i), Params{
162
PathPattern: rt.path,
166
hs = append(hs, Handler{
174
panic(errgo.Newf("no exported methods defined on %s", wt))
179
func checkHandlersWrapperFunc(fv reflect.Value) (reflect.Type, error) {
181
if ft.Kind() != reflect.Func {
182
return nil, errgo.Newf("expected function, got %v", ft)
185
return nil, errgo.Newf("function is nil")
187
if n := ft.NumIn(); n != 1 {
188
return nil, errgo.Newf("got %d arguments, want 1", n)
190
if n := ft.NumOut(); n != 2 {
191
return nil, errgo.Newf("function returns %d values, want 2", n)
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)
197
return ft.Out(0), nil
200
// Handler defines a HTTP handler that will handle the
201
// given HTTP method at the given httprouter path
202
type Handler struct {
205
Handle httprouter.Handle
208
func checkHandleType(t reflect.Type) (*requestType, error) {
209
if t.Kind() != reflect.Func {
210
return nil, errgo.New("not a function")
212
if n := t.NumIn(); n != 1 && n != 2 {
213
return nil, errgo.Newf("has %d parameters, need 1 or 2", t.NumIn())
216
return nil, errgo.Newf("has %d result parameters, need 0, 1 or 2", t.NumOut())
219
if t.In(0) != paramsType {
220
return nil, errgo.Newf("first argument is %v, need httprequest.Params", t.In(0))
223
if t.In(0) == paramsType {
224
return nil, errgo.Newf("no argument parameter after Params argument")
227
pt, err := getRequestType(t.In(t.NumIn() - 1))
229
return nil, errgo.Notef(err, "last argument cannot be used for Unmarshal")
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)
241
// handlerFunc returns a function that will call a function of the given type,
242
// unmarshaling request parameters and marshaling the response as
244
func (e ErrorMapper) handlerFunc(ft reflect.Type) (func(fv reflect.Value, p Params), *requestType, error) {
245
rt, err := checkHandleType(ft)
247
return nil, nil, errgo.Mask(err)
249
return e.handleResult(ft, handleParams(ft, rt)), rt, nil
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.
260
) func(fv reflect.Value, p Params) ([]reflect.Value, error) {
261
returnJSON := ft.NumOut() > 1
262
needsParams := ft.In(0) == paramsType
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")
270
p.Response = headerOnlyResponseWriter{p.Response.Header()}
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))
276
return fv.Call([]reflect.Value{
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")
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))
291
return fv.Call([]reflect.Value{argv}), nil
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(
301
f func(fv reflect.Value, p Params) ([]reflect.Value, error),
302
) func(fv reflect.Value, p Params) {
305
// func(w http.ResponseWriter, p Params, arg *ArgT)
306
return func(fv reflect.Value, p Params) {
309
e.WriteError(p.Response, err)
313
// func(w http.ResponseWriter, p Params, arg *ArgT) error
314
return func(fv reflect.Value, p Params) {
317
e.WriteError(p.Response, err)
320
herr := out[0].Interface()
322
e.WriteError(p.Response, herr.(error))
326
// func(header http.Header, p Params, arg *ArgT) (ResultT, error)
327
return func(fv reflect.Value, p Params) {
330
e.WriteError(p.Response, err)
333
herr := out[1].Interface()
335
e.WriteError(p.Response, herr.(error))
338
err = WriteJSON(p.Response, http.StatusOK, out[0].Interface())
340
e.WriteError(p.Response, err)
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) {
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)
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
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
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()},
382
if err = WriteJSON(w, http.StatusOK, val); err == nil {
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.
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{
400
if err := handle(Params{
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 {
414
// TODO log the error?
419
// WriteError writes an error to a ResponseWriter
420
// and sets the HTTP status code.
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
426
func (e ErrorMapper) WriteError(w http.ResponseWriter, err error) {
427
status, resp := e(err)
428
WriteJSON(w, status, resp)
431
// WriteJSON writes the given value to the ResponseWriter
432
// and sets the HTTP status to the given code.
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
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)
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)
450
w.Header().Set("content-type", "application/json")
451
if headerSetter, ok := val.(HeaderSetter); ok {
452
headerSetter.SetHeader(w.Header())
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
463
type HeaderSetter interface {
464
SetHeader(http.Header)
467
// CustomHeader is a type that allows a JSON value to
468
// set custom HTTP headers associated with the
470
type CustomHeader struct {
471
// Body holds the JSON-marshaled body of the response.
474
// SetHeaderFunc holds a function that will be called
475
// to set any custom headers on the response.
476
SetHeaderFunc func(http.Header)
479
// MarshalJSON implements json.Marshaler by marshaling
481
func (h CustomHeader) MarshalJSON() ([]byte, error) {
482
return json.Marshal(h.Body)
485
// SetHeader implements HeaderSetter by calling
487
func (h CustomHeader) SetHeader(header http.Header) {
488
h.SetHeaderFunc(header)
491
// Ensure statically that responseWriter does implement http.Flusher.
492
var _ http.Flusher = (*responseWriter)(nil)
494
// responseWriter wraps http.ResponseWriter but allows us
495
// to find out whether any body has already been written.
496
type responseWriter struct {
501
func (w *responseWriter) Write(data []byte) (int, error) {
502
w.headerWritten = true
503
return w.ResponseWriter.Write(data)
506
func (w *responseWriter) WriteHeader(code int) {
507
w.headerWritten = true
508
w.ResponseWriter.WriteHeader(code)
511
// Flush implements http.Flusher.Flush.
512
func (w *responseWriter) Flush() {
513
w.headerWritten = true
514
if f, ok := w.ResponseWriter.(http.Flusher); ok {
519
type headerOnlyResponseWriter struct {
523
func (w headerOnlyResponseWriter) Header() http.Header {
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")
532
func (w headerOnlyResponseWriter) WriteHeader(code int) {
533
// TODO log or panic when this happens?
536
func withoutReceiver(t reflect.Type) reflect.Type {
537
return withoutReceiverType{t}
540
type withoutReceiverType struct {
544
func (t withoutReceiverType) NumIn() int {
545
return t.Type.NumIn() - 1
548
func (t withoutReceiverType) In(i int) reflect.Type {
549
return t.Type.In(i + 1)