...
1
2
3
4
5
6
7
8
9
10
11
12
13 package chttp
14
15 import (
16 "bytes"
17 "context"
18 "io"
19 "net/http"
20 )
21
22 var clientTraceContextKey = &struct{ name string }{"client trace"}
23
24
25
26 func ContextClientTrace(ctx context.Context) *ClientTrace {
27 trace, _ := ctx.Value(clientTraceContextKey).(*ClientTrace)
28 return trace
29 }
30
31
32
33
34
35 type ClientTrace struct {
36
37
38
39 HTTPResponse func(*http.Response)
40
41
42
43
44 HTTPResponseBody func(*http.Response)
45
46
47
48
49 HTTPRequest func(*http.Request)
50
51
52
53
54 HTTPRequestBody func(*http.Request)
55 }
56
57
58
59
60
61
62 func WithClientTrace(ctx context.Context, trace *ClientTrace) context.Context {
63 if trace == nil {
64 panic("nil trace")
65 }
66 return context.WithValue(ctx, clientTraceContextKey, trace)
67 }
68
69 func (t *ClientTrace) httpResponse(r *http.Response) {
70 if t.HTTPResponse == nil || r == nil {
71 return
72 }
73 clone := new(http.Response)
74 *clone = *r
75 clone.Body = nil
76 t.HTTPResponse(clone)
77 }
78
79 func (t *ClientTrace) httpResponseBody(r *http.Response) {
80 if t.HTTPResponseBody == nil || r == nil {
81 return
82 }
83 clone := new(http.Response)
84 *clone = *r
85 rBody := r.Body
86 body, readErr := io.ReadAll(rBody)
87 closeErr := rBody.Close()
88 r.Body = newReplay(body, readErr, closeErr)
89 clone.Body = newReplay(body, readErr, closeErr)
90 t.HTTPResponseBody(clone)
91 }
92
93 func (t *ClientTrace) httpRequest(r *http.Request) {
94 if t.HTTPRequest == nil {
95 return
96 }
97 clone := new(http.Request)
98 *clone = *r
99 clone.Body = nil
100 t.HTTPRequest(clone)
101 }
102
103 func (t *ClientTrace) httpRequestBody(r *http.Request) {
104 if t.HTTPRequestBody == nil {
105 return
106 }
107 clone := new(http.Request)
108 *clone = *r
109 if r.Body != nil {
110 rBody := r.Body
111 body, readErr := io.ReadAll(rBody)
112 closeErr := rBody.Close()
113 r.Body = newReplay(body, readErr, closeErr)
114 clone.Body = newReplay(body, readErr, closeErr)
115 }
116 t.HTTPRequestBody(clone)
117 }
118
119 func newReplay(body []byte, readErr, closeErr error) io.ReadCloser {
120 if readErr == nil && closeErr == nil {
121 return io.NopCloser(bytes.NewReader(body))
122 }
123 return &replayReadCloser{
124 Reader: io.NopCloser(bytes.NewReader(body)),
125 readErr: readErr,
126 closeErr: closeErr,
127 }
128 }
129
130
131 type replayReadCloser struct {
132 io.Reader
133 readErr error
134 closeErr error
135 }
136
137 func (r *replayReadCloser) Read(p []byte) (int, error) {
138 c, err := r.Reader.Read(p)
139 if err == io.EOF && r.readErr != nil {
140 err = r.readErr
141 }
142 return c, err
143 }
144
145 func (r *replayReadCloser) Close() error {
146 return r.closeErr
147 }
148
View as plain text