Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / 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.LinkTypeParent,
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.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 }