1 /*
2  * Copyright 2017, OpenCensus Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.opencensus.implcore.trace.propagation;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 
21 import com.google.common.annotations.VisibleForTesting;
22 import io.opencensus.trace.SpanContext;
23 import io.opencensus.trace.SpanId;
24 import io.opencensus.trace.TraceId;
25 import io.opencensus.trace.TraceOptions;
26 import io.opencensus.trace.Tracestate;
27 import io.opencensus.trace.propagation.SpanContextParseException;
28 import io.opencensus.trace.propagation.TextFormat;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.List;
32 
33 /*>>>
34 import org.checkerframework.checker.nullness.qual.NonNull;
35 */
36 
37 /**
38  * Implementation of the B3 propagation protocol. See <a
39  * href=https://github.com/openzipkin/b3-propagation>b3-propagation</a>.
40  */
41 final class B3Format extends TextFormat {
42   private static final Tracestate TRACESTATE_DEFAULT = Tracestate.builder().build();
43   @VisibleForTesting static final String X_B3_TRACE_ID = "X-B3-TraceId";
44   @VisibleForTesting static final String X_B3_SPAN_ID = "X-B3-SpanId";
45   @VisibleForTesting static final String X_B3_PARENT_SPAN_ID = "X-B3-ParentSpanId";
46   @VisibleForTesting static final String X_B3_SAMPLED = "X-B3-Sampled";
47   @VisibleForTesting static final String X_B3_FLAGS = "X-B3-Flags";
48   private static final List<String> FIELDS =
49       Collections.unmodifiableList(
50           Arrays.asList(
51               X_B3_TRACE_ID, X_B3_SPAN_ID, X_B3_PARENT_SPAN_ID, X_B3_SAMPLED, X_B3_FLAGS));
52 
53   // Used as the upper TraceId.SIZE hex characters of the traceID. B3-propagation used to send
54   // TraceId.SIZE hex characters (8-bytes traceId) in the past.
55   private static final String UPPER_TRACE_ID = "0000000000000000";
56   // Sampled value via the X_B3_SAMPLED header.
57   private static final String SAMPLED_VALUE = "1";
58   // "Debug" sampled value.
59   private static final String FLAGS_VALUE = "1";
60 
61   @Override
fields()62   public List<String> fields() {
63     return FIELDS;
64   }
65 
66   @Override
inject( SpanContext spanContext, C carrier, Setter<C> setter)67   public <C /*>>> extends @NonNull Object*/> void inject(
68       SpanContext spanContext, C carrier, Setter<C> setter) {
69     checkNotNull(spanContext, "spanContext");
70     checkNotNull(setter, "setter");
71     checkNotNull(carrier, "carrier");
72     setter.put(carrier, X_B3_TRACE_ID, spanContext.getTraceId().toLowerBase16());
73     setter.put(carrier, X_B3_SPAN_ID, spanContext.getSpanId().toLowerBase16());
74     if (spanContext.getTraceOptions().isSampled()) {
75       setter.put(carrier, X_B3_SAMPLED, SAMPLED_VALUE);
76     }
77   }
78 
79   @Override
extract(C carrier, Getter<C> getter)80   public <C /*>>> extends @NonNull Object*/> SpanContext extract(C carrier, Getter<C> getter)
81       throws SpanContextParseException {
82     checkNotNull(carrier, "carrier");
83     checkNotNull(getter, "getter");
84     try {
85       TraceId traceId;
86       String traceIdStr = getter.get(carrier, X_B3_TRACE_ID);
87       if (traceIdStr != null) {
88         if (traceIdStr.length() == TraceId.SIZE) {
89           // This is an 8-byte traceID.
90           traceIdStr = UPPER_TRACE_ID + traceIdStr;
91         }
92         traceId = TraceId.fromLowerBase16(traceIdStr);
93       } else {
94         throw new SpanContextParseException("Missing X_B3_TRACE_ID.");
95       }
96       SpanId spanId;
97       String spanIdStr = getter.get(carrier, X_B3_SPAN_ID);
98       if (spanIdStr != null) {
99         spanId = SpanId.fromLowerBase16(spanIdStr);
100       } else {
101         throw new SpanContextParseException("Missing X_B3_SPAN_ID.");
102       }
103       TraceOptions traceOptions = TraceOptions.DEFAULT;
104       if (SAMPLED_VALUE.equals(getter.get(carrier, X_B3_SAMPLED))
105           || FLAGS_VALUE.equals(getter.get(carrier, X_B3_FLAGS))) {
106         traceOptions = TraceOptions.builder().setIsSampled(true).build();
107       }
108       return SpanContext.create(traceId, spanId, traceOptions, TRACESTATE_DEFAULT);
109     } catch (IllegalArgumentException e) {
110       throw new SpanContextParseException("Invalid input.", e);
111     }
112   }
113 }
114