Skip to content

Commit 6e56db0

Browse files
authored
H2 conversion support OWS for connection header csv (#2400)
Motivation: H2 doesn't support the connection header, and must remove all headers that are referenced in the value of the conneciton header. In the event the connection header contains a CSV we should support OWS.
1 parent 6b17ccd commit 6e56db0

File tree

3 files changed

+49
-12
lines changed

3 files changed

+49
-12
lines changed

servicetalk-http-netty/src/main/java/io/servicetalk/http/netty/H2ToStH1Utils.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,16 +159,20 @@ static Http2Headers h1HeadersToH2Headers(HttpHeaders h1Headers) {
159159
Iterator<? extends CharSequence> connectionItr = h1Headers.valuesIterator(CONNECTION);
160160
if (connectionItr.hasNext()) {
161161
do {
162-
String connectionHeader = connectionItr.next().toString();
162+
CharSequence connectionHeader = connectionItr.next();
163163
connectionItr.remove();
164-
int i = connectionHeader.indexOf(',');
164+
int i = indexOf(connectionHeader, ',', 0);
165165
if (i != -1) {
166166
int start = 0;
167167
do {
168-
h1Headers.remove(connectionHeader.substring(start, i));
168+
h1Headers.remove(connectionHeader.subSequence(start, i));
169169
start = i + 1;
170-
} while (start < connectionHeader.length() && (i = connectionHeader.indexOf(',', start)) != -1);
171-
h1Headers.remove(connectionHeader.substring(start));
170+
// Skip OWS
171+
if (start < connectionHeader.length() && connectionHeader.charAt(start) == ' ') {
172+
++start;
173+
}
174+
} while (start < connectionHeader.length() && (i = indexOf(connectionHeader, ',', start)) != -1);
175+
h1Headers.remove(connectionHeader.subSequence(start, connectionHeader.length()));
172176
} else {
173177
h1Headers.remove(connectionHeader);
174178
}

servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/H2PriorKnowledgeFeatureParityTest.java

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,14 @@
197197
import static org.junit.jupiter.api.Assumptions.assumeTrue;
198198

199199
class H2PriorKnowledgeFeatureParityTest {
200-
201200
private static final CharSequence[] PROHIBITED_HEADERS = {CONNECTION, KEEP_ALIVE, TRANSFER_ENCODING, UPGRADE,
202201
PROXY_CONNECTION};
202+
private static final String CONNECTION_HEADER1 = "conn1";
203+
private static final String CONNECTION_HEADER2 = "conn2";
204+
private static final String CONNECTION_HEADER3 = "conn3";
205+
private static final String CONNECTION_HEADER4 = "conn4";
206+
private static final CharSequence[] CONNECTION_HEADERS = {CONNECTION_HEADER1, CONNECTION_HEADER2,
207+
CONNECTION_HEADER3, CONNECTION_HEADER4};
203208
private static final String EXPECT_FAIL_HEADER = "please_fail_expect";
204209
private static final ContextMap.Key<String> K1 = newKey("k1", String.class);
205210
private static final ContextMap.Key<String> K2 = newKey("k2", String.class);
@@ -1439,7 +1444,7 @@ protected void initChannel(final Channel ch) {
14391444
}, __ -> { }, identity());
14401445
InetSocketAddress serverAddress = (InetSocketAddress) serverAcceptorChannel.localAddress();
14411446
try (BlockingHttpClient client = forSingleAddress(HostAndPort.of(serverAddress))
1442-
.protocols(HttpProtocol.HTTP_2.config)
1447+
.protocols(HttpProtocol.HTTP_2.configOtherHeaderFactory)
14431448
.enableWireLogging("servicetalk-tests-wire-logger", LogLevel.TRACE, () -> true)
14441449
.executionStrategy(clientExecutionStrategy)
14451450
.buildBlocking()) {
@@ -1453,7 +1458,7 @@ protected void initChannel(final Channel ch) {
14531458
void h2LayerFiltersOutProhibitedH1HeadersOnServerSide() throws Exception {
14541459
setUp(DEFAULT, true);
14551460
try (ServerContext serverContext = HttpServers.forAddress(localAddress(0))
1456-
.protocols(HttpProtocol.HTTP_2.config)
1461+
.protocols(HttpProtocol.HTTP_2.configOtherHeaderFactory)
14571462
.enableWireLogging("servicetalk-tests-wire-logger", LogLevel.TRACE, () -> true)
14581463
.listenBlockingAndAwait((ctx, request, responseFactory) -> addProhibitedHeaders(responseFactory.ok()));
14591464
BlockingHttpClient client = forSingleAddress(serverHostAndPort(serverContext))
@@ -1467,11 +1472,21 @@ void h2LayerFiltersOutProhibitedH1HeadersOnServerSide() throws Exception {
14671472
assertThat("Unexpected headerName: " + headerName,
14681473
response.headers().contains(headerName), is(false));
14691474
}
1475+
for (CharSequence headerName : CONNECTION_HEADERS) {
1476+
assertThat("Unexpected headerName: " + headerName,
1477+
response.headers().contains(headerName), is(false));
1478+
}
14701479
}
14711480
}
14721481

14731482
private static <T extends HttpMetaData> T addProhibitedHeaders(T metaData) {
14741483
metaData.addHeader(CONNECTION, UPGRADE)
1484+
.addHeader(CONNECTION, CONNECTION_HEADER1 + "," + CONNECTION_HEADER2)
1485+
.addHeader(CONNECTION, CONNECTION_HEADER3 + ", " + CONNECTION_HEADER4)
1486+
.addHeader(CONNECTION_HEADER1, "foo")
1487+
.addHeader(CONNECTION_HEADER2, "bar")
1488+
.addHeader(CONNECTION_HEADER3, "baz")
1489+
.addHeader(CONNECTION_HEADER4, "boo")
14751490
.addHeader(KEEP_ALIVE, "timeout=5")
14761491
.addHeader(TRANSFER_ENCODING, CHUNKED)
14771492
.addHeader(UPGRADE, "foo/2")
@@ -2072,7 +2087,17 @@ private static boolean allHeadersSanitized(Http2Headers headers) {
20722087
return !headers.contains(HttpHeaderNames.CONNECTION) && !headers.contains(HttpHeaderNames.KEEP_ALIVE)
20732088
&& !headers.contains(HttpHeaderNames.TRANSFER_ENCODING)
20742089
&& !headers.contains(HttpHeaderNames.UPGRADE)
2075-
&& !headers.contains(HttpHeaderNames.PROXY_CONNECTION);
2090+
&& !headers.contains(HttpHeaderNames.PROXY_CONNECTION) &&
2091+
allConnHeadersSanitized(headers);
2092+
}
2093+
2094+
private static boolean allConnHeadersSanitized(Http2Headers headers) {
2095+
for (CharSequence headerName : CONNECTION_HEADERS) {
2096+
if (headers.contains(headerName)) {
2097+
return false;
2098+
}
2099+
}
2100+
return true;
20762101
}
20772102
}
20782103

servicetalk-http-netty/src/test/java/io/servicetalk/http/netty/HttpProtocol.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,25 @@
2121
import java.util.Arrays;
2222
import java.util.Collection;
2323

24+
import static io.servicetalk.http.api.DefaultHttpHeadersFactory.INSTANCE;
2425
import static io.servicetalk.http.api.HttpProtocolVersion.HTTP_1_1;
2526
import static io.servicetalk.http.api.HttpProtocolVersion.HTTP_2_0;
27+
import static io.servicetalk.http.netty.HttpProtocolConfigs.h1;
2628
import static io.servicetalk.http.netty.HttpProtocolConfigs.h1Default;
2729
import static io.servicetalk.http.netty.HttpProtocolConfigs.h2;
2830
import static io.servicetalk.logging.api.LogLevel.TRACE;
2931

3032
enum HttpProtocol {
31-
HTTP_1(h1Default(), HTTP_1_1),
32-
HTTP_2(h2().enableFrameLogging("servicetalk-tests-h2-frame-logger", TRACE, () -> true).build(), HTTP_2_0);
33+
HTTP_1(h1Default(), h1().headersFactory(new H2HeadersFactory(true, true, false)).build(), HTTP_1_1),
34+
HTTP_2(applyFrameLogger(h2()).build(), applyFrameLogger(h2()).headersFactory(INSTANCE).build(), HTTP_2_0);
3335

36+
final HttpProtocolConfig configOtherHeaderFactory;
3437
final HttpProtocolConfig config;
3538
final HttpProtocolVersion version;
3639

37-
HttpProtocol(HttpProtocolConfig config, HttpProtocolVersion version) {
40+
HttpProtocol(HttpProtocolConfig config, HttpProtocolConfig configOtherHeaderFactory, HttpProtocolVersion version) {
3841
this.config = config;
42+
this.configOtherHeaderFactory = configOtherHeaderFactory;
3943
this.version = version;
4044
}
4145

@@ -46,4 +50,8 @@ static HttpProtocolConfig[] toConfigs(Collection<HttpProtocol> protocols) {
4650
static HttpProtocolConfig[] toConfigs(HttpProtocol[] protocols) {
4751
return Arrays.stream(protocols).map(p -> p.config).toArray(HttpProtocolConfig[]::new);
4852
}
53+
54+
private static H2ProtocolConfigBuilder applyFrameLogger(H2ProtocolConfigBuilder builder) {
55+
return builder.enableFrameLogging("servicetalk-tests-h2-frame-logger", TRACE, () -> true);
56+
}
4957
}

0 commit comments

Comments
 (0)