New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add possibility to use http2 preface with https connection #4704
Conversation
Hey, thanks a lot for your PR. I think it makes sense to add a way to deal with an HTTP/2 server that doesn't support ALPN. However, it'd be nice if this new behavior is enabled only when a user explicitly tells so. Currently, Therefore, could you avoid using I also would like to ask the following changes:
|
Wanted to clarify, so I can reuse the Upgrade logic from: |
I think the code you shared looks similar to how I imagined it 👍 |
…er that doesn't support ALPN.
Committed the USE_HTTP2_WITHOUT_ALPN option support and now is time to deal with test cases for it. Is there some similar test case that I could look into? If not I guess I need to start the server with tlsSelfSigned and mock io.netty.handler.ssl.SslHandler#applicationProtocol on the client to simulate negotiation failure. Any thoughts on this? |
You can build a simple HTTP/2 over TLS server using the combination of For example: @RegisterExtension
static SelfSignedCertificateExtension ssc = new SelfSignedCertificateExtension();
@RegisterExtension
static NettyServerExtension h2Server = new NettyServerExtension() {
@Override
protected void configure(Channel ch) throws Exception {
final SslContext sslContext = SslContextBuilder
.forServer(ssc.privateKey(), ssc.certificate())
.build();
ch.pipeline().addLast(new SniHandler(new DomainWildcardMappingBuilder<>(sslContext).build()));
ch.pipeline().addLast(new ApplicationProtocolNegotiationHandler("???") {
@Override
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
ctx.pipeline().addLast(new H2ConnectionHandlerBuilder().build());
}
});
}
};
class H2ConnectionHandler extends SimpleH2CServerHandler {
H2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
Http2Settings initialSettings) {
super(decoder, encoder, initialSettings);
}
}
class H2ConnectionHandlerBuilder
extends AbstractHttp2ConnectionHandlerBuilder<H2ConnectionHandler, H2ConnectionHandlerBuilder> {
@Override
protected H2ConnectionHandler build() {
return super.build();
}
@Override
protected H2ConnectionHandler build(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
Http2Settings initialSettings) throws Exception {
final H2ConnectionHandler handler = new H2ConnectionHandler(decoder, encoder,
initialSettings);
frameListener(handler);
return handler;
}
}
} |
Thanks, this seems to be working for the preface case, but for the upgrade case I tried this and it is not working: import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpServerUpgradeHandler;
import io.netty.handler.codec.http2.*;
static final NettyServerExtension server = new NettyServerExtension() {
@Override
protected void configure(Channel ch) throws Exception {
final SslContext sslContext = SslContextBuilder
.forServer(serverCertificate.privateKey(), serverCertificate.certificate())
.build();
ch.pipeline().addLast(new SniHandler(new DomainWildcardMappingBuilder<>(sslContext).build()));
ch.pipeline().addLast(new ApplicationProtocolNegotiationHandler("???") {
@Override
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
final H2ConnectionHandler handler = new H2ConnectionHandlerBuilder().build();
final HttpServerCodec sourceCodec = new HttpServerCodec();
final HttpServerUpgradeHandler upgradeHandler = new HttpServerUpgradeHandler(sourceCodec, c -> new Http2ServerUpgradeCodec(handler), 65536);
ctx.pipeline().addLast(new LoggingHandler(getClass()));
ctx.pipeline().addLast(upgradeHandler);
}
});
}
};
Do you have any ideas? Currently stuck on it( |
A armeria/core/src/main/java/com/linecorp/armeria/client/HttpClientPipelineConfigurator.java Lines 426 to 432 in f2da76b
It is working for HTTP since UpgradeRequestHandler is added before a channel gets active.However, UpgradeRequestHandler is added after the TLS handshake for HTTPS, so UpgradeRequestHandler can't get a channelActive event. As a workaround, we can use handlerAdded() event for HTTPS.
|
The server we're trying to test should actually not support ALPN, so it's probably better assuming that you write a plaintext HTTP/2 server that supports upgrade requests, and then insert an |
Yes, this worked, thanks, but making working test was not an easy challenge for me) It looks weird but this works: class H2ConnectionHandler extends SimpleH2CServerHandler {
protected final Http2Connection.PropertyKey streamKey;
private final Http2Connection.PropertyKey upgradeKey;
private final InboundHttpToHttp2Adapter adapter;
H2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
Http2Settings initialSettings) {
super(decoder, encoder, initialSettings);
streamKey = connection().newKey();
upgradeKey = connection().newKey();
adapter = new InboundHttpToHttp2Adapter(connection(), this);
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
super.userEventTriggered(ctx, evt);
if(evt instanceof UpgradeEvent) {
UpgradeEvent upgrade = (UpgradeEvent) evt;
try {
ctx.fireUserEventTriggered(upgrade.retain());
Http2Stream stream = connection().stream(HTTP_UPGRADE_STREAM_ID);
upgrade.upgradeRequest().headers().setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), HTTP_UPGRADE_STREAM_ID);
stream.setProperty(upgradeKey, true);
adapter.channelRead(ctx, upgrade.upgradeRequest().retain());
} finally {
upgrade.release();
}
}
}
} Is there any simpler way to achieve this or this is good enough for this test case? |
Understood! Good, then I`ll wait for it, thanks for the clarification. |
Gentle ping @0lejk4 😉 |
Sorry for inconvenience, I am really sorry I couldn't help you in time. I see that you are finishing this on your own. Still glad this feature is coming to life, thank you for your work guys! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
core/src/main/java/com/linecorp/armeria/client/ClientFactoryBuilder.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/client/ClientFactoryOptions.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/client/HttpClientFactory.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/client/HttpClientFactory.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, @0lejk4! 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot! 👍
Thanks, @0lejk4 for adding this feature. 🙇 |
Motivation:
Add the possibility to use http2 preface with HTTPS connection for cases when the server does not support ALPN negotiation
Modifications:
Result: