Skip to content

Commit 32d2d86

Browse files
committed
Merge branch 'master' into swagger_descriptions
2 parents ae2eadc + f5cb0d7 commit 32d2d86

File tree

3 files changed

+50
-105
lines changed

3 files changed

+50
-105
lines changed

runtime/marshaler_registry.go

Lines changed: 43 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@ import (
55
"net/http"
66
)
77

8-
const mimeWildcard = "*"
8+
// MIMEWildCard is the fallback MIME type used for requests which do not match
9+
// a registered MIME type.
10+
const MIMEWildcard = "*"
911

1012
var (
11-
defaultMarshaler = &JSONPb{OrigName: true}
12-
13+
acceptHeader = http.CanonicalHeaderKey("Accept")
1314
contentTypeHeader = http.CanonicalHeaderKey("Content-Type")
15+
16+
defaultMarshaler = &JSONPb{OrigName: true}
1417
)
1518

1619
// MarshalerForRequest returns the inbound/outbound marshalers for this request.
@@ -20,117 +23,68 @@ var (
2023
// exactly match in the registry.
2124
// Otherwise, it follows the above logic for "*"/InboundMarshaler/OutboundMarshaler.
2225
func MarshalerForRequest(mux *ServeMux, r *http.Request) (inbound Marshaler, outbound Marshaler) {
23-
headerVals := append(append([]string(nil), r.Header[contentTypeHeader]...), "*")
24-
25-
for _, val := range headerVals {
26-
m := mux.marshalers.lookup(val)
27-
if m != nil {
28-
if inbound == nil {
29-
inbound = m.inbound
30-
}
31-
if outbound == nil {
32-
outbound = m.outbound
33-
}
26+
for _, acceptVal := range r.Header[acceptHeader] {
27+
if m, ok := mux.marshalers.mimeMap[acceptVal]; ok {
28+
outbound = m
29+
break
3430
}
35-
if inbound != nil && outbound != nil {
36-
// Got them both, return
37-
return inbound, outbound
31+
}
32+
33+
for _, contentTypeVal := range r.Header[contentTypeHeader] {
34+
if m, ok := mux.marshalers.mimeMap[contentTypeVal]; ok {
35+
inbound = m
36+
break
3837
}
3938
}
39+
4040
if inbound == nil {
41-
inbound = defaultMarshaler
41+
inbound = mux.marshalers.mimeMap[MIMEWildcard]
4242
}
4343
if outbound == nil {
44-
outbound = defaultMarshaler
44+
outbound = inbound
4545
}
46-
return inbound, outbound
4746

47+
return inbound, outbound
4848
}
4949

50-
// marshalerRegistry keeps a mapping from MIME types to mimeMarshalers.
51-
type marshalerRegistry map[string]*mimeMarshaler
52-
53-
type mimeMarshaler struct {
54-
inbound Marshaler
55-
outbound Marshaler
50+
// marshalerRegistry is a mapping from MIME types to Marshalers.
51+
type marshalerRegistry struct {
52+
mimeMap map[string]Marshaler
5653
}
5754

58-
// addMarshaler adds an inbound and outbund marshaler for a case-sensitive MIME type string ("*" to match any MIME type).
59-
// Inbound is the marshaler that is used when marshaling inbound requests from the client.
60-
// Outbound is the marshaler that is used when marshaling outbound responses to the client.
61-
func (r *marshalerRegistry) add(mime string, inbound, outbound Marshaler) error {
62-
if mime == "" {
55+
// add adds a marshaler for a case-sensitive MIME type string ("*" to match any
56+
// MIME type).
57+
func (m marshalerRegistry) add(mime string, marshaler Marshaler) error {
58+
if len(mime) == 0 {
6359
return errors.New("empty MIME type")
6460
}
65-
(*r)[mime] = &mimeMarshaler{
66-
inbound: inbound,
67-
outbound: outbound,
68-
}
69-
return nil
70-
}
7161

72-
// addInboundMarshaler adds an inbound marshaler for a case-sensitive MIME type string ("*" to match any MIME type).
73-
// Inbound is the marshaler that is used when marshaling inbound requests from the client.
74-
func (r *marshalerRegistry) addInbound(mime string, inbound Marshaler) error {
75-
if mime == "" {
76-
return errors.New("empty MIME type")
77-
}
78-
if entry := (*r)[mime]; entry != nil {
79-
entry.inbound = inbound
80-
return nil
81-
}
82-
(*r)[mime] = &mimeMarshaler{inbound: inbound}
83-
return nil
84-
}
62+
m.mimeMap[mime] = marshaler
8563

86-
// addOutBound adds an outbund marshaler for a case-sensitive MIME type string ("*" to match any MIME type).
87-
// Outbound is the marshaler that is used when marshaling outbound responses to the client.
88-
func (r *marshalerRegistry) addOutbound(mime string, outbound Marshaler) error {
89-
mime = http.CanonicalHeaderKey(mime)
90-
if mime == "" {
91-
return errors.New("empty MIME type")
92-
}
93-
if entry := (*r)[mime]; entry != nil {
94-
entry.outbound = outbound
95-
return nil
96-
}
97-
(*r)[mime] = &mimeMarshaler{outbound: outbound}
9864
return nil
99-
10065
}
10166

102-
func (r *marshalerRegistry) lookup(mime string) *mimeMarshaler {
103-
if r == nil {
104-
return nil
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 "applicaton/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.
75+
func makeMarshalerMIMERegistry() marshalerRegistry {
76+
return marshalerRegistry{
77+
mimeMap: map[string]Marshaler{
78+
MIMEWildcard: defaultMarshaler,
79+
},
10580
}
106-
return (*r)[mime]
10781
}
10882

10983
// WithMarshalerOption returns a ServeMuxOption which associates inbound and outbound
11084
// Marshalers to a MIME type in mux.
111-
func WithMarshalerOption(mime string, in, out Marshaler) ServeMuxOption {
112-
return func(mux *ServeMux) {
113-
if err := mux.marshalers.add(mime, in, out); err != nil {
114-
panic(err)
115-
}
116-
}
117-
}
118-
119-
// WithInboundMarshalerOption returns a ServeMuxOption which associates an inbound
120-
// Marshaler to a MIME type in mux.
121-
func WithInboundMarshalerOption(mime string, in Marshaler) ServeMuxOption {
122-
return func(mux *ServeMux) {
123-
if err := mux.marshalers.addInbound(mime, in); err != nil {
124-
panic(err)
125-
}
126-
}
127-
}
128-
129-
// WithOutboundMarshalerOption returns a ServeMuxOption which associates an outbound
130-
// Marshaler to a MIME type in mux.
131-
func WithOutboundMarshalerOption(mime string, out Marshaler) ServeMuxOption {
85+
func WithMarshalerOption(mime string, marshaler Marshaler) ServeMuxOption {
13286
return func(mux *ServeMux) {
133-
if err := mux.marshalers.addOutbound(mime, out); err != nil {
87+
if err := mux.marshalers.add(mime, marshaler); err != nil {
13488
panic(err)
13589
}
13690
}

runtime/marshaler_registry_test.go

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ func TestMarshalerForRequest(t *testing.T) {
1414
if err != nil {
1515
t.Fatalf(`http.NewRequest("GET", "http://example.com", nil) failed with %v; want success`, err)
1616
}
17-
r.Header.Set("Content-Type", "application/x-example")
17+
r.Header.Set("Accept", "application/x-out")
18+
r.Header.Set("Content-Type", "application/x-in")
1819

1920
mux := runtime.NewServeMux()
2021

@@ -26,38 +27,28 @@ func TestMarshalerForRequest(t *testing.T) {
2627
t.Errorf("out = %#v; want a runtime.JSONPb", in)
2728
}
2829

29-
var marshalers [6]dummyMarshaler
30+
var marshalers [3]dummyMarshaler
3031
specs := []struct {
3132
opt runtime.ServeMuxOption
3233

3334
wantIn runtime.Marshaler
3435
wantOut runtime.Marshaler
3536
}{
3637
{
37-
opt: runtime.WithMarshalerOption("*", &marshalers[0], &marshalers[0]),
38+
opt: runtime.WithMarshalerOption(runtime.MIMEWildcard, &marshalers[0]),
3839
wantIn: &marshalers[0],
3940
wantOut: &marshalers[0],
4041
},
4142
{
42-
opt: runtime.WithInboundMarshalerOption("*", &marshalers[1]),
43+
opt: runtime.WithMarshalerOption("application/x-in", &marshalers[1]),
4344
wantIn: &marshalers[1],
4445
wantOut: &marshalers[0],
4546
},
4647
{
47-
opt: runtime.WithOutboundMarshalerOption("application/x-example", &marshalers[2]),
48+
opt: runtime.WithMarshalerOption("application/x-out", &marshalers[2]),
4849
wantIn: &marshalers[1],
4950
wantOut: &marshalers[2],
5051
},
51-
{
52-
opt: runtime.WithInboundMarshalerOption("application/x-example", &marshalers[3]),
53-
wantIn: &marshalers[3],
54-
wantOut: &marshalers[2],
55-
},
56-
{
57-
opt: runtime.WithMarshalerOption("application/x-example", &marshalers[4], &marshalers[5]),
58-
wantIn: &marshalers[4],
59-
wantOut: &marshalers[5],
60-
},
6152
}
6253
for i, spec := range specs {
6354
var opts []runtime.ServeMuxOption

runtime/mux.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func NewServeMux(opts ...ServeMuxOption) *ServeMux {
4141
serveMux := &ServeMux{
4242
handlers: make(map[string][]handler),
4343
forwardResponseOptions: make([]func(context.Context, http.ResponseWriter, proto.Message) error, 0),
44-
marshalers: make(marshalerRegistry),
44+
marshalers: makeMarshalerMIMERegistry(),
4545
}
4646

4747
for _, opt := range opts {

0 commit comments

Comments
 (0)