Skip to content

Commit f7e76e0

Browse files
committed
Remove Antlr dependency with manual parsing
1 parent 0ce21ba commit f7e76e0

File tree

6 files changed

+184
-129
lines changed

6 files changed

+184
-129
lines changed

grpc-contrib/pom.xml

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@
3535
<groupId>io.grpc</groupId>
3636
<artifactId>grpc-context</artifactId>
3737
</dependency>
38-
<dependency>
39-
<groupId>org.antlr</groupId>
40-
<artifactId>antlr4-runtime</artifactId>
41-
</dependency>
4238
<dependency>
4339
<groupId>com.google.code.gson</groupId>
4440
<artifactId>gson</artifactId>
@@ -103,18 +99,6 @@
10399
</execution>
104100
</executions>
105101
</plugin>
106-
<plugin>
107-
<groupId>org.antlr</groupId>
108-
<artifactId>antlr4-maven-plugin</artifactId>
109-
<version>${antlr.version}</version>
110-
<executions>
111-
<execution>
112-
<goals>
113-
<goal>antlr4</goal>
114-
</goals>
115-
</execution>
116-
</executions>
117-
</plugin>
118102
</plugins>
119103
</build>
120104
</project>

grpc-contrib/src/main/antlr4/com/salesforce/grpc/contrib/xfcc/Xfcc.g4

Lines changed: 0 additions & 53 deletions
This file was deleted.

grpc-contrib/src/main/java/com/salesforce/grpc/contrib/xfcc/XfccMarshaller.java

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,7 @@
88
package com.salesforce.grpc.contrib.xfcc;
99

1010
import io.grpc.Metadata;
11-
import org.antlr.v4.runtime.CharStreams;
12-
import org.antlr.v4.runtime.CommonTokenStream;
13-
import org.antlr.v4.runtime.tree.ParseTreeWalker;
1411

15-
import java.util.ArrayList;
1612
import java.util.List;
1713
import java.util.stream.Collectors;
1814

