]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/go.opencensus.io/plugin/ochttp/server.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / go.opencensus.io / plugin / ochttp / server.go
1 // Copyright 2018, OpenCensus Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package ochttp
16
17 import (
18 "context"
19 "io"
20 "net/http"
21 "strconv"
22 "sync"
23 "time"
24
25 "go.opencensus.io/stats"
26 "go.opencensus.io/tag"
27 "go.opencensus.io/trace"
28 "go.opencensus.io/trace/propagation"
29 )
30
31 // Handler is an http.Handler wrapper to instrument your HTTP server with
32 // OpenCensus. It supports both stats and tracing.
33 //
34 // Tracing
35 //
36 // This handler is aware of the incoming request's span, reading it from request
37 // headers as configured using the Propagation field.
38 // The extracted span can be accessed from the incoming request's
39 // context.
40 //
41 // span := trace.FromContext(r.Context())
42 //
43 // The server span will be automatically ended at the end of ServeHTTP.
44 type Handler struct {
45 // Propagation defines how traces are propagated. If unspecified,
46 // B3 propagation will be used.
47 Propagation propagation.HTTPFormat
48
49 // Handler is the handler used to handle the incoming request.
50 Handler http.Handler
51
52 // StartOptions are applied to the span started by this Handler around each
53 // request.
54 //
55 // StartOptions.SpanKind will always be set to trace.SpanKindServer
56 // for spans started by this transport.
57 StartOptions trace.StartOptions
58
59 // GetStartOptions allows to set start options per request. If set,
60 // StartOptions is going to be ignored.
61 GetStartOptions func(*http.Request) trace.StartOptions
62
63 // IsPublicEndpoint should be set to true for publicly accessible HTTP(S)
64 // servers. If true, any trace metadata set on the incoming request will
65 // be added as a linked trace instead of being added as a parent of the
66 // current trace.
67 IsPublicEndpoint bool
68
69 // FormatSpanName holds the function to use for generating the span name
70 // from the information found in the incoming HTTP Request. By default the
71 // name equals the URL Path.
72 FormatSpanName func(*http.Request) string
73 }
74
75 func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
76 var tags addedTags
77 r, traceEnd := h.startTrace(w, r)
78 defer traceEnd()
79 w, statsEnd := h.startStats(w, r)
80 defer statsEnd(&tags)
81 handler := h.Handler
82 if handler == nil {
83 handler = http.DefaultServeMux
84 }
85 r = r.WithContext(context.WithValue(r.Context(), addedTagsKey{}, &tags))
86 handler.ServeHTTP(w, r)
87 }
88
89 func (h *Handler) startTrace(w http.ResponseWriter, r *http.Request) (*http.Request, func()) {
90 if isHealthEndpoint(r.URL.Path) {
91 return r, func() {}
92 }
93 var name string
94 if h.FormatSpanName == nil {
95 name = spanNameFromURL(r)
96 } else {
97 name = h.FormatSpanName(r)
98 }
99 ctx := r.Context()
100
101 startOpts := h.StartOptions
102 if h.GetStartOptions != nil {
103 startOpts = h.GetStartOptions(r)
104 }
105
106 var span *trace.Span
107 sc, ok := h.extractSpanContext(r)
108 if ok && !h.IsPublicEndpoint {
109 ctx, span = trace.StartSpanWithRemoteParent(ctx, name, sc,
110 trace.WithSampler(startOpts.Sampler),
111 trace.WithSpanKind(trace.SpanKindServer))
112 } else {
113 ctx, span = trace.StartSpan(ctx, name,
114 trace.WithSampler(startOpts.Sampler),
115 trace.WithSpanKind(trace.SpanKindServer),
116 )
117 if ok {
118 span.AddLink(trace.Link{
119 TraceID: sc.TraceID,
120 SpanID: sc.SpanID,
121 Type: trace.LinkTypeChild,
122 Attributes: nil,
123 })
124 }
125 }
126 span.AddAttributes(requestAttrs(r)...)
127 return r.WithContext(ctx), span.End
128 }
129
130 func (h *Handler) extractSpanContext(r *http.Request) (trace.SpanContext, bool) {
131 if h.Propagation == nil {
132 return defaultFormat.SpanContextFromRequest(r)
133 }
134 return h.Propagation.SpanContextFromRequest(r)
135 }
136
137 func (h *Handler) startStats(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, func(tags *addedTags)) {
138 ctx, _ := tag.New(r.Context(),
139 tag.Upsert(Host, r.URL.Host),
140 tag.Upsert(Path, r.URL.Path),
141 tag.Upsert(Method, r.Method))
142 track := &trackingResponseWriter{
143 start: time.Now(),
144 ctx: ctx,
145 writer: w,
146 }
147 if r.Body == nil {
148 // TODO: Handle cases where ContentLength is not set.
149 track.reqSize = -1
150 } else if r.ContentLength > 0 {
151 track.reqSize = r.ContentLength
152 }
153 stats.Record(ctx, ServerRequestCount.M(1))
154 return track.wrappedResponseWriter(), track.end
155 }
156
157 type trackingResponseWriter struct {
158 ctx context.Context
159 reqSize int64
160 respSize int64
161 start time.Time
162 statusCode int
163 statusLine string
164 endOnce sync.Once
165 writer http.ResponseWriter
166 }
167
168 // Compile time assertion for ResponseWriter interface
169 var _ http.ResponseWriter = (*trackingResponseWriter)(nil)
170
171 var logTagsErrorOnce sync.Once
172
173 func (t *trackingResponseWriter) end(tags *addedTags) {
174 t.endOnce.Do(func() {
175 if t.statusCode == 0 {
176 t.statusCode = 200
177 }
178
179 span := trace.FromContext(t.ctx)
180 span.SetStatus(TraceStatus(t.statusCode, t.statusLine))
181 span.AddAttributes(trace.Int64Attribute(StatusCodeAttribute, int64(t.statusCode)))
182
183 m := []stats.Measurement{
184 ServerLatency.M(float64(time.Since(t.start)) / float64(time.Millisecond)),
185 ServerResponseBytes.M(t.respSize),
186 }
187 if t.reqSize >= 0 {
188 m = append(m, ServerRequestBytes.M(t.reqSize))
189 }
190 allTags := make([]tag.Mutator, len(tags.t)+1)
191 allTags[0] = tag.Upsert(StatusCode, strconv.Itoa(t.statusCode))
192 copy(allTags[1:], tags.t)
193 stats.RecordWithTags(t.ctx, allTags, m...)
194 })
195 }
196
197 func (t *trackingResponseWriter) Header() http.Header {
198 return t.writer.Header()
199 }
200
201 func (t *trackingResponseWriter) Write(data []byte) (int, error) {
202 n, err := t.writer.Write(data)
203 t.respSize += int64(n)
204 return n, err
205 }
206
207 func (t *trackingResponseWriter) WriteHeader(statusCode int) {
208 t.writer.WriteHeader(statusCode)
209 t.statusCode = statusCode
210 t.statusLine = http.StatusText(t.statusCode)
211 }
212
213 // wrappedResponseWriter returns a wrapped version of the original
214 // ResponseWriter and only implements the same combination of additional
215 // interfaces as the original.
216 // This implementation is based on https://github.com/felixge/httpsnoop.
217 func (t *trackingResponseWriter) wrappedResponseWriter() http.ResponseWriter {
218 var (
219 hj, i0 = t.writer.(http.Hijacker)
220 cn, i1 = t.writer.(http.CloseNotifier)
221 pu, i2 = t.writer.(http.Pusher)
222 fl, i3 = t.writer.(http.Flusher)
223 rf, i4 = t.writer.(io.ReaderFrom)
224 )
225
226 switch {
227 case !i0 && !i1 && !i2 && !i3 && !i4:
228 return struct {
229 http.ResponseWriter
230 }{t}
231 case !i0 && !i1 && !i2 && !i3 && i4:
232 return struct {
233 http.ResponseWriter
234 io.ReaderFrom
235 }{t, rf}
236 case !i0 && !i1 && !i2 && i3 && !i4:
237 return struct {
238 http.ResponseWriter
239 http.Flusher
240 }{t, fl}
241 case !i0 && !i1 && !i2 && i3 && i4:
242 return struct {
243 http.ResponseWriter
244 http.Flusher
245 io.ReaderFrom
246 }{t, fl, rf}
247 case !i0 && !i1 && i2 && !i3 && !i4:
248 return struct {
249 http.ResponseWriter
250 http.Pusher
251 }{t, pu}
252 case !i0 && !i1 && i2 && !i3 && i4:
253 return struct {
254 http.ResponseWriter
255 http.Pusher
256 io.ReaderFrom
257 }{t, pu, rf}
258 case !i0 && !i1 && i2 && i3 && !i4:
259 return struct {
260 http.ResponseWriter
261 http.Pusher
262 http.Flusher
263 }{t, pu, fl}
264 case !i0 && !i1 && i2 && i3 && i4:
265 return struct {
266 http.ResponseWriter
267 http.Pusher
268 http.Flusher
269 io.ReaderFrom
270 }{t, pu, fl, rf}
271 case !i0 && i1 && !i2 && !i3 && !i4:
272 return struct {
273 http.ResponseWriter
274 http.CloseNotifier
275 }{t, cn}
276 case !i0 && i1 && !i2 && !i3 && i4:
277 return struct {
278 http.ResponseWriter
279 http.CloseNotifier
280 io.ReaderFrom
281 }{t, cn, rf}
282 case !i0 && i1 && !i2 && i3 && !i4:
283 return struct {
284 http.ResponseWriter
285 http.CloseNotifier
286 http.Flusher
287 }{t, cn, fl}
288 case !i0 && i1 && !i2 && i3 && i4:
289 return struct {
290 http.ResponseWriter
291 http.CloseNotifier
292 http.Flusher
293 io.ReaderFrom
294 }{t, cn, fl, rf}
295 case !i0 && i1 && i2 && !i3 && !i4:
296 return struct {
297 http.ResponseWriter
298 http.CloseNotifier
299 http.Pusher
300 }{t, cn, pu}
301 case !i0 && i1 && i2 && !i3 && i4:
302 return struct {
303 http.ResponseWriter
304 http.CloseNotifier
305 http.Pusher
306 io.ReaderFrom
307 }{t, cn, pu, rf}
308 case !i0 && i1 && i2 && i3 && !i4:
309 return struct {
310 http.ResponseWriter
311 http.CloseNotifier
312 http.Pusher
313 http.Flusher
314 }{t, cn, pu, fl}
315 case !i0 && i1 && i2 && i3 && i4:
316 return struct {
317 http.ResponseWriter
318 http.CloseNotifier
319 http.Pusher
320 http.Flusher
321 io.ReaderFrom
322 }{t, cn, pu, fl, rf}
323 case i0 && !i1 && !i2 && !i3 && !i4:
324 return struct {
325 http.ResponseWriter
326 http.Hijacker
327 }{t, hj}
328 case i0 && !i1 && !i2 && !i3 && i4:
329 return struct {
330 http.ResponseWriter
331 http.Hijacker
332 io.ReaderFrom
333 }{t, hj, rf}
334 case i0 && !i1 && !i2 && i3 && !i4:
335 return struct {
336 http.ResponseWriter
337 http.Hijacker
338 http.Flusher
339 }{t, hj, fl}
340 case i0 && !i1 && !i2 && i3 && i4:
341 return struct {
342 http.ResponseWriter
343 http.Hijacker
344 http.Flusher
345 io.ReaderFrom
346 }{t, hj, fl, rf}
347 case i0 && !i1 && i2 && !i3 && !i4:
348 return struct {
349 http.ResponseWriter
350 http.Hijacker
351 http.Pusher
352 }{t, hj, pu}
353 case i0 && !i1 && i2 && !i3 && i4:
354 return struct {
355 http.ResponseWriter
356 http.Hijacker
357 http.Pusher
358 io.ReaderFrom
359 }{t, hj, pu, rf}
360 case i0 && !i1 && i2 && i3 && !i4:
361 return struct {
362 http.ResponseWriter
363 http.Hijacker
364 http.Pusher
365 http.Flusher
366 }{t, hj, pu, fl}
367 case i0 && !i1 && i2 && i3 && i4:
368 return struct {
369 http.ResponseWriter
370 http.Hijacker
371 http.Pusher
372 http.Flusher
373 io.ReaderFrom
374 }{t, hj, pu, fl, rf}
375 case i0 && i1 && !i2 && !i3 && !i4:
376 return struct {
377 http.ResponseWriter
378 http.Hijacker
379 http.CloseNotifier
380 }{t, hj, cn}
381 case i0 && i1 && !i2 && !i3 && i4:
382 return struct {
383 http.ResponseWriter
384 http.Hijacker
385 http.CloseNotifier
386 io.ReaderFrom
387 }{t, hj, cn, rf}
388 case i0 && i1 && !i2 && i3 && !i4:
389 return struct {
390 http.ResponseWriter
391 http.Hijacker
392 http.CloseNotifier
393 http.Flusher
394 }{t, hj, cn, fl}
395 case i0 && i1 && !i2 && i3 && i4:
396 return struct {
397 http.ResponseWriter
398 http.Hijacker
399 http.CloseNotifier
400 http.Flusher
401 io.ReaderFrom
402 }{t, hj, cn, fl, rf}
403 case i0 && i1 && i2 && !i3 && !i4:
404 return struct {
405 http.ResponseWriter
406 http.Hijacker
407 http.CloseNotifier
408 http.Pusher
409 }{t, hj, cn, pu}
410 case i0 && i1 && i2 && !i3 && i4:
411 return struct {
412 http.ResponseWriter
413 http.Hijacker
414 http.CloseNotifier
415 http.Pusher
416 io.ReaderFrom
417 }{t, hj, cn, pu, rf}
418 case i0 && i1 && i2 && i3 && !i4:
419 return struct {
420 http.ResponseWriter
421 http.Hijacker
422 http.CloseNotifier
423 http.Pusher
424 http.Flusher
425 }{t, hj, cn, pu, fl}
426 case i0 && i1 && i2 && i3 && i4:
427 return struct {
428 http.ResponseWriter
429 http.Hijacker
430 http.CloseNotifier
431 http.Pusher
432 http.Flusher
433 io.ReaderFrom
434 }{t, hj, cn, pu, fl, rf}
435 default:
436 return struct {
437 http.ResponseWriter
438 }{t}
439 }
440 }