forked from twitchtv/twirp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver_options.go
178 lines (164 loc) · 5.97 KB
/
server_options.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// Copyright 2018 Twitch Interactive, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may not
// use this file except in compliance with the License. A copy of the License is
// located at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// or in the "license" file accompanying this file. This file is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.
package twirp
import (
"context"
)
// ServerOption is a functional option for extending a Twirp client.
type ServerOption func(*ServerOptions)
// ServerOptions encapsulate the configurable parameters on a Twirp client.
type ServerOptions struct {
Interceptors []Interceptor
Hooks *ServerHooks
pathPrefix *string
JSONSkipDefaults bool
}
func (opts *ServerOptions) PathPrefix() string {
if opts.pathPrefix == nil {
return "/twirp" // default prefix
}
return *opts.pathPrefix
}
// WithServerInterceptors defines the interceptors for a Twirp server.
func WithServerInterceptors(interceptors ...Interceptor) ServerOption {
return func(o *ServerOptions) {
o.Interceptors = append(o.Interceptors, interceptors...)
}
}
// WithServerHooks defines the hooks for a Twirp server.
func WithServerHooks(hooks *ServerHooks) ServerOption {
return func(o *ServerOptions) {
o.Hooks = hooks
}
}
// ServerHooks is a container for callbacks that can instrument a
// Twirp-generated server. These callbacks all accept a context and return a
// context. They can use this to add to the request context as it threads
// through the system, appending values or deadlines to it.
//
// The RequestReceived and RequestRouted hooks are special: they can return
// errors. If they return a non-nil error, handling for that request will be
// stopped at that point. The Error hook will be triggered, and the error will
// be sent to the client. This can be used for stuff like auth checks before
// deserializing a request.
//
// The RequestReceived hook is always called first, and it is called for every
// request that the Twirp server handles. The last hook to be called in a
// request's lifecycle is always ResponseSent, even in the case of an error.
//
// Details on the timing of each hook are documented as comments on the fields
// of the ServerHooks type.
type ServerHooks struct {
// RequestReceived is called as soon as a request enters the Twirp
// server at the earliest available moment.
RequestReceived func(context.Context) (context.Context, error)
// RequestRouted is called when a request has been routed to a
// particular method of the Twirp server.
RequestRouted func(context.Context) (context.Context, error)
// ResponsePrepared is called when a request has been handled and a
// response is ready to be sent to the client.
ResponsePrepared func(context.Context) context.Context
// ResponseSent is called when all bytes of a response (including an error
// response) have been written. Because the ResponseSent hook is terminal, it
// does not return a context.
ResponseSent func(context.Context)
// Error hook is called when an error occurs while handling a request. The
// Error is passed as argument to the hook.
Error func(context.Context, Error) context.Context
}
// ChainHooks creates a new *ServerHooks which chains the callbacks in
// each of the constituent hooks passed in. Each hook function will be
// called in the order of the ServerHooks values passed in.
//
// For the erroring hooks, RequestReceived and RequestRouted, any returned
// errors prevent processing by later hooks.
func ChainHooks(hooks ...*ServerHooks) *ServerHooks {
if len(hooks) == 0 {
return nil
}
if len(hooks) == 1 {
return hooks[0]
}
return &ServerHooks{
RequestReceived: func(ctx context.Context) (context.Context, error) {
var err error
for _, h := range hooks {
if h != nil && h.RequestReceived != nil {
ctx, err = h.RequestReceived(ctx)
if err != nil {
return ctx, err
}
}
}
return ctx, nil
},
RequestRouted: func(ctx context.Context) (context.Context, error) {
var err error
for _, h := range hooks {
if h != nil && h.RequestRouted != nil {
ctx, err = h.RequestRouted(ctx)
if err != nil {
return ctx, err
}
}
}
return ctx, nil
},
ResponsePrepared: func(ctx context.Context) context.Context {
for _, h := range hooks {
if h != nil && h.ResponsePrepared != nil {
ctx = h.ResponsePrepared(ctx)
}
}
return ctx
},
ResponseSent: func(ctx context.Context) {
for _, h := range hooks {
if h != nil && h.ResponseSent != nil {
h.ResponseSent(ctx)
}
}
},
Error: func(ctx context.Context, twerr Error) context.Context {
for _, h := range hooks {
if h != nil && h.Error != nil {
ctx = h.Error(ctx, twerr)
}
}
return ctx
},
}
}
// WithServerPathPrefix specifies a different prefix for routing.
// If not specified, the "/twirp" prefix is used by default.
// An empty value "" can be speficied to use no prefix.
// The clients must be configured to send requests using the same prefix.
// URL format: "<baseURL>[<prefix>]/<package>.<Service>/<Method>"
// More info on Twirp docs: https://twitchtv.github.io/twirp/docs/routing.html
func WithServerPathPrefix(prefix string) ServerOption {
return func(o *ServerOptions) {
o.pathPrefix = &prefix
}
}
// WithServerJSONSkipDefaults configures JSON serialization to skip
// unpopulated or default values in JSON responses, which results in
// smaller response sizes. This was the default before v7 and can be
// enabled for full backwards compatibility if required.
// This is now disabled by default, because JSON serialization is
// commonly used for manual debugging, in which case it is useful
// to see the full shape of the response.
func WithServerJSONSkipDefaults(skipDefaults bool) ServerOption {
return func(o *ServerOptions) {
o.JSONSkipDefaults = skipDefaults
}
}