Skip to content

Commit 8d57cda

Browse files
authored
openapi3filter: guard BodyEncoder registration behind a RW lock (#934)
* openapi3filter: guard BodyEncoder registration behind a RW lock Signed-off-by: Pierre Fenoll <[email protected]> * docs.sh Signed-off-by: Pierre Fenoll <[email protected]> --------- Signed-off-by: Pierre Fenoll <[email protected]>
1 parent b6f165a commit 8d57cda

File tree

2 files changed

+23
-14
lines changed

2 files changed

+23
-14
lines changed

.github/docs/openapi3filter.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ func RegisterBodyDecoder(contentType string, decoder BodyDecoder)
6969
body decoders should not be created/destroyed by multiple goroutines.
7070

7171
func RegisterBodyEncoder(contentType string, encoder BodyEncoder)
72+
RegisterBodyEncoder enables package-wide decoding of contentType values
73+
7274
func TrimJSONPrefix(data []byte) []byte
7375
TrimJSONPrefix trims one of the possible prefixes
7476

@@ -80,8 +82,7 @@ func UnregisterBodyDecoder(contentType string)
8082
goroutines.
8183

8284
func UnregisterBodyEncoder(contentType string)
83-
This call is not thread-safe: body encoders should not be created/destroyed
84-
by multiple goroutines.
85+
UnregisterBodyEncoder disables package-wide decoding of contentType values
8586

8687
func ValidateParameter(ctx context.Context, input *RequestValidationInput, parameter *openapi3.Parameter) error
8788
ValidateParameter validates a parameter's value by JSON schema. The function
@@ -152,14 +153,13 @@ func RegisteredBodyDecoder(contentType string) BodyDecoder
152153
by multiple goroutines.
153154

154155
type BodyEncoder func(body interface{}) ([]byte, error)
156+
BodyEncoder really is an (encoding/json).Marshaler
155157

156158
func RegisteredBodyEncoder(contentType string) BodyEncoder
157159
RegisteredBodyEncoder returns the registered body encoder for the given
158160
content type.
159161

160162
If no encoder was registered for the given content type, nil is returned.
161-
This call is not thread-safe: body encoders should not be created/destroyed
162-
by multiple goroutines.
163163

164164
type ContentParameterDecoder func(param *openapi3.Parameter, values []string) (interface{}, *openapi3.Schema, error)
165165
A ContentParameterDecoder takes a parameter definition from the OpenAPI

openapi3filter/req_resp_encoder.go

+19-10
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,56 @@ package openapi3filter
33
import (
44
"encoding/json"
55
"fmt"
6+
"sync"
67
)
78

89
func encodeBody(body interface{}, mediaType string) ([]byte, error) {
9-
encoder, ok := bodyEncoders[mediaType]
10-
if !ok {
11-
return nil, &ParseError{
12-
Kind: KindUnsupportedFormat,
13-
Reason: fmt.Sprintf("%s %q", prefixUnsupportedCT, mediaType),
14-
}
10+
if encoder := RegisteredBodyEncoder(mediaType); encoder != nil {
11+
return encoder(body)
12+
}
13+
return nil, &ParseError{
14+
Kind: KindUnsupportedFormat,
15+
Reason: fmt.Sprintf("%s %q", prefixUnsupportedCT, mediaType),
1516
}
16-
return encoder(body)
1717
}
1818

19+
// BodyEncoder really is an (encoding/json).Marshaler
1920
type BodyEncoder func(body interface{}) ([]byte, error)
2021

22+
var bodyEncodersM sync.RWMutex
2123
var bodyEncoders = map[string]BodyEncoder{
2224
"application/json": json.Marshal,
2325
}
2426

27+
// RegisterBodyEncoder enables package-wide decoding of contentType values
2528
func RegisterBodyEncoder(contentType string, encoder BodyEncoder) {
2629
if contentType == "" {
2730
panic("contentType is empty")
2831
}
2932
if encoder == nil {
3033
panic("encoder is not defined")
3134
}
35+
bodyEncodersM.Lock()
3236
bodyEncoders[contentType] = encoder
37+
bodyEncodersM.Unlock()
3338
}
3439

35-
// This call is not thread-safe: body encoders should not be created/destroyed by multiple goroutines.
40+
// UnregisterBodyEncoder disables package-wide decoding of contentType values
3641
func UnregisterBodyEncoder(contentType string) {
3742
if contentType == "" {
3843
panic("contentType is empty")
3944
}
45+
bodyEncodersM.Lock()
4046
delete(bodyEncoders, contentType)
47+
bodyEncodersM.Unlock()
4148
}
4249

4350
// RegisteredBodyEncoder returns the registered body encoder for the given content type.
4451
//
4552
// If no encoder was registered for the given content type, nil is returned.
46-
// This call is not thread-safe: body encoders should not be created/destroyed by multiple goroutines.
4753
func RegisteredBodyEncoder(contentType string) BodyEncoder {
48-
return bodyEncoders[contentType]
54+
bodyEncodersM.RLock()
55+
mayBE := bodyEncoders[contentType]
56+
bodyEncodersM.RUnlock()
57+
return mayBE
4958
}

0 commit comments

Comments
 (0)