blob: 5cc53ae4f681405a46d16ff09839f364a38a3792 [file] [log] [blame]
Don Newton379ae252019-04-01 12:17:06 -04001package runtime
2
3import (
4 "errors"
5 "net/http"
6)
7
8// MIMEWildcard is the fallback MIME type used for requests which do not match
9// a registered MIME type.
10const MIMEWildcard = "*"
11
12var (
13 acceptHeader = http.CanonicalHeaderKey("Accept")
14 contentTypeHeader = http.CanonicalHeaderKey("Content-Type")
15
16 defaultMarshaler = &JSONPb{OrigName: true}
17)
18
19// MarshalerForRequest returns the inbound/outbound marshalers for this request.
20// It checks the registry on the ServeMux for the MIME type set by the Content-Type header.
21// If it isn't set (or the request Content-Type is empty), checks for "*".
22// If there are multiple Content-Type headers set, choose the first one that it can
23// exactly match in the registry.
24// Otherwise, it follows the above logic for "*"/InboundMarshaler/OutboundMarshaler.
25func MarshalerForRequest(mux *ServeMux, r *http.Request) (inbound Marshaler, outbound Marshaler) {
26 for _, acceptVal := range r.Header[acceptHeader] {
27 if m, ok := mux.marshalers.mimeMap[acceptVal]; ok {
28 outbound = m
29 break
30 }
31 }
32
33 for _, contentTypeVal := range r.Header[contentTypeHeader] {
34 if m, ok := mux.marshalers.mimeMap[contentTypeVal]; ok {
35 inbound = m
36 break
37 }
38 }
39
40 if inbound == nil {
41 inbound = mux.marshalers.mimeMap[MIMEWildcard]
42 }
43 if outbound == nil {
44 outbound = inbound
45 }
46
47 return inbound, outbound
48}
49
50// marshalerRegistry is a mapping from MIME types to Marshalers.
51type marshalerRegistry struct {
52 mimeMap map[string]Marshaler
53}
54
55// add adds a marshaler for a case-sensitive MIME type string ("*" to match any
56// MIME type).
57func (m marshalerRegistry) add(mime string, marshaler Marshaler) error {
58 if len(mime) == 0 {
59 return errors.New("empty MIME type")
60 }
61
62 m.mimeMap[mime] = marshaler
63
64 return nil
65}
66
67// makeMarshalerMIMERegistry returns a new registry of marshalers.
68// It allows for a mapping of case-sensitive Content-Type MIME type string to runtime.Marshaler interfaces.
69//
70// For example, you could allow the client to specify the use of the runtime.JSONPb marshaler
71// with a "application/jsonpb" Content-Type and the use of the runtime.JSONBuiltin marshaler
72// with a "application/json" Content-Type.
73// "*" can be used to match any Content-Type.
74// This can be attached to a ServerMux with the marshaler option.
75func makeMarshalerMIMERegistry() marshalerRegistry {
76 return marshalerRegistry{
77 mimeMap: map[string]Marshaler{
78 MIMEWildcard: defaultMarshaler,
79 },
80 }
81}
82
83// WithMarshalerOption returns a ServeMuxOption which associates inbound and outbound
84// Marshalers to a MIME type in mux.
85func WithMarshalerOption(mime string, marshaler Marshaler) ServeMuxOption {
86 return func(mux *ServeMux) {
87 if err := mux.marshalers.add(mime, marshaler); err != nil {
88 panic(err)
89 }
90 }
91}