New upstream snapshot.
Debian Janitor
2 years ago
4 | 4 | - 1.14.x |
5 | 5 | - tip |
6 | 6 | |
7 | arch: | |
8 | - amd64 | |
9 | - ppc64le | |
10 | ||
7 | 11 | script: |
8 | 12 | - go test -v -cover -race ./... |
9 | 13 |
0 | golang-github-opentracing-contrib-go-stdlib (1.0.0+git20210423.1.00fa856-1) UNRELEASED; urgency=low | |
1 | ||
2 | * New upstream snapshot. | |
3 | ||
4 | -- Debian Janitor <janitor@jelmer.uk> Thu, 27 May 2021 07:56:47 -0000 | |
5 | ||
0 | 6 | golang-github-opentracing-contrib-go-stdlib (1.0.0-1) unstable; urgency=medium |
1 | 7 | |
2 | 8 | * Team upload. |
33 | 33 | type clientOptions struct { |
34 | 34 | operationName string |
35 | 35 | componentName string |
36 | urlTagFunc func(u *url.URL) string | |
36 | urlTagFunc func(u *url.URL) string | |
37 | 37 | disableClientTrace bool |
38 | 38 | disableInjectSpanContext bool |
39 | 39 | spanObserver func(span opentracing.Span, r *http.Request) |
148 | 148 | return err |
149 | 149 | } |
150 | 150 | |
151 | type writerCloseTracker struct { | |
152 | io.ReadWriteCloser | |
153 | sp opentracing.Span | |
154 | } | |
155 | ||
156 | func (c writerCloseTracker) Close() error { | |
157 | err := c.ReadWriteCloser.Close() | |
158 | c.sp.LogFields(log.String("event", "ClosedBody")) | |
159 | c.sp.Finish() | |
160 | return err | |
161 | } | |
162 | ||
151 | 163 | // TracerFromRequest retrieves the Tracer from the request. If the request does |
152 | 164 | // not have a Tracer it will return nil. |
153 | 165 | func TracerFromRequest(req *http.Request) *Tracer { |
173 | 185 | |
174 | 186 | ext.HTTPMethod.Set(tracer.sp, req.Method) |
175 | 187 | ext.HTTPUrl.Set(tracer.sp, tracer.opts.urlTagFunc(req.URL)) |
188 | ext.PeerAddress.Set(tracer.sp, req.URL.Host) | |
176 | 189 | tracer.opts.spanObserver(tracer.sp, req) |
177 | 190 | |
178 | 191 | if !tracer.opts.disableInjectSpanContext { |
193 | 206 | if req.Method == "HEAD" { |
194 | 207 | tracer.sp.Finish() |
195 | 208 | } else { |
196 | resp.Body = closeTracker{resp.Body, tracer.sp} | |
209 | readWriteCloser, ok := resp.Body.(io.ReadWriteCloser) | |
210 | if ok { | |
211 | resp.Body = writerCloseTracker{readWriteCloser, tracer.sp} | |
212 | } else { | |
213 | resp.Body = closeTracker{resp.Body, tracer.sp} | |
214 | } | |
197 | 215 | } |
198 | 216 | return resp, nil |
199 | 217 | } |
222 | 240 | } |
223 | 241 | |
224 | 242 | ctx := h.root.Context() |
225 | h.sp = h.tr.StartSpan("HTTP "+req.Method, opentracing.ChildOf(ctx)) | |
226 | ext.SpanKindRPCClient.Set(h.sp) | |
243 | h.sp = h.tr.StartSpan("HTTP "+req.Method, opentracing.ChildOf(ctx), ext.SpanKindRPCClient) | |
227 | 244 | |
228 | 245 | componentName := h.opts.componentName |
229 | 246 | if componentName == "" { |
265 | 282 | } |
266 | 283 | |
267 | 284 | func (h *Tracer) getConn(hostPort string) { |
268 | ext.HTTPUrl.Set(h.sp, hostPort) | |
269 | h.sp.LogFields(log.String("event", "GetConn")) | |
285 | h.sp.LogFields(log.String("event", "GetConn"), log.String("hostPort", hostPort)) | |
270 | 286 | } |
271 | 287 | |
272 | 288 | func (h *Tracer) gotConn(info httptrace.GotConnInfo) { |
0 | 0 | package nethttp |
1 | 1 | |
2 | 2 | import ( |
3 | "bytes" | |
4 | "fmt" | |
5 | "io" | |
3 | 6 | "net/http" |
4 | 7 | "net/http/httptest" |
5 | 8 | "net/url" |
134 | 137 | } |
135 | 138 | } |
136 | 139 | |
140 | func TestWriteCloserFromRequest(t *testing.T) { | |
141 | wait := make(chan bool, 0) | |
142 | srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
143 | defer func() { | |
144 | wait <- true | |
145 | }() | |
146 | ||
147 | w.Header().Set("Upgrade", "websocket") | |
148 | w.Header().Set("Connection", "Upgrade") | |
149 | w.WriteHeader(http.StatusSwitchingProtocols) | |
150 | ||
151 | hijacker := w.(http.Hijacker) | |
152 | _, rw, err := hijacker.Hijack() | |
153 | ||
154 | if err != nil { | |
155 | t.Fatal("Failed to hijack connection") | |
156 | } | |
157 | ||
158 | line, _, err := rw.ReadLine() | |
159 | if string(line) != "ping" { | |
160 | t.Fatalf("Expected 'ping' received %q", string(line)) | |
161 | } | |
162 | ||
163 | if err != nil { | |
164 | t.Fatal(err) | |
165 | } | |
166 | })) | |
167 | ||
168 | var buf bytes.Buffer | |
169 | req, err := http.NewRequest("POST", srv.URL, &buf) | |
170 | req.Header.Set("Connection", "upgrade") | |
171 | req.Header.Set("Upgrade", "websocket") | |
172 | req.Proto = "HTTP/1.1" | |
173 | req.ProtoMajor = 1 | |
174 | req.ProtoMinor = 1 | |
175 | if err != nil { | |
176 | t.Fatal(err) | |
177 | } | |
178 | ||
179 | tr := &mocktracer.MockTracer{} | |
180 | req, _ = TraceRequest(tr, req) | |
181 | ||
182 | client := &http.Client{Transport: &Transport{}} | |
183 | resp, err := client.Do(req) | |
184 | if err != nil { | |
185 | t.Fatal(err) | |
186 | } | |
187 | ||
188 | rw, ok := resp.Body.(io.ReadWriteCloser) | |
189 | if !ok { | |
190 | t.Fatal("resp.Body is not a io.ReadWriteCloser") | |
191 | } | |
192 | ||
193 | fmt.Fprint(rw, "ping\n") | |
194 | <-wait | |
195 | rw.Close() | |
196 | } | |
197 | ||
137 | 198 | func TestInjectSpanContext(t *testing.T) { |
138 | 199 | tests := []struct { |
139 | 200 | name string |
226 | 287 | tag string |
227 | 288 | }{ |
228 | 289 | // These first cases fail early |
229 | {[]ClientOption{}, "/ok?token=a", srv.Listener.Addr().String()}, | |
230 | {[]ClientOption{URLTagFunc(fn)}, "/ok?token=c", srv.Listener.Addr().String()}, | |
290 | {[]ClientOption{}, "/ok?token=a", srv.URL + "/ok?token=a"}, | |
291 | {[]ClientOption{URLTagFunc(fn)}, "/ok?token=c", srv.URL + "/ok?token=*"}, | |
231 | 292 | // Disable ClientTrace to fire RoundTrip |
232 | 293 | {[]ClientOption{ClientTrace(false)}, "/ok?token=b", srv.URL + "/ok?token=b"}, |
233 | 294 | {[]ClientOption{ClientTrace(false), URLTagFunc(fn)}, "/ok?token=c", srv.URL + "/ok?token=*"}, |
250 | 311 | if got, want := tag, tt.tag; got != want { |
251 | 312 | t.Fatalf("got %s tag name, expected %s", got, want) |
252 | 313 | } |
253 | } | |
254 | }⏎ | |
314 | peerAddress, ok := clientSpan.Tags()["peer.address"] | |
315 | if !ok { | |
316 | t.Fatal("cannot find peer.address tag") | |
317 | } | |
318 | if peerAddress != srv.Listener.Addr().String() { | |
319 | t.Fatalf("got %s want %s in peer.address tag", peerAddress, srv.Listener.Addr().String()) | |
320 | } | |
321 | } | |
322 | } |
0 | // +build go1.8 | |
1 | ||
2 | package nethttp | |
3 | ||
4 | import ( | |
5 | "io" | |
6 | "net/http" | |
7 | ) | |
8 | ||
9 | type metricsTracker struct { | |
10 | http.ResponseWriter | |
11 | status int | |
12 | size int | |
13 | } | |
14 | ||
15 | func (w *metricsTracker) WriteHeader(status int) { | |
16 | w.status = status | |
17 | w.ResponseWriter.WriteHeader(status) | |
18 | } | |
19 | ||
20 | func (w *metricsTracker) Write(b []byte) (int, error) { | |
21 | size, err := w.ResponseWriter.Write(b) | |
22 | w.size += size | |
23 | return size, err | |
24 | } | |
25 | ||
26 | // wrappedResponseWriter returns a wrapped version of the original | |
27 | // ResponseWriter and only implements the same combination of additional | |
28 | // interfaces as the original. This implementation is based on | |
29 | // https://github.com/felixge/httpsnoop. | |
30 | func (w *metricsTracker) wrappedResponseWriter() http.ResponseWriter { | |
31 | var ( | |
32 | hj, i0 = w.ResponseWriter.(http.Hijacker) | |
33 | cn, i1 = w.ResponseWriter.(http.CloseNotifier) | |
34 | pu, i2 = w.ResponseWriter.(http.Pusher) | |
35 | fl, i3 = w.ResponseWriter.(http.Flusher) | |
36 | rf, i4 = w.ResponseWriter.(io.ReaderFrom) | |
37 | ) | |
38 | ||
39 | switch { | |
40 | case !i0 && !i1 && !i2 && !i3 && !i4: | |
41 | return struct { | |
42 | http.ResponseWriter | |
43 | }{w} | |
44 | case !i0 && !i1 && !i2 && !i3 && i4: | |
45 | return struct { | |
46 | http.ResponseWriter | |
47 | io.ReaderFrom | |
48 | }{w, rf} | |
49 | case !i0 && !i1 && !i2 && i3 && !i4: | |
50 | return struct { | |
51 | http.ResponseWriter | |
52 | http.Flusher | |
53 | }{w, fl} | |
54 | case !i0 && !i1 && !i2 && i3 && i4: | |
55 | return struct { | |
56 | http.ResponseWriter | |
57 | http.Flusher | |
58 | io.ReaderFrom | |
59 | }{w, fl, rf} | |
60 | case !i0 && !i1 && i2 && !i3 && !i4: | |
61 | return struct { | |
62 | http.ResponseWriter | |
63 | http.Pusher | |
64 | }{w, pu} | |
65 | case !i0 && !i1 && i2 && !i3 && i4: | |
66 | return struct { | |
67 | http.ResponseWriter | |
68 | http.Pusher | |
69 | io.ReaderFrom | |
70 | }{w, pu, rf} | |
71 | case !i0 && !i1 && i2 && i3 && !i4: | |
72 | return struct { | |
73 | http.ResponseWriter | |
74 | http.Pusher | |
75 | http.Flusher | |
76 | }{w, pu, fl} | |
77 | case !i0 && !i1 && i2 && i3 && i4: | |
78 | return struct { | |
79 | http.ResponseWriter | |
80 | http.Pusher | |
81 | http.Flusher | |
82 | io.ReaderFrom | |
83 | }{w, pu, fl, rf} | |
84 | case !i0 && i1 && !i2 && !i3 && !i4: | |
85 | return struct { | |
86 | http.ResponseWriter | |
87 | http.CloseNotifier | |
88 | }{w, cn} | |
89 | case !i0 && i1 && !i2 && !i3 && i4: | |
90 | return struct { | |
91 | http.ResponseWriter | |
92 | http.CloseNotifier | |
93 | io.ReaderFrom | |
94 | }{w, cn, rf} | |
95 | case !i0 && i1 && !i2 && i3 && !i4: | |
96 | return struct { | |
97 | http.ResponseWriter | |
98 | http.CloseNotifier | |
99 | http.Flusher | |
100 | }{w, cn, fl} | |
101 | case !i0 && i1 && !i2 && i3 && i4: | |
102 | return struct { | |
103 | http.ResponseWriter | |
104 | http.CloseNotifier | |
105 | http.Flusher | |
106 | io.ReaderFrom | |
107 | }{w, cn, fl, rf} | |
108 | case !i0 && i1 && i2 && !i3 && !i4: | |
109 | return struct { | |
110 | http.ResponseWriter | |
111 | http.CloseNotifier | |
112 | http.Pusher | |
113 | }{w, cn, pu} | |
114 | case !i0 && i1 && i2 && !i3 && i4: | |
115 | return struct { | |
116 | http.ResponseWriter | |
117 | http.CloseNotifier | |
118 | http.Pusher | |
119 | io.ReaderFrom | |
120 | }{w, cn, pu, rf} | |
121 | case !i0 && i1 && i2 && i3 && !i4: | |
122 | return struct { | |
123 | http.ResponseWriter | |
124 | http.CloseNotifier | |
125 | http.Pusher | |
126 | http.Flusher | |
127 | }{w, cn, pu, fl} | |
128 | case !i0 && i1 && i2 && i3 && i4: | |
129 | return struct { | |
130 | http.ResponseWriter | |
131 | http.CloseNotifier | |
132 | http.Pusher | |
133 | http.Flusher | |
134 | io.ReaderFrom | |
135 | }{w, cn, pu, fl, rf} | |
136 | case i0 && !i1 && !i2 && !i3 && !i4: | |
137 | return struct { | |
138 | http.ResponseWriter | |
139 | http.Hijacker | |
140 | }{w, hj} | |
141 | case i0 && !i1 && !i2 && !i3 && i4: | |
142 | return struct { | |
143 | http.ResponseWriter | |
144 | http.Hijacker | |
145 | io.ReaderFrom | |
146 | }{w, hj, rf} | |
147 | case i0 && !i1 && !i2 && i3 && !i4: | |
148 | return struct { | |
149 | http.ResponseWriter | |
150 | http.Hijacker | |
151 | http.Flusher | |
152 | }{w, hj, fl} | |
153 | case i0 && !i1 && !i2 && i3 && i4: | |
154 | return struct { | |
155 | http.ResponseWriter | |
156 | http.Hijacker | |
157 | http.Flusher | |
158 | io.ReaderFrom | |
159 | }{w, hj, fl, rf} | |
160 | case i0 && !i1 && i2 && !i3 && !i4: | |
161 | return struct { | |
162 | http.ResponseWriter | |
163 | http.Hijacker | |
164 | http.Pusher | |
165 | }{w, hj, pu} | |
166 | case i0 && !i1 && i2 && !i3 && i4: | |
167 | return struct { | |
168 | http.ResponseWriter | |
169 | http.Hijacker | |
170 | http.Pusher | |
171 | io.ReaderFrom | |
172 | }{w, hj, pu, rf} | |
173 | case i0 && !i1 && i2 && i3 && !i4: | |
174 | return struct { | |
175 | http.ResponseWriter | |
176 | http.Hijacker | |
177 | http.Pusher | |
178 | http.Flusher | |
179 | }{w, hj, pu, fl} | |
180 | case i0 && !i1 && i2 && i3 && i4: | |
181 | return struct { | |
182 | http.ResponseWriter | |
183 | http.Hijacker | |
184 | http.Pusher | |
185 | http.Flusher | |
186 | io.ReaderFrom | |
187 | }{w, hj, pu, fl, rf} | |
188 | case i0 && i1 && !i2 && !i3 && !i4: | |
189 | return struct { | |
190 | http.ResponseWriter | |
191 | http.Hijacker | |
192 | http.CloseNotifier | |
193 | }{w, hj, cn} | |
194 | case i0 && i1 && !i2 && !i3 && i4: | |
195 | return struct { | |
196 | http.ResponseWriter | |
197 | http.Hijacker | |
198 | http.CloseNotifier | |
199 | io.ReaderFrom | |
200 | }{w, hj, cn, rf} | |
201 | case i0 && i1 && !i2 && i3 && !i4: | |
202 | return struct { | |
203 | http.ResponseWriter | |
204 | http.Hijacker | |
205 | http.CloseNotifier | |
206 | http.Flusher | |
207 | }{w, hj, cn, fl} | |
208 | case i0 && i1 && !i2 && i3 && i4: | |
209 | return struct { | |
210 | http.ResponseWriter | |
211 | http.Hijacker | |
212 | http.CloseNotifier | |
213 | http.Flusher | |
214 | io.ReaderFrom | |
215 | }{w, hj, cn, fl, rf} | |
216 | case i0 && i1 && i2 && !i3 && !i4: | |
217 | return struct { | |
218 | http.ResponseWriter | |
219 | http.Hijacker | |
220 | http.CloseNotifier | |
221 | http.Pusher | |
222 | }{w, hj, cn, pu} | |
223 | case i0 && i1 && i2 && !i3 && i4: | |
224 | return struct { | |
225 | http.ResponseWriter | |
226 | http.Hijacker | |
227 | http.CloseNotifier | |
228 | http.Pusher | |
229 | io.ReaderFrom | |
230 | }{w, hj, cn, pu, rf} | |
231 | case i0 && i1 && i2 && i3 && !i4: | |
232 | return struct { | |
233 | http.ResponseWriter | |
234 | http.Hijacker | |
235 | http.CloseNotifier | |
236 | http.Pusher | |
237 | http.Flusher | |
238 | }{w, hj, cn, pu, fl} | |
239 | case i0 && i1 && i2 && i3 && i4: | |
240 | return struct { | |
241 | http.ResponseWriter | |
242 | http.Hijacker | |
243 | http.CloseNotifier | |
244 | http.Pusher | |
245 | http.Flusher | |
246 | io.ReaderFrom | |
247 | }{w, hj, cn, pu, fl, rf} | |
248 | default: | |
249 | return struct { | |
250 | http.ResponseWriter | |
251 | }{w} | |
252 | } | |
253 | } |
8 | 8 | opentracing "github.com/opentracing/opentracing-go" |
9 | 9 | "github.com/opentracing/opentracing-go/ext" |
10 | 10 | ) |
11 | ||
12 | var responseSizeKey = "http.response_size" | |
11 | 13 | |
12 | 14 | type mwOptions struct { |
13 | 15 | opNameFunc func(r *http.Request) string |
125 | 127 | ext.Component.Set(sp, componentName) |
126 | 128 | opts.spanObserver(sp, r) |
127 | 129 | |
128 | sct := &statusCodeTracker{ResponseWriter: w} | |
130 | mt := &metricsTracker{ResponseWriter: w} | |
129 | 131 | r = r.WithContext(opentracing.ContextWithSpan(r.Context(), sp)) |
130 | 132 | |
131 | 133 | defer func() { |
132 | 134 | panicErr := recover() |
133 | 135 | didPanic := panicErr != nil |
134 | 136 | |
135 | if sct.status == 0 && !didPanic { | |
137 | if mt.status == 0 && !didPanic { | |
136 | 138 | // Standard behavior of http.Server is to assume status code 200 if one was not written by a handler that returned successfully. |
137 | 139 | // https://github.com/golang/go/blob/fca286bed3ed0e12336532cc711875ae5b3cb02a/src/net/http/server.go#L120 |
138 | sct.status = 200 | |
140 | mt.status = 200 | |
139 | 141 | } |
140 | if sct.status > 0 { | |
141 | ext.HTTPStatusCode.Set(sp, uint16(sct.status)) | |
142 | if mt.status > 0 { | |
143 | ext.HTTPStatusCode.Set(sp, uint16(mt.status)) | |
142 | 144 | } |
143 | if sct.status >= http.StatusInternalServerError || didPanic { | |
145 | if mt.size > 0 { | |
146 | sp.SetTag(responseSizeKey, mt.size) | |
147 | } | |
148 | if mt.status >= http.StatusInternalServerError || didPanic { | |
144 | 149 | ext.Error.Set(sp, true) |
145 | 150 | } |
146 | 151 | sp.Finish() |
150 | 155 | } |
151 | 156 | }() |
152 | 157 | |
153 | h(sct.wrappedResponseWriter(), r) | |
158 | h(mt.wrappedResponseWriter(), r) | |
154 | 159 | } |
155 | 160 | return http.HandlerFunc(fn) |
156 | 161 | } |
98 | 98 | t.Fatalf("got %s operation name, expected %s", got, want) |
99 | 99 | } |
100 | 100 | |
101 | defaultLength := 5 | |
101 | defaultLength := 6 | |
102 | 102 | if len(spans[0].Tags()) != len(testCase.Tags)+defaultLength { |
103 | 103 | t.Fatalf("got tag length %d, expected %d", len(spans[0].Tags()), len(testCase.Tags)) |
104 | 104 | } |
265 | 265 | } |
266 | 266 | } |
267 | 267 | |
268 | func TestSpanResponseSize(t *testing.T) { | |
269 | mux := http.NewServeMux() | |
270 | mux.HandleFunc("/with-body", func(w http.ResponseWriter, r *http.Request) { | |
271 | w.WriteHeader(200) | |
272 | w.Write([]byte("12345")) | |
273 | }) | |
274 | mux.HandleFunc("/no-body", func(w http.ResponseWriter, r *http.Request) { | |
275 | w.WriteHeader(200) | |
276 | }) | |
277 | ||
278 | ||
279 | expBodySize := map[string]interface{}{"http.response_size": 5} | |
280 | ||
281 | tests := []struct { | |
282 | url string | |
283 | tags map[string]interface{} | |
284 | }{ | |
285 | {url: "/with-body", tags: expBodySize}, | |
286 | {url: "/no-body", tags: map[string]interface{}{}}, | |
287 | } | |
288 | ||
289 | for _, tt := range tests { | |
290 | testCase := tt | |
291 | t.Run(testCase.url, func(t *testing.T) { | |
292 | tr := &mocktracer.MockTracer{} | |
293 | mw := Middleware(tr, mux) | |
294 | srv := httptest.NewServer(mw) | |
295 | defer srv.Close() | |
296 | ||
297 | _, err := http.Get(srv.URL + testCase.url) | |
298 | if err != nil { | |
299 | t.Fatalf("server returned error: %v", err) | |
300 | } | |
301 | ||
302 | spans := tr.FinishedSpans() | |
303 | if got, want := len(spans), 1; got != want { | |
304 | t.Fatalf("got %d spans, expected %d", got, want) | |
305 | } | |
306 | ||
307 | for k, v := range testCase.tags { | |
308 | if tag := spans[0].Tag(k); !reflect.DeepEqual(tag, v) { | |
309 | t.Fatalf("tag %s: got %v, expected %v", k, tag, v) | |
310 | } | |
311 | } | |
312 | }) | |
313 | } | |
314 | } | |
315 | ||
268 | 316 | func BenchmarkStatusCodeTrackingOverhead(b *testing.B) { |
269 | 317 | mux := http.NewServeMux() |
270 | 318 | mux.HandleFunc("/root", func(w http.ResponseWriter, r *http.Request) {}) |
319 | tr := &mocktracer.MockTracer{} | |
320 | mw := Middleware(tr, mux) | |
321 | srv := httptest.NewServer(mw) | |
322 | defer srv.Close() | |
323 | ||
324 | b.RunParallel(func(pb *testing.PB) { | |
325 | for pb.Next() { | |
326 | resp, err := http.Get(srv.URL) | |
327 | if err != nil { | |
328 | b.Fatalf("server returned error: %v", err) | |
329 | } | |
330 | err = resp.Body.Close() | |
331 | if err != nil { | |
332 | b.Fatalf("failed to close response: %v", err) | |
333 | } | |
334 | } | |
335 | }) | |
336 | } | |
337 | ||
338 | func BenchmarkResponseSizeTrackingOverhead(b *testing.B) { | |
339 | mux := http.NewServeMux() | |
340 | mux.HandleFunc("/root", func(w http.ResponseWriter, r *http.Request) { | |
341 | w.WriteHeader(200) | |
342 | w.Write([]byte("12345")) | |
343 | }) | |
271 | 344 | tr := &mocktracer.MockTracer{} |
272 | 345 | mw := Middleware(tr, mux) |
273 | 346 | srv := httptest.NewServer(mw) |
0 | // +build go1.8 | |
1 | ||
2 | package nethttp | |
3 | ||
4 | import ( | |
5 | "io" | |
6 | "net/http" | |
7 | ) | |
8 | ||
9 | type statusCodeTracker struct { | |
10 | http.ResponseWriter | |
11 | status int | |
12 | } | |
13 | ||
14 | func (w *statusCodeTracker) WriteHeader(status int) { | |
15 | w.status = status | |
16 | w.ResponseWriter.WriteHeader(status) | |
17 | } | |
18 | ||
19 | func (w *statusCodeTracker) Write(b []byte) (int, error) { | |
20 | return w.ResponseWriter.Write(b) | |
21 | } | |
22 | ||
23 | // wrappedResponseWriter returns a wrapped version of the original | |
24 | // ResponseWriter and only implements the same combination of additional | |
25 | // interfaces as the original. This implementation is based on | |
26 | // https://github.com/felixge/httpsnoop. | |
27 | func (w *statusCodeTracker) wrappedResponseWriter() http.ResponseWriter { | |
28 | var ( | |
29 | hj, i0 = w.ResponseWriter.(http.Hijacker) | |
30 | cn, i1 = w.ResponseWriter.(http.CloseNotifier) | |
31 | pu, i2 = w.ResponseWriter.(http.Pusher) | |
32 | fl, i3 = w.ResponseWriter.(http.Flusher) | |
33 | rf, i4 = w.ResponseWriter.(io.ReaderFrom) | |
34 | ) | |
35 | ||
36 | switch { | |
37 | case !i0 && !i1 && !i2 && !i3 && !i4: | |
38 | return struct { | |
39 | http.ResponseWriter | |
40 | }{w} | |
41 | case !i0 && !i1 && !i2 && !i3 && i4: | |
42 | return struct { | |
43 | http.ResponseWriter | |
44 | io.ReaderFrom | |
45 | }{w, rf} | |
46 | case !i0 && !i1 && !i2 && i3 && !i4: | |
47 | return struct { | |
48 | http.ResponseWriter | |
49 | http.Flusher | |
50 | }{w, fl} | |
51 | case !i0 && !i1 && !i2 && i3 && i4: | |
52 | return struct { | |
53 | http.ResponseWriter | |
54 | http.Flusher | |
55 | io.ReaderFrom | |
56 | }{w, fl, rf} | |
57 | case !i0 && !i1 && i2 && !i3 && !i4: | |
58 | return struct { | |
59 | http.ResponseWriter | |
60 | http.Pusher | |
61 | }{w, pu} | |
62 | case !i0 && !i1 && i2 && !i3 && i4: | |
63 | return struct { | |
64 | http.ResponseWriter | |
65 | http.Pusher | |
66 | io.ReaderFrom | |
67 | }{w, pu, rf} | |
68 | case !i0 && !i1 && i2 && i3 && !i4: | |
69 | return struct { | |
70 | http.ResponseWriter | |
71 | http.Pusher | |
72 | http.Flusher | |
73 | }{w, pu, fl} | |
74 | case !i0 && !i1 && i2 && i3 && i4: | |
75 | return struct { | |
76 | http.ResponseWriter | |
77 | http.Pusher | |
78 | http.Flusher | |
79 | io.ReaderFrom | |
80 | }{w, pu, fl, rf} | |
81 | case !i0 && i1 && !i2 && !i3 && !i4: | |
82 | return struct { | |
83 | http.ResponseWriter | |
84 | http.CloseNotifier | |
85 | }{w, cn} | |
86 | case !i0 && i1 && !i2 && !i3 && i4: | |
87 | return struct { | |
88 | http.ResponseWriter | |
89 | http.CloseNotifier | |
90 | io.ReaderFrom | |
91 | }{w, cn, rf} | |
92 | case !i0 && i1 && !i2 && i3 && !i4: | |
93 | return struct { | |
94 | http.ResponseWriter | |
95 | http.CloseNotifier | |
96 | http.Flusher | |
97 | }{w, cn, fl} | |
98 | case !i0 && i1 && !i2 && i3 && i4: | |
99 | return struct { | |
100 | http.ResponseWriter | |
101 | http.CloseNotifier | |
102 | http.Flusher | |
103 | io.ReaderFrom | |
104 | }{w, cn, fl, rf} | |
105 | case !i0 && i1 && i2 && !i3 && !i4: | |
106 | return struct { | |
107 | http.ResponseWriter | |
108 | http.CloseNotifier | |
109 | http.Pusher | |
110 | }{w, cn, pu} | |
111 | case !i0 && i1 && i2 && !i3 && i4: | |
112 | return struct { | |
113 | http.ResponseWriter | |
114 | http.CloseNotifier | |
115 | http.Pusher | |
116 | io.ReaderFrom | |
117 | }{w, cn, pu, rf} | |
118 | case !i0 && i1 && i2 && i3 && !i4: | |
119 | return struct { | |
120 | http.ResponseWriter | |
121 | http.CloseNotifier | |
122 | http.Pusher | |
123 | http.Flusher | |
124 | }{w, cn, pu, fl} | |
125 | case !i0 && i1 && i2 && i3 && i4: | |
126 | return struct { | |
127 | http.ResponseWriter | |
128 | http.CloseNotifier | |
129 | http.Pusher | |
130 | http.Flusher | |
131 | io.ReaderFrom | |
132 | }{w, cn, pu, fl, rf} | |
133 | case i0 && !i1 && !i2 && !i3 && !i4: | |
134 | return struct { | |
135 | http.ResponseWriter | |
136 | http.Hijacker | |
137 | }{w, hj} | |
138 | case i0 && !i1 && !i2 && !i3 && i4: | |
139 | return struct { | |
140 | http.ResponseWriter | |
141 | http.Hijacker | |
142 | io.ReaderFrom | |
143 | }{w, hj, rf} | |
144 | case i0 && !i1 && !i2 && i3 && !i4: | |
145 | return struct { | |
146 | http.ResponseWriter | |
147 | http.Hijacker | |
148 | http.Flusher | |
149 | }{w, hj, fl} | |
150 | case i0 && !i1 && !i2 && i3 && i4: | |
151 | return struct { | |
152 | http.ResponseWriter | |
153 | http.Hijacker | |
154 | http.Flusher | |
155 | io.ReaderFrom | |
156 | }{w, hj, fl, rf} | |
157 | case i0 && !i1 && i2 && !i3 && !i4: | |
158 | return struct { | |
159 | http.ResponseWriter | |
160 | http.Hijacker | |
161 | http.Pusher | |
162 | }{w, hj, pu} | |
163 | case i0 && !i1 && i2 && !i3 && i4: | |
164 | return struct { | |
165 | http.ResponseWriter | |
166 | http.Hijacker | |
167 | http.Pusher | |
168 | io.ReaderFrom | |
169 | }{w, hj, pu, rf} | |
170 | case i0 && !i1 && i2 && i3 && !i4: | |
171 | return struct { | |
172 | http.ResponseWriter | |
173 | http.Hijacker | |
174 | http.Pusher | |
175 | http.Flusher | |
176 | }{w, hj, pu, fl} | |
177 | case i0 && !i1 && i2 && i3 && i4: | |
178 | return struct { | |
179 | http.ResponseWriter | |
180 | http.Hijacker | |
181 | http.Pusher | |
182 | http.Flusher | |
183 | io.ReaderFrom | |
184 | }{w, hj, pu, fl, rf} | |
185 | case i0 && i1 && !i2 && !i3 && !i4: | |
186 | return struct { | |
187 | http.ResponseWriter | |
188 | http.Hijacker | |
189 | http.CloseNotifier | |
190 | }{w, hj, cn} | |
191 | case i0 && i1 && !i2 && !i3 && i4: | |
192 | return struct { | |
193 | http.ResponseWriter | |
194 | http.Hijacker | |
195 | http.CloseNotifier | |
196 | io.ReaderFrom | |
197 | }{w, hj, cn, rf} | |
198 | case i0 && i1 && !i2 && i3 && !i4: | |
199 | return struct { | |
200 | http.ResponseWriter | |
201 | http.Hijacker | |
202 | http.CloseNotifier | |
203 | http.Flusher | |
204 | }{w, hj, cn, fl} | |
205 | case i0 && i1 && !i2 && i3 && i4: | |
206 | return struct { | |
207 | http.ResponseWriter | |
208 | http.Hijacker | |
209 | http.CloseNotifier | |
210 | http.Flusher | |
211 | io.ReaderFrom | |
212 | }{w, hj, cn, fl, rf} | |
213 | case i0 && i1 && i2 && !i3 && !i4: | |
214 | return struct { | |
215 | http.ResponseWriter | |
216 | http.Hijacker | |
217 | http.CloseNotifier | |
218 | http.Pusher | |
219 | }{w, hj, cn, pu} | |
220 | case i0 && i1 && i2 && !i3 && i4: | |
221 | return struct { | |
222 | http.ResponseWriter | |
223 | http.Hijacker | |
224 | http.CloseNotifier | |
225 | http.Pusher | |
226 | io.ReaderFrom | |
227 | }{w, hj, cn, pu, rf} | |
228 | case i0 && i1 && i2 && i3 && !i4: | |
229 | return struct { | |
230 | http.ResponseWriter | |
231 | http.Hijacker | |
232 | http.CloseNotifier | |
233 | http.Pusher | |
234 | http.Flusher | |
235 | }{w, hj, cn, pu, fl} | |
236 | case i0 && i1 && i2 && i3 && i4: | |
237 | return struct { | |
238 | http.ResponseWriter | |
239 | http.Hijacker | |
240 | http.CloseNotifier | |
241 | http.Pusher | |
242 | http.Flusher | |
243 | io.ReaderFrom | |
244 | }{w, hj, cn, pu, fl, rf} | |
245 | default: | |
246 | return struct { | |
247 | http.ResponseWriter | |
248 | }{w} | |
249 | } | |
250 | } |