On 07 Aug 2026, at 15:05, Steffen <[email protected]> wrote:

> Developing an HTTP3 module for Apache HTTPD is something that is desperately 
> needed, noting alternative web servers already offer HTTP3 support, e.g. 
> NGINX and Caddy.

This is very cool work, happy to help you.

> Whilst there's evidence of people actively working towards this, e.g. 
> https://github.com/machine-moon/mod_http3, it's evidently not possible with 
> the current 2.4.x code branch. To that end PR#699 
> https://github.com/apache/httpd/pull/699 has been raised, as a workaround to 
> allow (some) MPM's to note connections that are not TCP based.
> 
> The following post provides a detailed insight to these issues - 
> https://codeit.guru/apache-http3-pr-699-mpm-quic-backport-2-4-68

Not had a chance to look at the code yet, but some comments on the post above.

Brain dump to follow.

"The pull request proposes two optional MPM functions"

Optional functions come with the limitation that only one module can grab them. 
Ideally instead of optional functions they should be hooks, this allows more 
than one module to be present and do potentially do work in different ways.

One place where httpd definitely needs a hook is receiving socket descriptors.

Right now there is code hardcoded in httpd core that is aware of systemd for 
example, which can pass a pre-opened socket to httpd. That really should be a 
hook that is called when some code in a module has a socket ready. The core can 
be pure "open TCP or UDP", while an extra module can be systemd.

This means systemd can pass UDP sockets to mod_http3 in future and it just 
works.

"Every successful call to: ap_mpm_note_extra_connection_added() must eventually 
have exactly one matching call to: ap_mpm_note_extra_connection_removed()"

This is largely impractical, it is pretty much guaranteed that an error path 
will be mishandled at some point, which will lead to instability in the server.

The way httpd handles this is through the use of memory pools (arenas), each 
unit of work in httpd has its own pool. When the unit of work is finished, 
because it succeeded or failed, doesn't matter, the memory is cleaned up as a 
single unit, along with any extra resources like connection handles etc.

https://apr.apache.org/docs/apr/1.7/group___pool_cleanup.html

Pools are hierarchical, for example a request is created from a connection. If 
the request ends for whatever reason, run apr_pool_destroy(r->pool) will make 
that request go away. If the connection dies for whatever reason, run 
apr_pool_destroy(c->pool) and the connection, along with any requests currently 
in progress, will all go away.

Sometimes you might have a relationship between things that aren't nested, like 
an LDAP connection pool. The LDAP connection pool lives alongside HTTP 
requests. While the LDAP connection is being used by a request, a cleanup is 
registered with the request to return the LDAP connection to the pool at the 
end. No matter what happens to the request, sanity returns to the LDAP 
connection pool.

What all this means is that you need just one call, 
ap_mpm_note_extra_connection_added(), which is passed a pool, and it's the job 
of your code to register a cleanup in that pool.

"The MPM owns the process lifecycle, but protocol modules may own additional 
connection lifecycles and report them through a defined API."

We have the option to rethink how nghttp3 and httpd mesh with one another, and 
given certain limitations inside the core, this is likely to be necessary.

Thinking out loud, the first hook needed is the hook that allows us to open TCP 
or UDP sockets, either natively or from systemd, at or around make_sock() here 
https://github.com/apache/httpd/blob/trunk/server/listen.c#L72

Then each MPM would need a hook to pass UDP packets to. This hook might use 
ngtcp2 or openssl to decode the packets, and then pass to a hook that does 
nghttp3.

nghttp3 would the make a conn_rec out of nghttp3_conn, and then make a 
request_rec out of the callbacks for headers.

The entry point where the request is processed is possibly here at 
‎ap_process_async_request‎():

https://github.com/apache/httpd/blob/trunk/modules/http/http_request.c#L406

Alternatively, if the above function has too many side effects, run these three 
hooks ap_run_quick_handler‎(), ap_process_request_internal‎(), and 
ap_invoke_handler‎() one after the other and process the return code 
appropriately:

https://github.com/apache/httpd/blob/trunk/modules/http/http_request.c#L447
https://github.com/apache/httpd/blob/trunk/modules/http/http_request.c#L449
https://github.com/apache/httpd/blob/trunk/modules/http/http_request.c#L451

You would need to replace the connection filters with a filter that injects 
data into the request, and then allows writing of data from the request.

The filter would return EAGAIN until all the request is read, and unlike 
ap_process_async_request‎(), you would pass more data in and keep going until 
that hook is happy.

Some work might be required to make sure things are properly re-entrant, but 
that's doable and reasonable I think.

At the end of ap_invoke_handler‎() you will have buckets ready to be written, 
you can write those buckets to the network and process the next request. Make 
sure you limit the number of requests in flight like this lest you run out of 
file handles.

> We believe it would be really helpful if PR#699 could be approved for 
> subsequent 2.4.x releases, and equally whether similar functionality could be 
> added to mpm_winnt, so support for HTTP3 could be developed for Windows 
> platforms too.

Windows has its own http3 implementation called MsQuic, if the hooks above are 
created carefully it should be possible to switch in MsQuic instead of 
ngtcp2/nghttp3.

Regards,
Graham
--

Reply via email to