8 "github.com/go-kit/kit/endpoint"
9 "github.com/go-kit/kit/log"
12 // Server wraps an endpoint and implements http.Handler.
16 enc EncodeResponseFunc
18 after []ServerResponseFunc
19 errorEncoder ErrorEncoder
20 finalizer ServerFinalizerFunc
24 // NewServer constructs a new server, which implements http.Handler and wraps
25 // the provided endpoint.
28 dec DecodeRequestFunc,
29 enc EncodeResponseFunc,
30 options ...ServerOption,
36 errorEncoder: DefaultErrorEncoder,
37 logger: log.NewNopLogger(),
39 for _, option := range options {
45 // ServerOption sets an optional parameter for servers.
46 type ServerOption func(*Server)
48 // ServerBefore functions are executed on the HTTP request object before the
49 // request is decoded.
50 func ServerBefore(before ...RequestFunc) ServerOption {
51 return func(s *Server) { s.before = append(s.before, before...) }
54 // ServerAfter functions are executed on the HTTP response writer after the
55 // endpoint is invoked, but before anything is written to the client.
56 func ServerAfter(after ...ServerResponseFunc) ServerOption {
57 return func(s *Server) { s.after = append(s.after, after...) }
60 // ServerErrorEncoder is used to encode errors to the http.ResponseWriter
61 // whenever they're encountered in the processing of a request. Clients can
62 // use this to provide custom error formatting and response codes. By default,
63 // errors will be written with the DefaultErrorEncoder.
64 func ServerErrorEncoder(ee ErrorEncoder) ServerOption {
65 return func(s *Server) { s.errorEncoder = ee }
68 // ServerErrorLogger is used to log non-terminal errors. By default, no errors
69 // are logged. This is intended as a diagnostic measure. Finer-grained control
70 // of error handling, including logging in more detail, should be performed in a
71 // custom ServerErrorEncoder or ServerFinalizer, both of which have access to
73 func ServerErrorLogger(logger log.Logger) ServerOption {
74 return func(s *Server) { s.logger = logger }
77 // ServerFinalizer is executed at the end of every HTTP request.
78 // By default, no finalizer is registered.
79 func ServerFinalizer(f ServerFinalizerFunc) ServerOption {
80 return func(s *Server) { s.finalizer = f }
83 // ServeHTTP implements http.Handler.
84 func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
87 if s.finalizer != nil {
88 iw := &interceptingWriter{w, http.StatusOK, 0}
90 ctx = context.WithValue(ctx, ContextKeyResponseHeaders, iw.Header())
91 ctx = context.WithValue(ctx, ContextKeyResponseSize, iw.written)
92 s.finalizer(ctx, iw.code, r)
97 for _, f := range s.before {
101 request, err := s.dec(ctx, r)
103 s.logger.Log("err", err)
104 s.errorEncoder(ctx, err, w)
108 response, err := s.e(ctx, request)
110 s.logger.Log("err", err)
111 s.errorEncoder(ctx, err, w)
115 for _, f := range s.after {
119 if err := s.enc(ctx, w, response); err != nil {
120 s.logger.Log("err", err)
121 s.errorEncoder(ctx, err, w)
126 // ErrorEncoder is responsible for encoding an error to the ResponseWriter.
127 // Users are encouraged to use custom ErrorEncoders to encode HTTP errors to
128 // their clients, and will likely want to pass and check for their own error
129 // types. See the example shipping/handling service.
130 type ErrorEncoder func(ctx context.Context, err error, w http.ResponseWriter)
132 // ServerFinalizerFunc can be used to perform work at the end of an HTTP
133 // request, after the response has been written to the client. The principal
134 // intended use is for request logging. In addition to the response code
135 // provided in the function signature, additional response parameters are
136 // provided in the context under keys with the ContextKeyResponse prefix.
137 type ServerFinalizerFunc func(ctx context.Context, code int, r *http.Request)
139 // EncodeJSONResponse is a EncodeResponseFunc that serializes the response as a
140 // JSON object to the ResponseWriter. Many JSON-over-HTTP services can use it as
141 // a sensible default. If the response implements Headerer, the provided headers
142 // will be applied to the response. If the response implements StatusCoder, the
143 // provided StatusCode will be used instead of 200.
144 func EncodeJSONResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
145 w.Header().Set("Content-Type", "application/json; charset=utf-8")
146 if headerer, ok := response.(Headerer); ok {
147 for k := range headerer.Headers() {
148 w.Header().Set(k, headerer.Headers().Get(k))
151 code := http.StatusOK
152 if sc, ok := response.(StatusCoder); ok {
153 code = sc.StatusCode()
156 if code == http.StatusNoContent {
159 return json.NewEncoder(w).Encode(response)
162 // DefaultErrorEncoder writes the error to the ResponseWriter, by default a
163 // content type of text/plain, a body of the plain text of the error, and a
164 // status code of 500. If the error implements Headerer, the provided headers
165 // will be applied to the response. If the error implements json.Marshaler, and
166 // the marshaling succeeds, a content type of application/json and the JSON
167 // encoded form of the error will be used. If the error implements StatusCoder,
168 // the provided StatusCode will be used instead of 500.
169 func DefaultErrorEncoder(_ context.Context, err error, w http.ResponseWriter) {
170 contentType, body := "text/plain; charset=utf-8", []byte(err.Error())
171 if marshaler, ok := err.(json.Marshaler); ok {
172 if jsonBody, marshalErr := marshaler.MarshalJSON(); marshalErr == nil {
173 contentType, body = "application/json; charset=utf-8", jsonBody
176 w.Header().Set("Content-Type", contentType)
177 if headerer, ok := err.(Headerer); ok {
178 for k := range headerer.Headers() {
179 w.Header().Set(k, headerer.Headers().Get(k))
182 code := http.StatusInternalServerError
183 if sc, ok := err.(StatusCoder); ok {
184 code = sc.StatusCode()
190 // StatusCoder is checked by DefaultErrorEncoder. If an error value implements
191 // StatusCoder, the StatusCode will be used when encoding the error. By default,
192 // StatusInternalServerError (500) is used.
193 type StatusCoder interface {
197 // Headerer is checked by DefaultErrorEncoder. If an error value implements
198 // Headerer, the provided headers will be applied to the response writer, after
199 // the Content-Type is set.
200 type Headerer interface {
201 Headers() http.Header
204 type interceptingWriter struct {
210 // WriteHeader may not be explicitly called, so care must be taken to
211 // initialize w.code to its default value of http.StatusOK.
212 func (w *interceptingWriter) WriteHeader(code int) {
214 w.ResponseWriter.WriteHeader(code)
217 func (w *interceptingWriter) Write(p []byte) (int, error) {
218 n, err := w.ResponseWriter.Write(p)
219 w.written += int64(n)