forked from open-telemetry/opentelemetry-java
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Parser.java
171 lines (155 loc) · 4.67 KB
/
Parser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.baggage.propagation;
import io.opentelemetry.api.baggage.BaggageBuilder;
import io.opentelemetry.api.baggage.BaggageEntryMetadata;
import java.nio.charset.StandardCharsets;
import javax.annotation.Nullable;
/**
* Implements single-pass Baggage parsing in accordance with https://w3c.github.io/baggage/ Key /
* value are restricted in accordance with https://www.ietf.org/rfc/rfc2616.txt
*
* <p>Note: following aspects are not specified in RFC: - some invalid elements (key or value) -
* parser will include valid ones, disregard invalid - empty "value" is regarded as invalid - meta -
* anything besides element terminator (comma)
*/
class Parser {
private enum State {
KEY,
VALUE,
META
}
private final String baggageHeader;
private final Element key = Element.createKeyElement();
private final Element value = Element.createValueElement();
private String meta;
private State state;
private int metaStart;
private boolean skipToNext;
public Parser(String baggageHeader) {
this.baggageHeader = baggageHeader;
reset(0);
}
void parseInto(BaggageBuilder baggageBuilder) {
for (int i = 0, n = baggageHeader.length(); i < n; i++) {
char current = baggageHeader.charAt(i);
if (skipToNext) {
if (current == ',') {
reset(i + 1);
}
continue;
}
switch (current) {
case '=':
{
if (state == State.KEY) {
if (key.tryTerminating(i, baggageHeader)) {
setState(State.VALUE, i + 1);
} else {
skipToNext = true;
}
} else if (state == State.VALUE) {
skipToNext = !value.tryNextChar(current, i);
}
break;
}
case ';':
{
if (state == State.VALUE) {
skipToNext = !value.tryTerminating(i, baggageHeader);
setState(State.META, i + 1);
}
break;
}
case ',':
{
switch (state) {
case VALUE:
value.tryTerminating(i, baggageHeader);
break;
case META:
meta = baggageHeader.substring(metaStart, i).trim();
break;
case KEY: // none
}
putBaggage(baggageBuilder, key.getValue(), value.getValue(), meta);
reset(i + 1);
break;
}
default:
{
switch (state) {
case KEY:
skipToNext = !key.tryNextChar(current, i);
break;
case VALUE:
skipToNext = !value.tryNextChar(current, i);
break;
case META: // none
}
}
}
}
// need to finish parsing if there was no list element termination comma
switch (state) {
case KEY:
break;
case META:
{
String rest = baggageHeader.substring(metaStart).trim();
putBaggage(baggageBuilder, key.getValue(), value.getValue(), rest);
break;
}
case VALUE:
{
if (!skipToNext) {
value.tryTerminating(baggageHeader.length(), baggageHeader);
putBaggage(baggageBuilder, key.getValue(), value.getValue(), null);
break;
}
}
}
}
private static void putBaggage(
BaggageBuilder baggage,
@Nullable String key,
@Nullable String value,
@Nullable String metadataValue) {
String decodedValue = decodeValue(value);
metadataValue = decodeValue(metadataValue);
BaggageEntryMetadata baggageEntryMetadata =
metadataValue != null
? BaggageEntryMetadata.create(metadataValue)
: BaggageEntryMetadata.empty();
if (key != null && decodedValue != null) {
baggage.put(key, decodedValue, baggageEntryMetadata);
}
}
@Nullable
private static String decodeValue(@Nullable String value) {
if (value == null) {
return null;
}
return BaggageCodec.decode(value, StandardCharsets.UTF_8);
}
/**
* Resets parsing state, preparing to start a new list element (see spec).
*
* @param index index where parser should start new element scan
*/
private void reset(int index) {
this.skipToNext = false;
this.state = State.KEY;
this.key.reset(index);
this.value.reset(index);
this.meta = "";
this.metaStart = 0;
}
/** Switches parser state (element of a list member). */
private void setState(State state, int start) {
this.state = state;
this.metaStart = start;
}
}