...

Source file src/google.golang.org/api/transport/http/internal/propagation/http.go

Documentation: google.golang.org/api/transport/http/internal/propagation

     1  // Copyright 2018 Google LLC.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build go1.8
     6  // +build go1.8
     7  
     8  // Package propagation implements X-Cloud-Trace-Context header propagation used
     9  // by Google Cloud products.
    10  package propagation
    11  
    12  import (
    13  	"encoding/binary"
    14  	"encoding/hex"
    15  	"fmt"
    16  	"net/http"
    17  	"strconv"
    18  	"strings"
    19  
    20  	"go.opencensus.io/trace"
    21  	"go.opencensus.io/trace/propagation"
    22  )
    23  
    24  const (
    25  	httpHeaderMaxSize = 200
    26  	httpHeader        = `X-Cloud-Trace-Context`
    27  )
    28  
    29  var _ propagation.HTTPFormat = (*HTTPFormat)(nil)
    30  
    31  // HTTPFormat implements propagation.HTTPFormat to propagate
    32  // traces in HTTP headers for Google Cloud Platform and Stackdriver Trace.
    33  type HTTPFormat struct{}
    34  
    35  // SpanContextFromRequest extracts a Stackdriver Trace span context from incoming requests.
    36  func (f *HTTPFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) {
    37  	h := req.Header.Get(httpHeader)
    38  	// See https://cloud.google.com/trace/docs/faq for the header HTTPFormat.
    39  	// Return if the header is empty or missing, or if the header is unreasonably
    40  	// large, to avoid making unnecessary copies of a large string.
    41  	if h == "" || len(h) > httpHeaderMaxSize {
    42  		return trace.SpanContext{}, false
    43  	}
    44  
    45  	// Parse the trace id field.
    46  	slash := strings.Index(h, `/`)
    47  	if slash == -1 {
    48  		return trace.SpanContext{}, false
    49  	}
    50  	tid, h := h[:slash], h[slash+1:]
    51  
    52  	buf, err := hex.DecodeString(tid)
    53  	if err != nil {
    54  		return trace.SpanContext{}, false
    55  	}
    56  	copy(sc.TraceID[:], buf)
    57  
    58  	// Parse the span id field.
    59  	spanstr := h
    60  	semicolon := strings.Index(h, `;`)
    61  	if semicolon != -1 {
    62  		spanstr, h = h[:semicolon], h[semicolon+1:]
    63  	}
    64  	sid, err := strconv.ParseUint(spanstr, 10, 64)
    65  	if err != nil {
    66  		return trace.SpanContext{}, false
    67  	}
    68  	binary.BigEndian.PutUint64(sc.SpanID[:], sid)
    69  
    70  	// Parse the options field, options field is optional.
    71  	if !strings.HasPrefix(h, "o=") {
    72  		return sc, true
    73  	}
    74  	o, err := strconv.ParseUint(h[2:], 10, 64)
    75  	if err != nil {
    76  		return trace.SpanContext{}, false
    77  	}
    78  	sc.TraceOptions = trace.TraceOptions(o)
    79  	return sc, true
    80  }
    81  
    82  // SpanContextToRequest modifies the given request to include a Stackdriver Trace header.
    83  func (f *HTTPFormat) SpanContextToRequest(sc trace.SpanContext, req *http.Request) {
    84  	sid := binary.BigEndian.Uint64(sc.SpanID[:])
    85  	header := fmt.Sprintf("%s/%d;o=%d", hex.EncodeToString(sc.TraceID[:]), sid, int64(sc.TraceOptions))
    86  	req.Header.Set(httpHeader, header)
    87  }
    88  

View as plain text