...
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 package httpretty
55
56 import (
57 "bytes"
58 "context"
59 "crypto/tls"
60 "encoding/json"
61 "errors"
62 "io"
63 "net/http"
64 "net/textproto"
65 "os"
66 "sync"
67
68 "github.com/henvic/httpretty/internal/color"
69 )
70
71
72
73
74
75 type Formatter interface {
76 Match(mediatype string) bool
77 Format(w io.Writer, src []byte) error
78 }
79
80
81 func WithHide(ctx context.Context) context.Context {
82 return context.WithValue(ctx, contextHide{}, struct{}{})
83 }
84
85
86 type Logger struct {
87
88
89 SkipRequestInfo bool
90
91
92 Time bool
93
94
95
96
97 TLS bool
98
99
100 RequestHeader bool
101
102
103 RequestBody bool
104
105
106 ResponseHeader bool
107
108
109 ResponseBody bool
110
111
112 SkipSanitize bool
113
114
115 Colors bool
116
117
118
119
120 Formatters []Formatter
121
122
123
124 MaxRequestBody int64
125
126
127
128 MaxResponseBody int64
129
130 mu sync.Mutex
131 w io.Writer
132 filter Filter
133 skipHeader map[string]struct{}
134 bodyFilter BodyFilter
135 flusher Flusher
136 }
137
138
139
140
141 type Filter func(req *http.Request) (skip bool, err error)
142
143
144
145
146
147
148
149
150 type BodyFilter func(h http.Header) (skip bool, err error)
151
152
153 type Flusher int
154
155
156 const (
157
158
159 NoBuffer Flusher = iota
160
161
162
163 OnReady
164
165
166 OnEnd
167 )
168
169
170
171 func (l *Logger) SetFilter(f Filter) {
172 l.mu.Lock()
173 defer l.mu.Unlock()
174 l.filter = f
175 }
176
177
178
179 func (l *Logger) SkipHeader(headers []string) {
180 l.mu.Lock()
181 defer l.mu.Unlock()
182 m := map[string]struct{}{}
183 for _, h := range headers {
184 m[textproto.CanonicalMIMEHeaderKey(h)] = struct{}{}
185 }
186 l.skipHeader = m
187 }
188
189
190
191 func (l *Logger) SetBodyFilter(f BodyFilter) {
192 l.mu.Lock()
193 defer l.mu.Unlock()
194 l.bodyFilter = f
195 }
196
197
198 func (l *Logger) SetOutput(w io.Writer) {
199 l.mu.Lock()
200 defer l.mu.Unlock()
201 l.w = w
202 }
203
204
205 func (l *Logger) SetFlusher(f Flusher) {
206 l.mu.Lock()
207 defer l.mu.Unlock()
208 l.flusher = f
209 }
210
211 func (l *Logger) getWriter() io.Writer {
212 if l.w == nil {
213 return os.Stdout
214 }
215
216 return l.w
217 }
218
219 func (l *Logger) getFilter() Filter {
220 l.mu.Lock()
221 f := l.filter
222 defer l.mu.Unlock()
223 return f
224 }
225
226 func (l *Logger) getBodyFilter() BodyFilter {
227 l.mu.Lock()
228 f := l.bodyFilter
229 defer l.mu.Unlock()
230 return f
231 }
232
233 func (l *Logger) cloneSkipHeader() map[string]struct{} {
234 l.mu.Lock()
235 skipped := l.skipHeader
236 l.mu.Unlock()
237
238 m := map[string]struct{}{}
239 for h := range skipped {
240 m[h] = struct{}{}
241 }
242
243 return m
244 }
245
246 type contextHide struct{}
247
248 type roundTripper struct {
249 logger *Logger
250 rt http.RoundTripper
251 }
252
253
254 func (l *Logger) RoundTripper(rt http.RoundTripper) http.RoundTripper {
255 return roundTripper{
256 logger: l,
257 rt: rt,
258 }
259 }
260
261
262 func (r roundTripper) RoundTrip(req *http.Request) (resp *http.Response, err error) {
263 tripper := r.rt
264
265 if tripper == nil {
266
267
268
269 tripper = http.RoundTripper(http.DefaultTransport)
270 }
271
272 l := r.logger
273 p := newPrinter(l)
274 defer p.flush()
275
276 if hide := req.Context().Value(contextHide{}); hide != nil || p.checkFilter(req) {
277 return tripper.RoundTrip(req)
278 }
279
280 var tlsClientConfig *tls.Config
281
282 if l.Time {
283 defer p.printTimeRequest()()
284 }
285
286 if !l.SkipRequestInfo {
287 p.printRequestInfo(req)
288 }
289
290 if transport, ok := tripper.(*http.Transport); ok && transport.TLSClientConfig != nil {
291 tlsClientConfig = transport.TLSClientConfig
292
293 if tlsClientConfig.InsecureSkipVerify {
294 p.printf("* Skipping TLS verification: %s\n",
295 p.format(color.FgRed, "connection is susceptible to man-in-the-middle attacks."))
296 }
297 }
298
299 if l.TLS && tlsClientConfig != nil {
300
301 p.printOutgoingClientTLS(tlsClientConfig)
302 }
303
304 p.printRequest(req)
305
306 defer func() {
307 if err != nil {
308 p.printf("* %s\n", p.format(color.FgRed, err.Error()))
309
310 if resp == nil {
311 return
312 }
313 }
314
315 if l.TLS {
316 p.printTLSInfo(resp.TLS, false)
317 p.printTLSServer(req.Host, resp.TLS)
318 }
319
320 p.printResponse(resp)
321 }()
322
323 return tripper.RoundTrip(req)
324 }
325
326
327 func (l *Logger) Middleware(next http.Handler) http.Handler {
328 return httpHandler{
329 logger: l,
330 next: next,
331 }
332 }
333
334 type httpHandler struct {
335 logger *Logger
336 next http.Handler
337 }
338
339
340 func (h httpHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
341 l := h.logger
342 p := newPrinter(l)
343 defer p.flush()
344
345 if hide := req.Context().Value(contextHide{}); hide != nil || p.checkFilter(req) {
346 h.next.ServeHTTP(w, req)
347 return
348 }
349
350 if p.logger.Time {
351 defer p.printTimeRequest()()
352 }
353
354 if !p.logger.SkipRequestInfo {
355 p.printRequestInfo(req)
356 }
357
358 if p.logger.TLS {
359 p.printTLSInfo(req.TLS, true)
360 p.printIncomingClientTLS(req.TLS)
361 }
362
363 p.printRequest(req)
364
365 rec := &responseRecorder{
366 ResponseWriter: w,
367
368 statusCode: http.StatusOK,
369
370 maxReadableBody: l.MaxResponseBody,
371 buf: &bytes.Buffer{},
372 }
373
374 defer p.printServerResponse(req, rec)
375 h.next.ServeHTTP(rec, req)
376 }
377
378
379
380
381 func (l *Logger) PrintRequest(req *http.Request) {
382 var p = printer{logger: l}
383
384 if skip := p.checkFilter(req); skip {
385 return
386 }
387
388 p.printRequest(req)
389 }
390
391
392 func (l *Logger) PrintResponse(resp *http.Response) {
393 var p = printer{logger: l}
394 p.printResponse(resp)
395 }
396
397
398
399
400
401
402 type JSONFormatter struct{}
403
404
405 func (j *JSONFormatter) Match(mediatype string) bool {
406 return mediatype == "application/json"
407 }
408
409
410 func (j *JSONFormatter) Format(w io.Writer, src []byte) error {
411 if !json.Valid(src) {
412
413
414 if err := json.Unmarshal(src, &json.RawMessage{}); err != nil {
415 return err
416 }
417 }
418
419
420 dst, ok := w.(*bytes.Buffer)
421 if !ok {
422
423 return errors.New("underlying writer for JSONFormatter must be *bytes.Buffer")
424 }
425 return json.Indent(dst, src, "", " ")
426 }
427
View as plain text