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
=htc Support UseHttp2.Negotiated
for http2 connections over plain http
#2543
The head ref may contain hidden characters: "wip-prior-knowledge-\u221A"
Conversation
…with Prior Knowledge on the same server instance
Test PASSed. |
Related to #2085 (a more common but arguably less elegant way to support both http 1 and 2 on the same unencrypted port) |
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.
First look: looks good, couple of comments here and there. Haven't looked closely at the changes to the main Http2
class yet, but at first glance it seems like a pretty neat cut!
val http2Enabled = settings.previewServerSettings.enableHttp2 && connectionContext.http2 != Never | ||
val http2Forced = connectionContext.http2 == Always | ||
if (http2Enabled && (connectionContext.isSecure || http2Forced)) { | ||
val http2Enabled = true || settings.previewServerSettings.enableHttp2 && connectionContext.http2 != Never |
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.
don't forget to remove this ;)
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.
haha, good catch! :D
@@ -17,4 +17,5 @@ object UseHttp2 { | |||
def always: UseHttp2 = Always | |||
def negotiated: UseHttp2 = Negotiated | |||
def never: UseHttp2 = Never | |||
def priorKnowledge: UseHttp2 = PriorKnowledge |
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.
When this PR and #1966 are both merged, it would be useful to be able to support h2c via upgrade and h2c with prior knowledge on the same port.
Perhaps negotiated
should enable h2c via 'upgrade' (since it doesn't add overhead), and priorKnowledge
should enable both h2c mechanisms? That behavior seems reasonable, but then perhaps the naming needs some more thought?
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.
Perhaps consolidate that behavior once both PRs are merged? (to make them independent)
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.
sure, just throwing this out there since finding a better name now is nicer than changing it (or not ;) ) later ;)
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.
@raboof I didn't find any preexisting good place to write tests for this feature, is there somewhere you'd recommend?
akka-http2-support/src/main/scala/akka/http/impl/engine/http2/PriorKnowledgeSwitch.scala
Outdated
Show resolved
Hide resolved
akka-http2-support/src/main/scala/akka/http/impl/engine/http2/PriorKnowledgeSwitch.scala
Show resolved
Hide resolved
install(http2Stack, data) | ||
else | ||
install(http1Stack, data) | ||
} else if (data.isEmpty || data.startsWith(PRIOR_KNOWLEDGE_PREFACE, 0)) { // Still unknown |
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.
data.startsWith(PRIOR_KNOWLEDGE_PREFACE, 0)
will always be false here, since data.length >= PRIOR_KNOWLEDGE_PREFACE.length
was already false
, I think?
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.
(looks like the intent was to check whether the data received so far matches the preface, so we can switch to http1 as soon as we see a mismatch - which makes sense)
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.
@raboof Indeed!
|
||
/** INTERNAL API */ | ||
@InternalApi | ||
private[http] object PriorKnowledgeSwitch { |
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.
Seems pretty verbose for what it does, but I can't really think of a more straightforward way either.
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.
I'd be more than happy to cut down the noise here, open to all suggestions!
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.
Since it is mostly copied and adapted from AlpnSwitch, it would be nice if we could abstract it somehow.
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.
@jrudolph I thought about that, but then again, the AlpnSwitch deals with Bidi and this works with Flow, so I think the correct move in that case would be to abstract each separately so that you'd be reusable for other stages. That does seem orthogonal to this PR though?
Test PASSed. |
Test FAILed. |
Test PASSed. |
Test PASSed. |
It doesn't seem pressing to merge it right now, so I'd rather improve it first and then merge it, instead of adding a rarely-used feature to our pile of code we have no capacity to maintain. Especially, the public API part needs to be figured out, since |
@jrudolph Well, I need it for StateServ: cloudstateio/cloudstate@a6b6b70 |
Just for discussion, I don't think we should do this. #2543 (comment) I'm not sure we should do this, as `Negotiated` is the default, and adding the `PriorKnowledgeSwitch` might have a performance penalty on top of 'plain' HTTP/1. For the same reason I'm not sure we should roll it into `Always`.
Just for discussion, I'm not sure we should do this. #2543 (comment) I'm hesitant because `Negotiated` is the default, and adding the `PriorKnowledgeSwitch` might have a performance penalty on top of 'plain' HTTP/1. For the same reason I'm not sure we should roll it into `Always`.
Just for discussion, I'm not sure we should do this. #2543 (comment) I'm hesitant because `Negotiated` is the default, and adding the `PriorKnowledgeSwitch` might have a performance penalty on top of 'plain' HTTP/1. For the same reason I'm not sure we should roll it into `Always`.
Test PASSed. |
I'd like to have a try at removing the code duplication in the switches. |
@jrudolph Ok, cool, let me know if you want me to review it! |
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.
From my perspective we could merge this as is, I have a follow up PR ready to clean things up a bit.
UseHttp2.Negotiated
for http2 connections over plain http
Thanks, @viktorklang, that was some quick turnaround on that feature. |
Thanks @jrudolph, I couldn't have completed it in such short amount of time without the great advice and contributions from the Akka team! ^^ |
install(http2Stack, data) | ||
else | ||
install(http1Stack, data) | ||
} else if (data.isEmpty || data.startsWith(HTTP2_CONNECTION_PREFACE, 0)) { // Still unknown |
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.
Shouldn't this be HTTP2_CONNECTION_PREFACE.startsWith(data, 0)
? data.startsWith(HTTP2_CONNECTION_PREFACE, 0)
will always evaluate to false, since we know that data.length < HTTP2_CONNECTION_PREFACE.length
.
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.
Jup, I'll PR
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.
We can fix that in my PR directly.
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.
Wow—nicely spotted, James!
--
Cheers,
√
|
…ttp (akka#2543) By looking ahead and deciding on the prior knowledge preface a potential HTTP/2 client sends whether to use HTTP/1.1 or HTTP/2. Co-Authored-By: Arnout Engelen <github@bzzt.net> Co-Authored-By: Johannes Rudolph <johannes.rudolph@gmail.com>
Purpose
If we want to support things like serving unencrypted gRPC traffic from the same server instance as unencrypted REST traffic—we already support serving encrypted gRPC traffic and REST traffic from the same server as is with UseHttp2.ALWAYS—then the server logic needs to be changed.
This draft PR adds support for a new UseHttp2 setting called PriorKnowledge,
this allows for serving non-tls http requests for both HTTP1.1 and HTTP2.0 on the same server/port.
If an incoming request sends the HTTP2.0 PRI preamble we service that request through an HTTP2 pipeline, otherwise we serve it though the HTTP1.1 pipeline.
References
References #2523
Changes
Adds a PriorKnowledgeSwitch in the same vein as AlpnSwitch, and then sniffs the initial bytes of each request to match against the HTTP2 Prior Knowledge preamble.
Background Context
@raboof and @jrudolph pointed me in this direction.
This feature, assuming approval by the maintainers, will be getting a test suite to verify the implementation. Current testing has been done with
curl
to validate that it works with existing, external, clients.use the curl flag
--http2-prior-knowledge
to have curl send the HTTP2 Prior Knowledge preamble with the request.