...

Source file src/github.com/go-kit/kit/transport/http/server.go

Documentation: github.com/go-kit/kit/transport/http

     1  package http
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"net/http"
     7  
     8  	"github.com/go-kit/kit/endpoint"
     9  	"github.com/go-kit/kit/transport"
    10  	"github.com/go-kit/log"
    11  )
    12  
    13  // Server wraps an endpoint and implements http.Handler.
    14  type Server struct {
    15  	e            endpoint.Endpoint
    16  	dec          DecodeRequestFunc
    17  	enc          EncodeResponseFunc
    18  	before       []RequestFunc
    19  	after        []ServerResponseFunc
    20  	errorEncoder ErrorEncoder
    21  	finalizer    []ServerFinalizerFunc
    22  	errorHandler transport.ErrorHandler
    23  }
    24  
    25  // NewServer constructs a new server, which implements http.Handler and wraps
    26  // the provided endpoint.
    27  func NewServer(
    28  	e endpoint.Endpoint,
    29  	dec DecodeRequestFunc,
    30  	enc EncodeResponseFunc,
    31  	options ...ServerOption,
    32  ) *Server {
    33  	s := &Server{
    34  		e:            e,
    35  		dec:          dec,
    36  		enc:          enc,
    37  		errorEncoder: DefaultErrorEncoder,
    38  		errorHandler: transport.NewLogErrorHandler(log.NewNopLogger()),
    39  	}
    40  	for _, option := range options {
    41  		option(s)
    42  	}
    43  	return s
    44  }
    45  
    46  // ServerOption sets an optional parameter for servers.
    47  type ServerOption func(*Server)
    48  
    49  // ServerBefore functions are executed on the HTTP request object before the
    50  // request is decoded.
    51  func ServerBefore(before ...RequestFunc) ServerOption {
    52  	return func(s *Server) { s.before = append(s.before, before...) }
    53  }
    54  
    55  // ServerAfter functions are executed on the HTTP response writer after the
    56  // endpoint is invoked, but before anything is written to the client.
    57  func ServerAfter(after ...ServerResponseFunc) ServerOption {
    58  	return func(s *Server) { s.after = append(s.after, after...) }
    59  }
    60  
    61  // ServerErrorEncoder is used to encode errors to the http.ResponseWriter
    62  // whenever they're encountered in the processing of a request. Clients can
    63  // use this to provide custom error formatting and response codes. By default,
    64  // errors will be written with the DefaultErrorEncoder.
    65  func ServerErrorEncoder(ee ErrorEncoder) ServerOption {
    66  	return func(s *Server) { s.errorEncoder = ee }
    67  }
    68  
    69  // ServerErrorLogger is used to log non-terminal errors. By default, no errors
    70  // are logged. This is intended as a diagnostic measure. Finer-grained control
    71  // of error handling, including logging in more detail, should be performed in a
    72  // custom ServerErrorEncoder or ServerFinalizer, both of which have access to
    73  // the context.
    74  // Deprecated: Use ServerErrorHandler instead.
    75  func ServerErrorLogger(logger log.Logger) ServerOption {
    76  	return func(s *Server) { s.errorHandler = transport.NewLogErrorHandler(logger) }
    77  }
    78  
    79  // ServerErrorHandler is used to handle non-terminal errors. By default, non-terminal errors
    80  // are ignored. This is intended as a diagnostic measure. Finer-grained control
    81  // of error handling, including logging in more detail, should be performed in a
    82  // custom ServerErrorEncoder or ServerFinalizer, both of which have access to
    83  // the context.
    84  func ServerErrorHandler(errorHandler transport.ErrorHandler) ServerOption {
    85  	return func(s *Server) { s.errorHandler = errorHandler }
    86  }
    87  
    88  // ServerFinalizer is executed at the end of every HTTP request.
    89  // By default, no finalizer is registered.
    90  func ServerFinalizer(f ...ServerFinalizerFunc) ServerOption {
    91  	return func(s *Server) { s.finalizer = append(s.finalizer, f...) }
    92  }
    93  
    94  // ServeHTTP implements http.Handler.
    95  func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    96  	ctx := r.Context()
    97  
    98  	if len(s.finalizer) > 0 {
    99  		iw := &interceptingWriter{w, http.StatusOK, 0}
   100  		defer func() {
   101  			ctx = context.WithValue(ctx, ContextKeyResponseHeaders, iw.Header())
   102  			ctx = context.WithValue(ctx, ContextKeyResponseSize, iw.written)
   103  			for _, f := range s.finalizer {
   104  				f(ctx, iw.code, r)
   105  			}
   106  		}()
   107  		w = iw.reimplementInterfaces()
   108  	}
   109  
   110  	for _, f := range s.before {
   111  		ctx = f(ctx, r)
   112  	}
   113  
   114  	request, err := s.dec(ctx, r)
   115  	if err != nil {
   116  		s.errorHandler.Handle(ctx, err)
   117  		s.errorEncoder(ctx, err, w)
   118  		return
   119  	}
   120  
   121  	response, err := s.e(ctx, request)
   122  	if err != nil {
   123  		s.errorHandler.Handle(ctx, err)
   124  		s.errorEncoder(ctx, err, w)
   125  		return
   126  	}
   127  
   128  	for _, f := range s.after {
   129  		ctx = f(ctx, w)
   130  	}
   131  
   132  	if err := s.enc(ctx, w, response); err != nil {
   133  		s.errorHandler.Handle(ctx, err)
   134  		s.errorEncoder(ctx, err, w)
   135  		return
   136  	}
   137  }
   138  
   139  // ErrorEncoder is responsible for encoding an error to the ResponseWriter.
   140  // Users are encouraged to use custom ErrorEncoders to encode HTTP errors to
   141  // their clients, and will likely want to pass and check for their own error
   142  // types. See the example shipping/handling service.
   143  type ErrorEncoder func(ctx context.Context, err error, w http.ResponseWriter)
   144  
   145  // ServerFinalizerFunc can be used to perform work at the end of an HTTP
   146  // request, after the response has been written to the client. The principal
   147  // intended use is for request logging. In addition to the response code
   148  // provided in the function signature, additional response parameters are
   149  // provided in the context under keys with the ContextKeyResponse prefix.
   150  type ServerFinalizerFunc func(ctx context.Context, code int, r *http.Request)
   151  
   152  // NopRequestDecoder is a DecodeRequestFunc that can be used for requests that do not
   153  // need to be decoded, and simply returns nil, nil.
   154  func NopRequestDecoder(ctx context.Context, r *http.Request) (interface{}, error) {
   155  	return nil, nil
   156  }
   157  
   158  // EncodeJSONResponse is a EncodeResponseFunc that serializes the response as a
   159  // JSON object to the ResponseWriter. Many JSON-over-HTTP services can use it as
   160  // a sensible default. If the response implements Headerer, the provided headers
   161  // will be applied to the response. If the response implements StatusCoder, the
   162  // provided StatusCode will be used instead of 200.
   163  func EncodeJSONResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
   164  	w.Header().Set("Content-Type", "application/json; charset=utf-8")
   165  	if headerer, ok := response.(Headerer); ok {
   166  		for k, values := range headerer.Headers() {
   167  			for _, v := range values {
   168  				w.Header().Add(k, v)
   169  			}
   170  		}
   171  	}
   172  	code := http.StatusOK
   173  	if sc, ok := response.(StatusCoder); ok {
   174  		code = sc.StatusCode()
   175  	}
   176  	w.WriteHeader(code)
   177  	if code == http.StatusNoContent {
   178  		return nil
   179  	}
   180  	return json.NewEncoder(w).Encode(response)
   181  }
   182  
   183  // DefaultErrorEncoder writes the error to the ResponseWriter, by default a
   184  // content type of text/plain, a body of the plain text of the error, and a
   185  // status code of 500. If the error implements Headerer, the provided headers
   186  // will be applied to the response. If the error implements json.Marshaler, and
   187  // the marshaling succeeds, a content type of application/json and the JSON
   188  // encoded form of the error will be used. If the error implements StatusCoder,
   189  // the provided StatusCode will be used instead of 500.
   190  func DefaultErrorEncoder(_ context.Context, err error, w http.ResponseWriter) {
   191  	contentType, body := "text/plain; charset=utf-8", []byte(err.Error())
   192  	if marshaler, ok := err.(json.Marshaler); ok {
   193  		if jsonBody, marshalErr := marshaler.MarshalJSON(); marshalErr == nil {
   194  			contentType, body = "application/json; charset=utf-8", jsonBody
   195  		}
   196  	}
   197  	w.Header().Set("Content-Type", contentType)
   198  	if headerer, ok := err.(Headerer); ok {
   199  		for k, values := range headerer.Headers() {
   200  			for _, v := range values {
   201  				w.Header().Add(k, v)
   202  			}
   203  		}
   204  	}
   205  	code := http.StatusInternalServerError
   206  	if sc, ok := err.(StatusCoder); ok {
   207  		code = sc.StatusCode()
   208  	}
   209  	w.WriteHeader(code)
   210  	w.Write(body)
   211  }
   212  
   213  // StatusCoder is checked by DefaultErrorEncoder. If an error value implements
   214  // StatusCoder, the StatusCode will be used when encoding the error. By default,
   215  // StatusInternalServerError (500) is used.
   216  type StatusCoder interface {
   217  	StatusCode() int
   218  }
   219  
   220  // Headerer is checked by DefaultErrorEncoder. If an error value implements
   221  // Headerer, the provided headers will be applied to the response writer, after
   222  // the Content-Type is set.
   223  type Headerer interface {
   224  	Headers() http.Header
   225  }
   226  

View as plain text