diff options
Diffstat (limited to 'vendor/go.opencensus.io/trace/tracestate/tracestate.go')
-rw-r--r-- | vendor/go.opencensus.io/trace/tracestate/tracestate.go | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/vendor/go.opencensus.io/trace/tracestate/tracestate.go b/vendor/go.opencensus.io/trace/tracestate/tracestate.go new file mode 100644 index 0000000..2d6c713 --- /dev/null +++ b/vendor/go.opencensus.io/trace/tracestate/tracestate.go | |||
@@ -0,0 +1,147 @@ | |||
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 tracestate implements support for the Tracestate header of the | ||
16 | // W3C TraceContext propagation format. | ||
17 | package tracestate | ||
18 | |||
19 | import ( | ||
20 | "fmt" | ||
21 | "regexp" | ||
22 | ) | ||
23 | |||
24 | const ( | ||
25 | keyMaxSize = 256 | ||
26 | valueMaxSize = 256 | ||
27 | maxKeyValuePairs = 32 | ||
28 | ) | ||
29 | |||
30 | const ( | ||
31 | keyWithoutVendorFormat = `[a-z][_0-9a-z\-\*\/]{0,255}` | ||
32 | keyWithVendorFormat = `[a-z][_0-9a-z\-\*\/]{0,240}@[a-z][_0-9a-z\-\*\/]{0,13}` | ||
33 | keyFormat = `(` + keyWithoutVendorFormat + `)|(` + keyWithVendorFormat + `)` | ||
34 | valueFormat = `[\x20-\x2b\x2d-\x3c\x3e-\x7e]{0,255}[\x21-\x2b\x2d-\x3c\x3e-\x7e]` | ||
35 | ) | ||
36 | |||
37 | var keyValidationRegExp = regexp.MustCompile(`^(` + keyFormat + `)$`) | ||
38 | var valueValidationRegExp = regexp.MustCompile(`^(` + valueFormat + `)$`) | ||
39 | |||
40 | // Tracestate represents tracing-system specific context in a list of key-value pairs. Tracestate allows different | ||
41 | // vendors propagate additional information and inter-operate with their legacy Id formats. | ||
42 | type Tracestate struct { | ||
43 | entries []Entry | ||
44 | } | ||
45 | |||
46 | // Entry represents one key-value pair in a list of key-value pair of Tracestate. | ||
47 | type Entry struct { | ||
48 | // Key is an opaque string up to 256 characters printable. It MUST begin with a lowercase letter, | ||
49 | // and can only contain lowercase letters a-z, digits 0-9, underscores _, dashes -, asterisks *, and | ||
50 | // forward slashes /. | ||
51 | Key string | ||
52 | |||
53 | // Value is an opaque string up to 256 characters printable ASCII RFC0020 characters (i.e., the | ||
54 | // range 0x20 to 0x7E) except comma , and =. | ||
55 | Value string | ||
56 | } | ||
57 | |||
58 | // Entries returns a slice of Entry. | ||
59 | func (ts *Tracestate) Entries() []Entry { | ||
60 | if ts == nil { | ||
61 | return nil | ||
62 | } | ||
63 | return ts.entries | ||
64 | } | ||
65 | |||
66 | func (ts *Tracestate) remove(key string) *Entry { | ||
67 | for index, entry := range ts.entries { | ||
68 | if entry.Key == key { | ||
69 | ts.entries = append(ts.entries[:index], ts.entries[index+1:]...) | ||
70 | return &entry | ||
71 | } | ||
72 | } | ||
73 | return nil | ||
74 | } | ||
75 | |||
76 | func (ts *Tracestate) add(entries []Entry) error { | ||
77 | for _, entry := range entries { | ||
78 | ts.remove(entry.Key) | ||
79 | } | ||
80 | if len(ts.entries)+len(entries) > maxKeyValuePairs { | ||
81 | return fmt.Errorf("adding %d key-value pairs to current %d pairs exceeds the limit of %d", | ||
82 | len(entries), len(ts.entries), maxKeyValuePairs) | ||
83 | } | ||
84 | ts.entries = append(entries, ts.entries...) | ||
85 | return nil | ||
86 | } | ||
87 | |||
88 | func isValid(entry Entry) bool { | ||
89 | return keyValidationRegExp.MatchString(entry.Key) && | ||
90 | valueValidationRegExp.MatchString(entry.Value) | ||
91 | } | ||
92 | |||
93 | func containsDuplicateKey(entries ...Entry) (string, bool) { | ||
94 | keyMap := make(map[string]int) | ||
95 | for _, entry := range entries { | ||
96 | if _, ok := keyMap[entry.Key]; ok { | ||
97 | return entry.Key, true | ||
98 | } | ||
99 | keyMap[entry.Key] = 1 | ||
100 | } | ||
101 | return "", false | ||
102 | } | ||
103 | |||
104 | func areEntriesValid(entries ...Entry) (*Entry, bool) { | ||
105 | for _, entry := range entries { | ||
106 | if !isValid(entry) { | ||
107 | return &entry, false | ||
108 | } | ||
109 | } | ||
110 | return nil, true | ||
111 | } | ||
112 | |||
113 | // New creates a Tracestate object from a parent and/or entries (key-value pair). | ||
114 | // Entries from the parent are copied if present. The entries passed to this function | ||
115 | // are inserted in front of those copied from the parent. If an entry copied from the | ||
116 | // parent contains the same key as one of the entry in entries then the entry copied | ||
117 | // from the parent is removed. See add func. | ||
118 | // | ||
119 | // An error is returned with nil Tracestate if | ||
120 | // 1. one or more entry in entries is invalid. | ||
121 | // 2. two or more entries in the input entries have the same key. | ||
122 | // 3. the number of entries combined from the parent and the input entries exceeds maxKeyValuePairs. | ||
123 | // (duplicate entry is counted only once). | ||
124 | func New(parent *Tracestate, entries ...Entry) (*Tracestate, error) { | ||
125 | if parent == nil && len(entries) == 0 { | ||
126 | return nil, nil | ||
127 | } | ||
128 | if entry, ok := areEntriesValid(entries...); !ok { | ||
129 | return nil, fmt.Errorf("key-value pair {%s, %s} is invalid", entry.Key, entry.Value) | ||
130 | } | ||
131 | |||
132 | if key, duplicate := containsDuplicateKey(entries...); duplicate { | ||
133 | return nil, fmt.Errorf("contains duplicate keys (%s)", key) | ||
134 | } | ||
135 | |||
136 | tracestate := Tracestate{} | ||
137 | |||
138 | if parent != nil && len(parent.entries) > 0 { | ||
139 | tracestate.entries = append([]Entry{}, parent.entries...) | ||
140 | } | ||
141 | |||
142 | err := tracestate.add(entries) | ||
143 | if err != nil { | ||
144 | return nil, err | ||
145 | } | ||
146 | return &tracestate, nil | ||
147 | } | ||