On Fri, Mar 27, 2026 at 10:00 AM Oleg Kalnichevski <[email protected]> wrote:

> There is absolutely no guarantee there is a way to build a reasonable
> HTTP/2 transport using classic i/o given its nature and inherent
> limitations. If one ends up having to have a dedicated thread per H2
> stream it is not going to work and scale well no matter how cheap
> virtual threads are.
>

I agree that HTTP/2 is the sticking point due multiplexing and support for
full-duplex streaming. However, keep in mind that Go implements HTTP/2
exactly as you're describing. There's a connection-level read loop that
does blocking frame reads and dispatches frames to the appropriate stream:

https://github.com/golang/go/blob/4f62ef0625628e52d56779803805ca125ce34230/src/net/http/internal/http2/transport.go#L2097

And the write path similarly spawns a goroutine that writes the request and
then waits for the response:

https://github.com/golang/go/blob/4f62ef0625628e52d56779803805ca125ce34230/src/net/http/internal/http2/transport.go#L1310

This doesn't translate directly to Java since Go's `select` statement is
inherently multiplexed and can block on multiple channels simultaneously
(closest thing we have is `CompletableFuture.anyOf()` from Java 9), but
it's an interesting proof-of-concept for an HTTP/2 implementation built
entirely on stackful coroutines.

We also have the option of retaining the event loop for connection-level
code, while migrating higher-level code in the async client (such as route
establishment) to the blocking implementation used by the classic
transport. The point is that virtual threads are a huge opportunity to
consolidate the classic and async implementations, and we're mainly limited
by the performance of virtual threads compared to the callback-based
implementation.

I understand that Amazon has no interest in our server-side components.
> That is fine. This proposal however directly conflicts with the
> project's charter however outdated it is these days. You will have to
> start off by drafting a new charter, getting it voted upon and
> approved. One should also consider dropping `HttpComponents` name as no
> longer reflective of the project scope and purpose and re-branding it
> back as `HttpClient` as it originally was prior to the year 2005.
>

If people are getting use out of the server-side components then I don't
want to drop them. What I *do* want is to make the core-client boundary
easier to maintain, if possible. We have a lot of heavily overloaded
methods and constructors and so on in order to preserve core's ABI
compatibility with older versions of client. I'm not sure what to do about
this.

Would the existing charter allow us to consolidate core and client into a
single repository and release train? Lockstep versioning of the core and
client modules might allow us to relax backwards compatibility for
the @Internal APIs in core.

In this case one should also _seriously_ consider dropping the entire
> core module except for protocol code and porting it to Netty.
> Rationally that would be the best decision to ensure there is a future
> for the project long-term. At the same you would likely need to attract
> new people to the project as some of the old project members may not be
> willing to stick around.


Some degree of Netty integration is definitely worth considering. However,
a Netty dependency will increase CVE churn, both for the project itself and
its users. I see, and sometimes deal with, regular fire drills for Netty,
Tomcat, etc., so I appreciate that HttpComponents doesn't create additional
work for me.

The best of both worlds would be optional Netty integration, provided
through a custom transport layer facility (kind of a generalization
of DetachedSocketFactory). This would allow us to still ship a secure,
portable, pure Java client with a minimal dependency closure, while also
providing optional access to advanced features that might never be exposed
through Java's abstractions. The work on UDS and Windows named pipes is
instructive regarding some of the difficulties here: the classic/async
split prevents code reuse; `java.net.Socket` is almost impossible to
extend; a `Selector` can't mix JDK and third party SocketChannels; etc.

Reply via email to