-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Fixes #8979 - Jetty 12 - HttpClientTransport network "modes". #11368
Fixes #8979 - Jetty 12 - HttpClientTransport network "modes". #11368
Conversation
* Introduced oej.io.TransportProtocol as the abstraction for the low-level transport of high-level protocols. Now protocols such as HTTP/1.1 or HTTP/2 can be transported over QUIC, Unix-Domain, memory, and possibly over other low-level custom protocols too. * Introduced oej.client.Request.transportProtocol(TransportProtocol) to specify TransportProtocol for each request. * Introduced TransportProtocol to [HTTP2Client|HTTP3Client].connect(...) methods. * Introduced [Client|Server]QuicConfiguration so that it can be used in other Connectors such as MemoryConnector. * Introduced oej.server.MemoryConnector and EndPoint.Pipe for memory communication between peers, along with a MemoryTransportProtocol. * Introduced QuicTransportProtocol as a wrapper for other TransportProtocols, so that QUIC can now also be transported over memory. Signed-off-by: Simone Bordet <[email protected]>
* Removed usage of ClientConnector.forUnixDomain() from FastCGIProxyServlet (ee10 and ee9). * Replaced usage of HTTP3ServerConnector with QuicServerConnector in jetty-http3.xml. Signed-off-by: Simone Bordet <[email protected]>
…-http3.xml. Signed-off-by: Simone Bordet <[email protected]>
* Updated client documentation to consider TransportProtocol. Signed-off-by: Simone Bordet <[email protected]>
* @return the peer address that sent the data, or {@link #EOF} | ||
* @throws IOException if the receive fails | ||
*/ | ||
default SocketAddress receive(ByteBuffer buffer) throws IOException |
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.
Is this blocking?
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.
hmmm few of the existing methods have javadoc making it clear their blocking behaviour. We should fix this .
jetty-core/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryEndPointPipe.java
Show resolved
Hide resolved
Signed-off-by: Simone Bordet <[email protected]>
@sbordet Are there any instructions how to build this locally for testing? Local Maven build fails with the same errors than the CI. |
@marchof build locally with The CI failure is due to javadocs, I'm fixing it right now. |
Signed-off-by: Simone Bordet <[email protected]>
@sbordet Thanks for this effort! Finally I got a super simple prototype running with a naive
|
@marchof you should not be needing to implement a new If you explain your use case in more details, perhaps we can help you save some unnecessary work. |
@sbordet Let me try to explain my use case: A proxy server which proxies incoming HTTPS requests over SSH tunnels to HTTP servers. The SSH server managing the tunnels itself is implemented in Java with Apache Mina. So if implemented in Jetty I see the following flow:
|
@marchof let me try to write a test case similar to your use case. I'll ping you when done. |
@marchof I need more information. Are you using SSH port forwarding? If so, then this is what I think it happens:
Does Mina allow you to bypass the connection to |
@sbordet Yes, I'm using SSH port forwarding but without using sockets on the server side. This avoids the network stack and allows me to separate the forwards of different users. The forwarding between http hosts and ssh sockets is pure virtual and happens in memory. Therefore I cannot use a HTTP client which connects to a (local) TCP port. Apache Mina supports this, you can basically implement your own forwarding endpoints. The use something similar to |
@marchof thanks, we'll write a test case for this scenario. |
Now first clear the list, then notify to avoid that when re-entering the same instruction is notified multiple times. Signed-off-by: Simone Bordet <[email protected]>
Introduced ContentSourceRequestContent, and updated ProxyHandler to use it. Signed-off-by: Simone Bordet <[email protected]>
@marchof see |
...ports/src/test/java/org/eclipse/jetty/test/client/transport/CustomTransportProtocolTest.java
Outdated
Show resolved
Hide resolved
Now unix-domain files are temp files, so their name is unique. Signed-off-by: Simone Bordet <[email protected]>
HTTP3Client http3Client = new HTTP3Client(quicConfiguration, connector); | ||
ClientConnectionFactoryOverHTTP3.HTTP3 http3 = new ClientConnectionFactoryOverHTTP3.HTTP3(http3Client); | ||
|
||
// The order of the protocols indicates the client's preference. |
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.
Can you please clarify (in the documentation and test code) whether the order is from the most preferred to least preferred, or the other way around? In the following line, does the client prefer HTTP/1 first, HTTP/2 second and HTTP/3 third?
Is it technically possible to flip that around and have it try HTTP/3, followed by HTTP/2, followed by HTTP/1? Or is this not possible because the connection type needs to be upgraded through the earlier versions?
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.
@cowwoc clarified the preference.
It is possible to reverse the order.
However, since HTTP/3 is not compatible with HTTP/2 and HTTP/1, there is no way to "downgrade" the attempt (and switch from UDP to TCP), so if the server does not support HTTP/3 the request will fail.
Applications may handle the failure and attempt to specify explicitly the HTTP version via request.version(...)
.
This is not done automatically by HttpClient
.
The reverse order may be useful if you want your default to be HTTP/3, but you want to explicitly specify HTTP/2 or HTTP/1 for those servers you know don't support HTTP/3.
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.
@sbordet if the order of preference is HTTP/1, HTTP/2, HTTP/3 does that mean that the client will settle on HTTP/1 if the server supports all three protocols? Or will it try to upgrade the connection to HTTP/3?
I suspect that the client will settle on HTTP/1. If this is the case, how could one configure the client to try HTTP/3 and if that fails try HTTP/1 followed by an upgrade to HTTP/2? And if the upgrade to HTTP/2 fails then stay on HTTP/1.
I may be wrong, but this sounds like the happy path to me.
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.
HTTP/2 does not usually work with upgrades, it's negotiated via TLS + ALPN.
A client configured with http/1.1, h2
will send that to the server, and the server will likely pick h2
because it's the server that decides.
Trying HTTP/3 with a server that does not support it will result in a failure after the connect timeout (differently from TCP, where the failure is almost immediate).
After that timeout, the application may try explicitly HTTP/2.
That's why it's probably most common to do the opposite: if you know the server supports HTTP/3, you explicitly try that; otherwise you are not explicit and therefore HTTP/1.1+HTTP/2 will be negotiated.
Likely, as of now, the best configuration for the client is therefore: HTTP/2, HTTP/1.1, HTTP/3.
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.
As usual, nothing is simple in life :) Check out this interesting discussion related to HTTP/3 negotiation: https://stackoverflow.com/a/77408325/14731 and https://stackoverflow.com/a/67470164/14731
I assume that if support is added for these approaches then users can move from the order HTTP/2, HTTP/1.1, HTTP/3 to HTTP/3, HTTP/2, HTTP/1.1.
I'm not sure what Jetty can do with the Alt-Svc
approach though. In the case of browsers, they say that future connections will then try HTTP/3 first but in the case of Jetty can the HttpClient
instance do the same? Meaning, the first connection would try HTTP/2, HTTP/1.1, HTTP/3 but if HTTP/3 support is found then subsequent connections to the same host would try HTTP/3 first.
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.
@sbordet I guess the only thing I'm wondering is whether:
- I can start out with an HTTP/2, HTTP/1.1, HTTP/3 preference.
- Upon making the first call to a hostname, take note whether the server response contains a
Alt-Svc: h3="<port number>
header. - Configure Jetty to use a different order of preference for that host in future calls.
If Jetty doesn't provide a way to set preferences on a per-host basis, one thing I could do is create one HttpClient
per preference order (one normal, one that prefers HTTP/3) and use application-level logic to issue requests with one or the other depending on whether the host is known to support HTTP/3.
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.
Bullet 3 should be: Explicitly set the HTTP version on the request via request.version(HttpVersion.HTTP_3)
.
That's it.
The option of configuring different HttpClient
instances is not one I would suggest.
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.
I got HTTPS/3 working on the server side but I can't get past the dreaded "PKIX path building failed" exception on the client side. Even hitting "google.com" fails with this error. I'll let you know once I figure this out.
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.
Signed-off-by: Simone Bordet <[email protected]>
…rk-modes'. Signed-off-by: Simone Bordet <[email protected]>
Signed-off-by: Simone Bordet <[email protected]>
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.
Besides EndPoint.Pipe
which I think isn't needed as I do not think we're going to add other pipe connectors in the future, LGTM.
/** | ||
* <p>A communication conduit between two peers.</p> | ||
*/ | ||
interface Pipe |
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'm not sure we need this interface in the IO API.
Signed-off-by: Simone Bordet <[email protected]>
…es'. Signed-off-by: Simone Bordet <[email protected]>
…meResolution(). Marked HttpDestination constructor as deprecated for removal. Signed-off-by: Simone Bordet <[email protected]>
oej.io.TransportProtocol
as the abstraction for the low-level transport of high-level protocols. Now protocols such as HTTP/1.1 or HTTP/2 can be transported over QUIC, Unix-Domain, memory, and possibly over other low-level custom protocols too.oej.client.Request.transportProtocol(TransportProtocol)
to specifyTransportProtocol
for each request.TransportProtocol
to[HTTP2Client|HTTP3Client].connect(...)
methods.[Client|Server]QuicConfiguration
so that it can be used in other Connectors such asMemoryConnector
.oej.server.MemoryConnector
andEndPoint.Pipe
for memory communication between peers, along with aMemoryTransportProtocol
.QuicTransportProtocol
as a wrapper for otherTransportProtocol
s, so that QUIC can now also be transported over memory.