-
Notifications
You must be signed in to change notification settings - Fork 0
/
respond.go
156 lines (139 loc) · 4.7 KB
/
respond.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
package maleohttp
import (
"context"
"io"
"net/http"
"github.com/tigorlazuardi/maleo"
)
// Responder handles the response and writing to http.ResponseWriter.
type Responder struct {
encoder Encoder
transformer BodyTransformer
errorTransformer ErrorBodyTransformer
maleo *maleo.Maleo
compressor Compressor
streamCompressor StreamCompressor
callerDepth int
hooks RespondHookList
}
// NewResponder creates a new Responder instance.
//
// It has the following default values:
//
// - Encoder: JSONEncoder (encodes to JSON)
//
// - BodyTransformer: NoopBodyTransform (does nothing to whatever value you pass in)
//
// - ErrorBodyTransformer: SimpleErrorTransformer (encodes error to {"error": "message/err.Error()"}) with JSONEncoder.
// Different Encoder may have different output.
//
// - Maleo: points to the global maleo instance
//
// - Compressor: NoCompression.
func NewResponder() *Responder {
return &Responder{
encoder: NewJSONEncoder(),
transformer: NoopBodyTransform{},
errorTransformer: SimpleErrorTransformer{},
maleo: maleo.Global(),
compressor: NoCompression{},
streamCompressor: NoCompression{},
callerDepth: 2, // default to 3, which is wherever the user calls Responder.Respond() or it's derivatives.
}
}
// SetErrorTransformer sets the ErrorBodyTransformer to be used by the Responder.
func (r *Responder) SetErrorTransformer(errorTransformer ErrorBodyTransformer) {
r.errorTransformer = errorTransformer
}
// SetEncoder sets the Encoder to be used by the Responder.
func (r *Responder) SetEncoder(encoder Encoder) {
r.encoder = encoder
}
// SetBodyTransformer sets the BodyTransformer to be used by the Responder.
func (r *Responder) SetBodyTransformer(transform BodyTransformer) {
r.transformer = transform
}
// SetMaleo sets the maleo instance to be used by the Responder.
func (r *Responder) SetMaleo(t *maleo.Maleo) {
r.maleo = t
}
// SetCompressor sets the compression to be used by the Responder.
func (r *Responder) SetCompressor(compressor Compressor) {
r.compressor = compressor
}
// SetCallerDepth sets the caller depth to be used to get caller function by the Responder.
func (r *Responder) SetCallerDepth(depth int) {
r.callerDepth = depth
}
// SetStreamCompressor sets the stream compression to be used by the Responder.
func (r *Responder) SetStreamCompressor(streamCompressor StreamCompressor) {
r.streamCompressor = streamCompressor
}
func (r Responder) buildOption(statusCode int, rw http.ResponseWriter, request *http.Request, body any, opts ...RespondOption) *RespondContext {
opt := &RespondContext{
Encoder: r.encoder,
BodyTransformer: r.transformer,
Compressor: r.compressor,
StatusCode: statusCode,
ErrorBodyTransformer: r.errorTransformer,
CallerDepth: r.callerDepth,
StreamCompressor: r.streamCompressor,
}
for _, o := range opts {
o.Apply(opt)
}
opt.Caller = maleo.GetCaller(opt.CallerDepth + 1)
for _, hook := range r.hooks {
opt = hook.BeforeRespond(opt, rw, request, body)
}
return opt
}
func (r Responder) buildOptionError(statusCode int, rw http.ResponseWriter, request *http.Request, errPayload error, opts ...RespondOption) *RespondContext {
opt := &RespondContext{
Encoder: r.encoder,
BodyTransformer: r.transformer,
Compressor: r.compressor,
StatusCode: statusCode,
ErrorBodyTransformer: r.errorTransformer,
CallerDepth: r.callerDepth,
StreamCompressor: r.streamCompressor,
}
for _, o := range opts {
o.Apply(opt)
}
opt.Caller = maleo.GetCaller(opt.CallerDepth + 1)
for _, hook := range r.hooks {
opt = hook.BeforeRespondError(opt, rw, request, errPayload)
}
return opt
}
func (r Responder) buildOptionStream(statusCode int, rw http.ResponseWriter, request *http.Request, reader io.Reader, opts ...RespondOption) *RespondContext {
opt := &RespondContext{
Encoder: r.encoder,
BodyTransformer: r.transformer,
Compressor: r.compressor,
StatusCode: statusCode,
ErrorBodyTransformer: r.errorTransformer,
CallerDepth: r.callerDepth,
StreamCompressor: r.streamCompressor,
}
for _, o := range opts {
o.Apply(opt)
}
opt.Caller = maleo.GetCaller(opt.CallerDepth + 1)
for _, hook := range r.hooks {
opt = hook.BeforeRespondStream(opt, rw, request, reader)
}
return opt
}
var requestBodyKey = struct{ key int }{777}
func clonedBodyFromContext(ctx context.Context) ClonedBody {
body, ok := ctx.Value(requestBodyKey).(ClonedBody)
if !ok {
return nil
}
return body
}
func contextWithClonedBody(ctx context.Context, body ClonedBody) context.Context {
return context.WithValue(ctx, requestBodyKey, body)
}