Skip to content

Commit 9f467f4

Browse files
committed
Support Clear Text Http protocol negotiation
1 parent 4e39324 commit 9f467f4

File tree

5 files changed

+311
-115
lines changed

5 files changed

+311
-115
lines changed

driver/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
<copyright>Copyright (c) 2011, 2022 Oracle and/or its affiliates. All rights reserved.</copyright>
4747
<java.apidoc>http://docs.oracle.com/javase/8/docs/api</java.apidoc>
4848
<maven.deploy.skip>false</maven.deploy.skip>
49-
<netty.version>4.1.77.Final</netty.version>
49+
<netty.version>4.1.82.Final</netty.version>
5050
<jackson.version>2.12.5</jackson.version>
5151
<bouncy.version>1.70</bouncy.version>
5252
<!-- by default, skip tests; tests require a profile -->

driver/src/main/java/oracle/nosql/driver/httpclient/HttpClient.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,15 @@ public int getFreeChannelCount() {
379379
return pool.getFreeChannels();
380380
}
381381

382+
/**
383+
* Check if "h2" is in the protocols list
384+
*
385+
* @return true if "h2" is in the protocols list
386+
*/
387+
public boolean useHttp2() {
388+
return this.httpProtocols.contains(ApplicationProtocolNames.HTTP_2);
389+
}
390+
382391
/* available for testing */
383392
ConnectionPool getConnectionPool() {
384393
return pool;

driver/src/main/java/oracle/nosql/driver/httpclient/HttpClientChannelPoolHandler.java

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.netty.channel.pool.ChannelHealthChecker;
2424
import io.netty.channel.pool.ChannelPoolHandler;
2525
import io.netty.handler.proxy.HttpProxyHandler;
26+
import io.netty.handler.ssl.ApplicationProtocolNames;
2627
import io.netty.handler.ssl.SslHandler;
2728
import io.netty.handler.ssl.SslHandshakeCompletionEvent;
2829
import io.netty.util.concurrent.Future;
@@ -48,6 +49,62 @@ public class HttpClientChannelPoolHandler implements ChannelPoolHandler,
4849
this.client = client;
4950
}
5051

52+
private void configureSSL(Channel ch) {
53+
ChannelPipeline p = ch.pipeline();
54+
/* Enable hostname verification */
55+
final SslHandler sslHandler = client.getSslContext().newHandler(
56+
ch.alloc(), client.getHost(), client.getPort());
57+
final SSLEngine sslEngine = sslHandler.engine();
58+
final SSLParameters sslParameters = sslEngine.getSSLParameters();
59+
sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
60+
sslEngine.setSSLParameters(sslParameters);
61+
sslHandler.setHandshakeTimeoutMillis(client.getHandshakeTimeoutMs());
62+
63+
p.addLast(sslHandler);
64+
p.addLast(new ChannelLoggingHandler(client));
65+
// Handle ALPN protocol negotiation result, and configure the pipeline accordingly
66+
p.addLast(new HttpProtocolNegotiationHandler(
67+
client.getHttpFallbackProtocol(), new HttpClientHandler(client.getLogger()),
68+
client.getMaxChunkSize(), client.getMaxContentLength(), client.getLogger()));
69+
}
70+
71+
private void configureClearText(Channel ch) {
72+
ChannelPipeline p = ch.pipeline();
73+
HttpClientHandler handler = new HttpClientHandler(client.getLogger());
74+
75+
// Only true when HTTP_2 is the only protocol, as if user set:
76+
// config.setHttpProtocols(ApplicationProtocolNames.HTTP_2);
77+
if (client.useHttp2() &&
78+
ApplicationProtocolNames.HTTP_2.equals(client.getHttpFallbackProtocol())) {
79+
// If choose to use H2 and fallback is also H2
80+
// Then there is no need to upgrade from Http1.1 to H2C
81+
// Directly connects with H2 protocol, so called Http2-prior-knowledge
82+
HttpUtil.configureHttp2(ch.pipeline(), client.getMaxContentLength());
83+
p.addLast(handler);
84+
return;
85+
}
86+
87+
// Only true when HTTP_1_1 is the only protocol, as if user set:
88+
// config.setHttpProtocols(ApplicationProtocolNames.HTTP_1_1);
89+
if (!client.useHttp2() &&
90+
ApplicationProtocolNames.HTTP_1_1.equals(client.getHttpFallbackProtocol())) {
91+
HttpUtil.configureHttp1(ch.pipeline(), client.getMaxChunkSize(), client.getMaxContentLength());
92+
p.addLast(handler);
93+
return;
94+
}
95+
96+
// Only true when both HTTP_2 and HTTP_1_1 are available, the default option:
97+
// config.setHttpProtocols(ApplicationProtocolNames.HTTP_2,
98+
// ApplicationProtocolNames.HTTP_1_1)
99+
if (client.useHttp2() &&
100+
ApplicationProtocolNames.HTTP_1_1.equals(client.getHttpFallbackProtocol())) {
101+
HttpUtil.configureH2C(ch.pipeline(), client.getMaxChunkSize(), client.getMaxContentLength());
102+
p.addLast(handler);
103+
return;
104+
}
105+
throw new IllegalStateException("unknown protocol: " + client.getHttpProtocols());
106+
}
107+
51108
/**
52109
* Initialize a channel with handlers that:
53110
* 1 -- handle and HTTP
@@ -62,25 +119,10 @@ public void channelCreated(Channel ch) {
62119
logFine(client.getLogger(),
63120
"HttpClient " + client.getName() + ", channel created: " + ch
64121
+ ", acquired channel cnt " + client.getAcquiredChannelCount());
65-
ChannelPipeline p = ch.pipeline();
66122
if (client.getSslContext() != null) {
67-
/* Enable hostname verification */
68-
final SslHandler sslHandler = client.getSslContext().newHandler(
69-
ch.alloc(), client.getHost(), client.getPort());
70-
final SSLEngine sslEngine = sslHandler.engine();
71-
final SSLParameters sslParameters = sslEngine.getSSLParameters();
72-
sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
73-
sslEngine.setSSLParameters(sslParameters);
74-
sslHandler.setHandshakeTimeoutMillis(client.getHandshakeTimeoutMs());
75-
76-
p.addLast(sslHandler);
77-
p.addLast(new ChannelLoggingHandler(client));
78-
// Handle ALPN protocol negotiation result, and configure the pipeline accordingly
79-
p.addLast(new HttpProtocolNegotiationHandler(
80-
client.getHttpFallbackProtocol(), new HttpClientHandler(client.getLogger()), client.getMaxChunkSize(),
81-
client.getMaxContentLength(), client.getLogger()));
123+
configureSSL(ch);
82124
} else {
83-
// TODO: H2C upgrade
125+
configureClearText(ch);
84126
}
85127

86128
if (client.getProxyHost() != null) {
@@ -94,7 +136,7 @@ public void channelCreated(Channel ch) {
94136
client.getProxyUsername(),
95137
client.getProxyPassword());
96138

97-
p.addFirst("proxyServer", proxyHandler);
139+
ch.pipeline().addFirst("proxyServer", proxyHandler);
98140
}
99141
}
100142

driver/src/main/java/oracle/nosql/driver/httpclient/HttpProtocolNegotiationHandler.java

Lines changed: 4 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
package oracle.nosql.driver.httpclient;
99

10-
import static io.netty.handler.logging.LogLevel.DEBUG;
1110
import static oracle.nosql.driver.util.LogUtil.logFine;
1211

1312
import java.net.SocketAddress;
@@ -16,18 +15,8 @@
1615

1716
import io.netty.channel.ChannelHandlerContext;
1817
import io.netty.channel.ChannelOutboundHandler;
19-
import io.netty.channel.ChannelPipeline;
2018
import io.netty.channel.ChannelPromise;
21-
import io.netty.handler.codec.http.HttpClientCodec;
2219
import io.netty.handler.codec.http.HttpMessage;
23-
import io.netty.handler.codec.http.HttpObjectAggregator;
24-
import io.netty.handler.codec.http2.DefaultHttp2Connection;
25-
import io.netty.handler.codec.http2.DelegatingDecompressorFrameListener;
26-
import io.netty.handler.codec.http2.Http2Connection;
27-
import io.netty.handler.codec.http2.Http2FrameLogger;
28-
import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandler;
29-
import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder;
30-
import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder;
3120
import io.netty.handler.ssl.ApplicationProtocolNames;
3221
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
3322
import io.netty.util.internal.RecyclableArrayList;
@@ -43,10 +32,6 @@
4332
* 5. {@link HttpProtocolNegotiationHandler} removes itself from the pipeline. Writes any buffered {@link HttpMessage} to the channel.
4433
*/
4534
public class HttpProtocolNegotiationHandler extends ApplicationProtocolNegotiationHandler implements ChannelOutboundHandler {
46-
private static final Http2FrameLogger frameLogger = new Http2FrameLogger(DEBUG, HttpProtocolNegotiationHandler.class);
47-
48-
private static final String CODEC_HANDLER_NAME = "http-codec";
49-
private static final String AGG_HANDLER_NAME = "http-aggregator";
5035
private static final String HTTP_HANDLER_NAME = "http-client-handler";
5136

5237
private final Logger logger;
@@ -64,53 +49,12 @@ public HttpProtocolNegotiationHandler(String fallbackProtocol, HttpClientHandler
6449
this.maxContentLength = maxContentLength;
6550
}
6651

67-
private void writeBufferedMessages(ChannelHandlerContext ctx) {
68-
if (!this.bufferedMessages.isEmpty()) {
69-
for(int i = 0; i < this.bufferedMessages.size(); ++i) {
70-
Pair<Object, ChannelPromise> p = (Pair<Object, ChannelPromise>)this.bufferedMessages.get(i);
71-
ctx.channel().write(p.first, p.second);
72-
}
73-
74-
this.bufferedMessages.clear();
75-
}
76-
this.bufferedMessages.recycle();
77-
}
78-
79-
private void configureHttp1(ChannelHandlerContext ctx) {
80-
ChannelPipeline p = ctx.pipeline();
81-
82-
p.addLast(CODEC_HANDLER_NAME,
83-
new HttpClientCodec(4096, // initial line
84-
8192, // header size
85-
maxChunkSize)); // chunksize
86-
p.addLast(AGG_HANDLER_NAME,
87-
new HttpObjectAggregator(maxContentLength));
88-
}
89-
90-
private void configureHttp2(ChannelHandlerContext ctx) {
91-
ChannelPipeline p = ctx.pipeline();
92-
93-
Http2Connection connection = new DefaultHttp2Connection(false);
94-
HttpToHttp2ConnectionHandler connectionHandler = new HttpToHttp2ConnectionHandlerBuilder()
95-
.frameListener(new DelegatingDecompressorFrameListener(
96-
connection,
97-
new InboundHttp2ToHttpAdapterBuilder(connection)
98-
.maxContentLength(this.maxContentLength)
99-
.propagateSettings(false)
100-
.build()))
101-
.frameLogger(frameLogger)
102-
.connection(connection)
103-
.build();
104-
105-
p.addLast(connectionHandler);
106-
}
107-
10852
@Override
10953
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
11054
if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
111-
configureHttp2(ctx);
55+
HttpUtil.configureHttp2(ctx.pipeline(), this.maxContentLength);
11256
} else if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) {
113-
configureHttp1(ctx);
57+
HttpUtil.configureHttp1(ctx.pipeline(), this.maxChunkSize, this.maxContentLength);
11458
} else {
11559
throw new IllegalStateException("unknown http protocol: " + protocol);
11660
}
@@ -126,7 +70,7 @@ protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
12670
@Override
12771
public void write(ChannelHandlerContext ctx, Object o, ChannelPromise channelPromise) throws Exception {
12872
if (o instanceof HttpMessage) {
129-
Pair<Object, ChannelPromise> p = Pair.of(o, channelPromise);
73+
HttpUtil.Pair<Object, ChannelPromise> p = HttpUtil.Pair.of(o, channelPromise);
13074
this.bufferedMessages.add(p);
13175
return;
13276
}
@@ -142,7 +86,7 @@ public void write(ChannelHandlerContext ctx, Object o, ChannelPromise channelPro
14286
@Override
14387
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
14488
super.handlerRemoved(ctx);
145-
this.writeBufferedMessages(ctx);
89+
HttpUtil.writeBufferedMessages(ctx, this.bufferedMessages);
14690
}
14791

14892
@Override
@@ -180,41 +124,5 @@ public void flush(ChannelHandlerContext ctx) {
180124
ctx.flush();
181125
}
182126

183-
private static class Pair<A, B> {
184-
185-
public final A first;
186-
public final B second;
187-
188-
public Pair(A fst, B snd) {
189-
this.first = fst;
190-
this.second = snd;
191-
}
192-
193-
public String toString() {
194-
return "Pair[" + first + "," + second + "]";
195-
}
196-
197-
public boolean equals(Object other) {
198-
if (other instanceof Pair<?, ?>) {
199-
Pair<?,?> pair = (Pair<?,?>) other;
200-
return Objects.equals(first, pair.first) &&
201-
Objects.equals(second, pair.second);
202-
}
203-
return false;
204-
}
205-
206-
public int hashCode() {
207-
if (first == null)
208-
return (second == null) ? 0 : second.hashCode() + 1;
209-
else if (second == null)
210-
return first.hashCode() + 2;
211-
else
212-
return first.hashCode() * 17 + second.hashCode();
213-
}
214-
215-
public static <A, B> Pair<A, B> of(A a, B b) {
216-
return new Pair<>(a, b);
217-
}
218-
}
219127
}
220128

0 commit comments

Comments
 (0)