@@ -31,54 +27,6 @@ public String toAsciiString(List<XForwardedClientCert> value) {
3127

3228
@Override
3329
public List<XForwardedClientCert> parseAsciiString(String serialized) {
34-
try {
35-
List<XForwardedClientCert> clientCerts = new ArrayList<>();
36-
37-
XfccLexer lexer = new XfccLexer(CharStreams.fromString(serialized));
38-
CommonTokenStream tokens = new CommonTokenStream(lexer);
39-
XfccParser parser = new XfccParser(tokens);
40-
41-
XfccParser.HeaderContext headerContext = parser.header();
42-
ParseTreeWalker walker = new ParseTreeWalker();
43-
44-
XfccListener listener = new XfccBaseListener() {
45-
private XForwardedClientCert clientCert;
46-
47-
@Override
48-
public void enterElement(XfccParser.ElementContext ctx) {
49-
clientCert = new XForwardedClientCert();
50-
}
51-
52-
@Override
53-
public void enterKvp(XfccParser.KvpContext ctx) {
54-
XfccParser.KeyContext key = ctx.key();
55-
XfccParser.ValueContext value = ctx.value();
56-
57-
if (key.BY() != null) {
58-
clientCert.setBy(value.getText());
59-
}
60-
if (key.HASH() != null) {
61-
clientCert.setHash(value.getText());
62-
}
63-
if (key.SAN() != null) {
64-
clientCert.setSan(value.getText());
65-
}
66-
if (key.SUBJECT() != null) {
67-
clientCert.setSubject(value.getText());
68-
}
69-
}
70-
71-
@Override
72-
public void exitElement(XfccParser.ElementContext ctx) {
73-
clientCerts.add(clientCert);
74-
clientCert = null;
75-
}
76-
};
77-
78-
walker.walk(listener, headerContext);
79-
return clientCerts;
80-
} catch (NoClassDefFoundError err) {
81-
throw new RuntimeException("Likely missing optional dependency on org.antlr:antlr4-runtime:4.7", err);
82-
}
30+
return XfccParser.parse(serialized);
8331
}
8432
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright (c) 2017, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
8+
package com.salesforce.grpc.contrib.xfcc;
9+
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
13+
/**
14+
* {@code XfccParser} parses the {@code x-forwarded-client-cert} (XFCC) header populated by TLS-terminating
15+
* reverse proxies.
16+
*
17+
* @see <a href="https://www.envoyproxy.io/docs/envoy/latest/configuration/http_conn_man/headers.html#config-http-conn-man-headers-x-forwarded-client-cert">Envoy XFCC Header</a>
18+
*/
19+
final class XfccParser {
20+
private XfccParser() { }
21+
22+
/**
23+
* Given a header string, parse and return a collection of {@link XForwardedClientCert} objects.
24+
*/
25+
static List<XForwardedClientCert> parse(String header) {
26+
List<XForwardedClientCert> certs = new ArrayList<>();
27+
28+
for (String element : quoteAwareSplit(header, ',')) {
29+
XForwardedClientCert cert = new XForwardedClientCert();
30+
List<String> kvps = quoteAwareSplit(element, ';');
31+
for (String kvp : kvps) {
32+
List<String> l = quoteAwareSplit(kvp, '=');
33+
34+
if (l.get(0).toLowerCase().equals("by")) {
35+
cert.setBy(dequote(l.get(1)));
36+
}
37+
if (l.get(0).toLowerCase().equals("hash")) {
38+
cert.setHash(dequote(l.get(1)));
39+
}
40+
if (l.get(0).toLowerCase().equals("san")) {
41+
cert.setSan(dequote(l.get(1)));
42+
}
43+
if (l.get(0).toLowerCase().equals("subject")) {
44+
cert.setSubject(dequote(l.get(1)));
45+
}
46+
}
47+
certs.add(cert);
48+
}
49+
50+
return certs;
51+
}
52+
53+
// Break str into individual elements, splitting on delim (not in quotes)
54+
private static List<String> quoteAwareSplit(String str, char delim) {
55+
boolean inQuotes = false;
56+
boolean inEscape = false;
57+
58+
List<String> elements = new ArrayList<>();
59+
StringBuilder buffer = new StringBuilder();
60+
for (char c : str.toCharArray()) {
61+
if (c == delim && !inQuotes) {
62+
elements.add(buffer.toString());
63+
buffer = new StringBuilder();
64+
inEscape = false;
65+
continue;
66+
}
67+
68+
if (c == '"') {
69+
if (inQuotes) {
70+
if (!inEscape) {
71+
inQuotes = false;
72+
}
73+
} else {
74+
inQuotes = true;
75+
76+
}
77+
inEscape = false;
78+
buffer.append(c);
79+
continue;
80+
}
81+
82+
if (c == '\\') {
83+
if (!inEscape) {
84+
inEscape = true;
85+
buffer.append(c);
86+
continue;
87+
}
88+
}
89+
90+
// all other characters
91+
inEscape = false;
92+
buffer.append(c);
93+
}
94+
95+
if (inQuotes) {
96+
throw new RuntimeException("Quoted string not closed");
97+
}
98+
99+
elements.add(buffer.toString());
100+
101+
return elements;
102+
}
103+
104+
// Remove leading and tailing unescaped quotes, remove escaping from escaped internal quotes
105+
private static String dequote(String str) {
106+
str = str.replace("\\\"", "\"");
107+
if (str.startsWith("\"")) {
108+
str = str.substring(1);
109+
}
110+
if (str.endsWith("\"")) {
111+
str = str.substring(0, str.length() - 1);
112+
}
113+
return str;
114+
}
115+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright (c) 2017, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
8+
package com.salesforce.grpc.contrib.xfcc;
9+
10+
import org.junit.Test;
11+
12+
import java.util.List;
13+
14+
import static org.assertj.core.api.Assertions.assertThat;
15+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
16+
17+
public class XfccParserTest {
18+
@Test
19+
public void parseSimpleHeaderWorks() {
20+
String header = "By=http://frontend.lyft.com;Hash=468ed33be74eee6556d90c0149c1309e9ba61d6425303443c0748a02dd8de688;" +
21+
"SAN=http://testclient.lyft.com";
22+
List<XForwardedClientCert> certs = XfccParser.parse(header);
23+
24+
assertThat(certs.size()).isEqualTo(1);
25+
assertThat(certs.get(0).getBy()).isEqualTo("http://frontend.lyft.com");
26+
assertThat(certs.get(0).getHash()).isEqualTo("468ed33be74eee6556d90c0149c1309e9ba61d6425303443c0748a02dd8de688");
27+
assertThat(certs.get(0).getSan()).isEqualTo("http://testclient.lyft.com");
28+
assertThat(certs.get(0).getSubject()).isEmpty();
29+
}
30+
@Test
31+
public void parseCompoundHeaderWorks() {
32+
String header = "By=http://frontend.lyft.com;Hash=468ed33be74eee6556d90c0149c1309e9ba61d6425303443c0748a02dd8de688;SAN=http://testclient.lyft.com," +
33+
"By=http://backend.lyft.com;Hash=9ba61d6425303443c0748a02dd8de688468ed33be74eee6556d90c0149c1309e;SAN=http://frontend.lyft.com";
34+
List<XForwardedClientCert> certs = XfccParser.parse(header);
35+
36+
assertThat(certs.size()).isEqualTo(2);
37+
assertThat(certs.get(0).getBy()).isEqualTo("http://frontend.lyft.com");
38+
assertThat(certs.get(1).getBy()).isEqualTo("http://backend.lyft.com");
39+
}
40+
41+
@Test
42+
public void quotedHeaderWorks() {
43+
String header = "By=http://frontend.lyft.com;Hash=468ed33be74eee6556d90c0149c1309e9ba61d6425303443c0748a02dd8de688;" +
44+
"Subject=\"/C=US/ST=CA/L=San Francisco/OU=Lyft/CN=Test Client\";SAN=http://testclient.lyft.com";
45+
List<XForwardedClientCert> certs = XfccParser.parse(header);
46+
47+
assertThat(certs.size()).isEqualTo(1);
48+
assertThat(certs.get(0).getSubject()).isEqualTo("/C=US/ST=CA/L=San Francisco/OU=Lyft/CN=Test Client");
49+
}
50+
51+
@Test
52+
public void escapedQuotedHeaderWorks() {
53+
String header = "By=http://frontend.lyft.com;Hash=468ed33be74eee6556d90c0149c1309e9ba61d6425303443c0748a02dd8de688;" +
54+
"Subject=\"/C=US/ST=CA/L=\\\"San Francisco\\\"/OU=Lyft/CN=Test Client\";SAN=http://testclient.lyft.com";
55+
List<XForwardedClientCert> certs = XfccParser.parse(header);
56+
57+
assertThat(certs.size()).isEqualTo(1);
58+
assertThat(certs.get(0).getSubject()).isEqualTo("/C=US/ST=CA/L=\"San Francisco\"/OU=Lyft/CN=Test Client");
59+
}
60+
61+
@Test
62+
public void mismatchedQuotesThrows() {
63+
String header = "By=http://frontend.lyft.com;Hash=468ed33be74eee6556d90c0149c1309e9ba61d6425303443c0748a02dd8de688;" +
64+
"Subject=\"/C=US/ST=CA/L=\\\"San Francisco\"/OU=Lyft/CN=Test Client\";SAN=http://testclient.lyft.com";
65+
66+
assertThatThrownBy(() -> XfccParser.parse(header)).isInstanceOf(RuntimeException.class);
67+
}
68+
}

pom.xml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@
6969
<gson.version>2.7</gson.version> <!-- Same version as grpc-proto -->
7070
<mustache-java.version>0.9.4</mustache-java.version>
7171
<spring.version>4.2.0.RELEASE</spring.version>
72-
<antlr.version>4.7</antlr.version>
7372

7473
<!-- Test Dependency Versions -->
7574
<junit.version>4.12</junit.version>
@@ -154,12 +153,6 @@
154153
<artifactId>spring-webmvc</artifactId>
155154
<version>${spring.version}</version>
156155
</dependency>
157-
<dependency>
158-
<groupId>org.antlr</groupId>
159-
<artifactId>antlr4-runtime</artifactId>
160-
<version>${antlr.version}</version>
161-
<optional>true</optional>
162-
</dependency>
163156
<!-- Provided dependencies -->
164157
<dependency>
165158
<groupId>org.slf4j</groupId>

0 commit comments

Comments
 (0)