aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/go.opencensus.io/plugin/ochttp/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/go.opencensus.io/plugin/ochttp/server.go')
-rw-r--r--vendor/go.opencensus.io/plugin/ochttp/server.go440
1 files changed, 440 insertions, 0 deletions
diff --git a/vendor/go.opencensus.io/plugin/ochttp/server.go b/vendor/go.opencensus.io/plugin/ochttp/server.go
new file mode 100644
index 000000000..ff72de97a
--- /dev/null
+++ b/vendor/go.opencensus.io/plugin/ochttp/server.go
@@ -0,0 +1,440 @@
+// Copyright 2018, OpenCensus Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package ochttp
+
+import (
+ "context"
+ "io"
+ "net/http"
+ "strconv"
+ "sync"
+ "time"
+
+ "go.opencensus.io/stats"
+ "go.opencensus.io/tag"
+ "go.opencensus.io/trace"
+ "go.opencensus.io/trace/propagation"
+)
+
+// Handler is an http.Handler wrapper to instrument your HTTP server with
+// OpenCensus. It supports both stats and tracing.
+//
+// Tracing
+//
+// This handler is aware of the incoming request's span, reading it from request
+// headers as configured using the Propagation field.
+// The extracted span can be accessed from the incoming request's
+// context.
+//
+// span := trace.FromContext(r.Context())
+//
+// The server span will be automatically ended at the end of ServeHTTP.
+type Handler struct {
+ // Propagation defines how traces are propagated. If unspecified,
+ // B3 propagation will be used.
+ Propagation propagation.HTTPFormat
+
+ // Handler is the handler used to handle the incoming request.
+ Handler http.Handler
+
+ // StartOptions are applied to the span started by this Handler around each
+ // request.
+ //
+ // StartOptions.SpanKind will always be set to trace.SpanKindServer
+ // for spans started by this transport.
+ StartOptions trace.StartOptions
+
+ // GetStartOptions allows to set start options per request. If set,
+ // StartOptions is going to be ignored.
+ GetStartOptions func(*http.Request) trace.StartOptions
+
+ // IsPublicEndpoint should be set to true for publicly accessible HTTP(S)
+ // servers. If true, any trace metadata set on the incoming request will
+ // be added as a linked trace instead of being added as a parent of the
+ // current trace.
+ IsPublicEndpoint bool
+
+ // FormatSpanName holds the function to use for generating the span name
+ // from the information found in the incoming HTTP Request. By default the
+ // name equals the URL Path.
+ FormatSpanName func(*http.Request) string
+}
+
+func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ var tags addedTags
+ r, traceEnd := h.startTrace(w, r)
+ defer traceEnd()
+ w, statsEnd := h.startStats(w, r)
+ defer statsEnd(&tags)
+ handler := h.Handler
+ if handler == nil {
+ handler = http.DefaultServeMux
+ }
+ r = r.WithContext(context.WithValue(r.Context(), addedTagsKey{}, &tags))
+ handler.ServeHTTP(w, r)
+}
+
+func (h *Handler) startTrace(w http.ResponseWriter, r *http.Request) (*http.Request, func()) {
+ if isHealthEndpoint(r.URL.Path) {
+ return r, func() {}
+ }
+ var name string
+ if h.FormatSpanName == nil {
+ name = spanNameFromURL(r)
+ } else {
+ name = h.FormatSpanName(r)
+ }
+ ctx := r.Context()
+
+ startOpts := h.StartOptions
+ if h.GetStartOptions != nil {
+ startOpts = h.GetStartOptions(r)
+ }
+
+ var span *trace.Span
+ sc, ok := h.extractSpanContext(r)
+ if ok && !h.IsPublicEndpoint {
+ ctx, span = trace.StartSpanWithRemoteParent(ctx, name, sc,
+ trace.WithSampler(startOpts.Sampler),
+ trace.WithSpanKind(trace.SpanKindServer))
+ } else {
+ ctx, span = trace.StartSpan(ctx, name,
+ trace.WithSampler(startOpts.Sampler),
+ trace.WithSpanKind(trace.SpanKindServer),
+ )
+ if ok {
+ span.AddLink(trace.Link{
+ TraceID: sc.TraceID,
+ SpanID: sc.SpanID,
+ Type: trace.LinkTypeChild,
+ Attributes: nil,
+ })
+ }
+ }
+ span.AddAttributes(requestAttrs(r)...)
+ return r.WithContext(ctx), span.End
+}
+
+func (h *Handler) extractSpanContext(r *http.Request) (trace.SpanContext, bool) {
+ if h.Propagation == nil {
+ return defaultFormat.SpanContextFromRequest(r)
+ }
+ return h.Propagation.SpanContextFromRequest(r)
+}
+
+func (h *Handler) startStats(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, func(tags *addedTags)) {
+ ctx, _ := tag.New(r.Context(),
+ tag.Upsert(Host, r.URL.Host),
+ tag.Upsert(Path, r.URL.Path),
+ tag.Upsert(Method, r.Method))
+ track := &trackingResponseWriter{
+ start: time.Now(),
+ ctx: ctx,
+ writer: w,
+ }
+ if r.Body == nil {
+ // TODO: Handle cases where ContentLength is not set.
+ track.reqSize = -1
+ } else if r.ContentLength > 0 {
+ track.reqSize = r.ContentLength
+ }
+ stats.Record(ctx, ServerRequestCount.M(1))
+ return track.wrappedResponseWriter(), track.end
+}
+
+type trackingResponseWriter struct {
+ ctx context.Context
+ reqSize int64
+ respSize int64
+ start time.Time
+ statusCode int
+ statusLine string
+ endOnce sync.Once
+ writer http.ResponseWriter
+}
+
+// Compile time assertion for ResponseWriter interface
+var _ http.ResponseWriter = (*trackingResponseWriter)(nil)
+
+var logTagsErrorOnce sync.Once
+
+func (t *trackingResponseWriter) end(tags *addedTags) {
+ t.endOnce.Do(func() {
+ if t.statusCode == 0 {
+ t.statusCode = 200
+ }
+
+ span := trace.FromContext(t.ctx)
+ span.SetStatus(TraceStatus(t.statusCode, t.statusLine))
+ span.AddAttributes(trace.Int64Attribute(StatusCodeAttribute, int64(t.statusCode)))
+
+ m := []stats.Measurement{
+ ServerLatency.M(float64(time.Since(t.start)) / float64(time.Millisecond)),
+ ServerResponseBytes.M(t.respSize),
+ }
+ if t.reqSize >= 0 {
+ m = append(m, ServerRequestBytes.M(t.reqSize))
+ }
+ allTags := make([]tag.Mutator, len(tags.t)+1)
+ allTags[0] = tag.Upsert(StatusCode, strconv.Itoa(t.statusCode))
+ copy(allTags[1:], tags.t)
+ stats.RecordWithTags(t.ctx, allTags, m...)
+ })
+}
+
+func (t *trackingResponseWriter) Header() http.Header {
+ return t.writer.Header()
+}
+
+func (t *trackingResponseWriter) Write(data []byte) (int, error) {
+ n, err := t.writer.Write(data)
+ t.respSize += int64(n)
+ return n, err
+}
+
+func (t *trackingResponseWriter) WriteHeader(statusCode int) {
+ t.writer.WriteHeader(statusCode)
+ t.statusCode = statusCode
+ t.statusLine = http.StatusText(t.statusCode)
+}
+
+// wrappedResponseWriter returns a wrapped version of the original
+// ResponseWriter and only implements the same combination of additional
+// interfaces as the original.
+// This implementation is based on https://github.com/felixge/httpsnoop.
+func (t *trackingResponseWriter) wrappedResponseWriter() http.ResponseWriter {
+ var (
+ hj, i0 = t.writer.(http.Hijacker)
+ cn, i1 = t.writer.(http.CloseNotifier)
+ pu, i2 = t.writer.(http.Pusher)
+ fl, i3 = t.writer.(http.Flusher)
+ rf, i4 = t.writer.(io.ReaderFrom)
+ )
+
+ switch {
+ case !i0 && !i1 && !i2 && !i3 && !i4:
+ return struct {
+ http.ResponseWriter
+ }{t}
+ case !i0 && !i1 && !i2 && !i3 && i4:
+ return struct {
+ http.ResponseWriter
+ io.ReaderFrom
+ }{t, rf}
+ case !i0 && !i1 && !i2 && i3 && !i4:
+ return struct {
+ http.ResponseWriter
+ http.Flusher
+ }{t, fl}
+ case !i0 && !i1 && !i2 && i3 && i4:
+ return struct {
+ http.ResponseWriter
+ http.Flusher
+ io.ReaderFrom
+ }{t, fl, rf}
+ case !i0 && !i1 && i2 && !i3 && !i4:
+ return struct {
+ http.ResponseWriter
+ http.Pusher
+ }{t, pu}
+ case !i0 && !i1 && i2 && !i3 && i4:
+ return struct {
+ http.ResponseWriter
+ http.Pusher
+ io.ReaderFrom
+ }{t, pu, rf}
+ case !i0 && !i1 && i2 && i3 && !i4:
+ return struct {
+ http.ResponseWriter
+ http.Pusher
+ http.Flusher
+ }{t, pu, fl}
+ case !i0 && !i1 && i2 && i3 && i4:
+ return struct {
+ http.ResponseWriter
+ http.Pusher
+ http.Flusher
+ io.ReaderFrom
+ }{t, pu, fl, rf}
+ case !i0 && i1 && !i2 && !i3 && !i4:
+ return struct {
+ http.ResponseWriter
+ http.CloseNotifier
+ }{t, cn}
+ case !i0 && i1 && !i2 && !i3 && i4:
+ return struct {
+ http.ResponseWriter
+ http.CloseNotifier
+ io.ReaderFrom
+ }{t, cn, rf}
+ case !i0 && i1 && !i2 && i3 && !i4:
+ return struct {
+ http.ResponseWriter
+ http.CloseNotifier
+ http.Flusher
+ }{t, cn, fl}
+ case !i0 && i1 && !i2 && i3 && i4:
+ return struct {
+ http.ResponseWriter
+ http.CloseNotifier
+ http.Flusher
+ io.ReaderFrom
+ }{t, cn, fl, rf}
+ case !i0 && i1 && i2 && !i3 && !i4:
+ return struct {
+ http.ResponseWriter
+ http.CloseNotifier
+ http.Pusher
+ }{t, cn, pu}
+ case !i0 && i1 && i2 && !i3 && i4:
+ return struct {
+ http.ResponseWriter
+ http.CloseNotifier
+ http.Pusher
+ io.ReaderFrom
+ }{t, cn, pu, rf}
+ case !i0 && i1 && i2 && i3 && !i4:
+ return struct {
+ http.ResponseWriter
+ http.CloseNotifier
+ http.Pusher
+ http.Flusher
+ }{t, cn, pu, fl}
+ case !i0 && i1 && i2 && i3 && i4:
+ return struct {
+ http.ResponseWriter
+ http.CloseNotifier
+ http.Pusher
+ http.Flusher
+ io.ReaderFrom
+ }{t, cn, pu, fl, rf}
+ case i0 && !i1 && !i2 && !i3 && !i4:
+ return struct {
+ http.ResponseWriter
+ http.Hijacker
+ }{t, hj}
+ case i0 && !i1 && !i2 && !i3 && i4:
+ return struct {
+ http.ResponseWriter
+ http.Hijacker
+ io.ReaderFrom
+ }{t, hj, rf}
+ case i0 && !i1 && !i2 && i3 && !i4:
+ return struct {
+ http.ResponseWriter
+ http.Hijacker
+ http.Flusher
+ }{t, hj, fl}
+ case i0 && !i1 && !i2 && i3 && i4:
+ return struct {
+ http.ResponseWriter
+ http.Hijacker
+ http.Flusher
+ io.ReaderFrom
+ }{t, hj, fl, rf}
+ case i0 && !i1 && i2 && !i3 && !i4:
+ return struct {
+ http.ResponseWriter
+ http.Hijacker
+ http.Pusher
+ }{t, hj, pu}
+ case i0 && !i1 && i2 && !i3 && i4:
+ return struct {
+ http.ResponseWriter
+ http.Hijacker
+ http.Pusher
+ io.ReaderFrom
+ }{t, hj, pu, rf}
+ case i0 && !i1 && i2 && i3 && !i4:
+ return struct {
+ http.ResponseWriter
+ http.Hijacker
+ http.Pusher
+ http.Flusher
+ }{t, hj, pu, fl}
+ case i0 && !i1 && i2 && i3 && i4:
+ return struct {
+ http.ResponseWriter
+ http.Hijacker
+ http.Pusher
+ http.Flusher
+ io.ReaderFrom
+ }{t, hj, pu, fl, rf}
+ case i0 && i1 && !i2 && !i3 && !i4:
+ return struct {
+ http.ResponseWriter
+ http.Hijacker
+ http.CloseNotifier
+ }{t, hj, cn}
+ case i0 && i1 && !i2 && !i3 && i4:
+ return struct {
+ http.ResponseWriter
+ http.Hijacker
+ http.CloseNotifier
+ io.ReaderFrom
+ }{t, hj, cn, rf}
+ case i0 && i1 && !i2 && i3 && !i4:
+ return struct {
+ http.ResponseWriter
+ http.Hijacker
+ http.CloseNotifier
+ http.Flusher
+ }{t, hj, cn, fl}
+ case i0 && i1 && !i2 && i3 && i4:
+ return struct {
+ http.ResponseWriter
+ http.Hijacker
+ http.CloseNotifier
+ http.Flusher
+ io.ReaderFrom
+ }{t, hj, cn, fl, rf}
+ case i0 && i1 && i2 && !i3 && !i4:
+ return struct {
+ http.ResponseWriter
+ http.Hijacker
+ http.CloseNotifier
+ http.Pusher
+ }{t, hj, cn, pu}
+ case i0 && i1 && i2 && !i3 && i4:
+ return struct {
+ http.ResponseWriter
+ http.Hijacker
+ http.CloseNotifier
+ http.Pusher
+ io.ReaderFrom
+ }{t, hj, cn, pu, rf}
+ case i0 && i1 && i2 && i3 && !i4:
+ return struct {
+ http.ResponseWriter
+ http.Hijacker
+ http.CloseNotifier
+ http.Pusher
+ http.Flusher
+ }{t, hj, cn, pu, fl}
+ case i0 && i1 && i2 && i3 && i4:
+ return struct {
+ http.ResponseWriter
+ http.Hijacker
+ http.CloseNotifier
+ http.Pusher
+ http.Flusher
+ io.ReaderFrom
+ }{t, hj, cn, pu, fl, rf}
+ default:
+ return struct {
+ http.ResponseWriter
+ }{t}
+ }
+}