| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 | // 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 ochttpimport (	"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.LinkTypeParent,				Attributes: nil,			})		}	}	span.AddAttributes(requestAttrs(r)...)	if r.Body == nil {		// TODO: Handle cases where ContentLength is not set.	} else if r.ContentLength > 0 {		span.AddMessageReceiveEvent(0, /* TODO: messageID */			int64(r.ContentLength), -1)	}	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.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 interfacevar _ http.ResponseWriter = (*trackingResponseWriter)(nil)var logTagsErrorOnce sync.Oncefunc (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)	// Add message event for request bytes sent.	span := trace.FromContext(t.ctx)	span.AddMessageSendEvent(0 /* TODO: messageID */, int64(n), -1)	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}	}}
 |