net/http - How to listen to HTTP/2 only ?

1,281 views
Skip to first unread message

Jean-François Giorgi

unread,
Dec 5, 2022, 9:38:51 AM12/5/22
to golang-dev
hi
I'm trying to setup a Go HTTP server that only accepts HTTP/2 connections (ie don't accept http/1.1 in the APLN).

I'm using "nmap --script=tls-alpn -p  server_port server_ip" to display accepted alpn protocots.

This doesn't seem possible without extensive rewrite of the net/http lib. Any ideas ?

thx

Eli Lindsey

unread,
Dec 5, 2022, 10:43:00 AM12/5/22
to Jean-François Giorgi, golang-dev
Hi,

This is likely a more appropriate question for golang-nuts@. 🙂 

net/http is fairly 1.1 focused. There may be a way to disable 1.1 in net/http that I’m not aware of and someone else can chime in about, but one option that definitely works if you want a strictly http2 server is adding a dependency on x/net (which net/http uses internally) and creating/using an http2.Server directly.

-eli

--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-dev/4fb21341-131c-4a51-acbd-50f7f99dac25n%40googlegroups.com.

Jean-François Giorgi

unread,
Dec 5, 2022, 1:35:31 PM12/5/22
to golang-dev
I posted here mainly because there is no "ServeTLS" for http2.Server  in x/net/http2. So unless I'm mistaken, we can't really use directly and simply http2.Server. 
The only entry point to using http2.Server is ServeConn which must be called from a TLSNextProto function of a http.Server (which makes sense).

http2.ConfigureServer can be used for that but it also adds "http/1.1" after "h2" ( why ?! )
Then calling ServeTLS also add "http/1.1" if not found (?!).

So the only workaround I found is by duplicating the code of ServeTLS:
  - call http2.ConfigureServer (which add "h2" but also "http/1.1" ?! to ALPN) 
  - remove "http1.1"  from TLSConfig.NextProtos 
  - set up the TLS listener
  - call Serve on that TLS listener

This seems to work but doesn't fully replicate the net/http ServeTLS function. We also need to set http2.Server.NewWriteScheduler as it is set in net/http/Server.setupHTTP2_ServeTLS.

This isn't very future proof as any bug fixes/changes to net/http/Server.ServeTLS need to be backported but I understand this was done mainly to add HTTP/2 to the existing net/http 1.1 server rather then be a "HTTP/2 standalone server" implementation.

Eli Lindsey

unread,
Dec 5, 2022, 2:51:31 PM12/5/22
to Jean-François Giorgi, golang-dev
So unless I'm mistaken, we can't really use directly and simply http2.Server. 

(apologies, ugly pseudo code follows)

l, _ := tls.NewListener(…)
s := http2.Server{...}
for {
    c, _ := l.Accept()
    go s.ServeConn(c …)
}

Did you go down that route and hit obstacles?

net/http.Server is really an http1.1 server with optional http2 support. I don’t think you should or need to use it at all for what you’re trying to accomplish, and that’s probably part of what’s making your workaround so roundabout (setting then unsetting ALPNs, etc.).

We also need to set http2.Server.NewWriteScheduler as it is set in net/http/Server.setupHTTP2_ServeTLS.

For what it’s worth, http.Server is setting NewWriteScheduler to what http2.Server already defaults to. You may need to add handshake timeout handling to the above accept loop, but there shouldn’t be much from http.ServeTLS that needs to carry forward.

None of this is to discourage writing a proposal for making it more straightforward to stand up http2-only servers; as a user that’d be useful to me too! But I do think current APIs support this, if less conveniently than http.Server.

-eli

Jean-François Giorgi

unread,
Dec 6, 2022, 6:43:42 AM12/6/22
to golang-dev
> Did you go down that route and hit obstacles?
no but afaik it's basically like rewriting net/http/Server.Serve accept loop.

since Serve works if we rewrite our own ServeTLS we will go that route for now.

I do agree a new more configurable ServeTLS would be nice to have, may be if/when HTTP/3 will be also supported by net/http ( https://github.com/golang/go/issues/32204 ) ?

Thx for your time.

Jean-François Giorgi

unread,
Dec 6, 2022, 8:27:54 AM12/6/22
to golang-dev
> since Serve works if we rewrite our own ServeTLS we will go that route for now.

actually, I spoke too early ! After testing a real case, the server still fallbacks to http/1.x if  the negotiated ALPN  protocol is empty.


There is no distinction between "no ALPN" and "ALPN with no negotiated protocol". In both cases , the code fallbacks to http/1.x
I don't know the rfc about this behavior, CuRL doesn't complain and accept the fallback (but with -v we see  "ALPN, server did not agree to a protocol")
We can't even add our own handlers for http/1.x in  TLSNextProto because the code forbid that (see validNextProto function)...

So we have no choice but to rewrite our own accept loop (duplicate Serve and remove the fallback).

a future proposal could be to remove the restriction imposed by validNextProto.

Robert Engels

unread,
Dec 6, 2022, 9:00:15 AM12/6/22
to Jean-François Giorgi, golang-dev
You’re probably aware but the http2 faq has a section on implementing http2 without http1 https://http2.github.io/faq/

On Dec 6, 2022, at 7:28 AM, Jean-François Giorgi <jfgi...@gmail.com> wrote:

> since Serve works if we rewrite our own ServeTLS we will go that route for now.

Jean-François Giorgi

unread,
Dec 7, 2022, 10:52:49 AM12/7/22
to Robert Engels, golang-dev
thx. I didn't know about this faq.

> implementing http2 without http1 https://http2.github.io/faq/
"For HTTP/2 over TLS (h2), if you do not implement the http1.1 ALPN identifier, then you will not need to support any HTTP/1.1 features."

That is what we're trying to do in Go using net/http but we can't directly because the std lib always implements http1.x even if we remove it from the ALPN negotiation.
If the client doesn't do ALPN or doesn't negotiate H2 then we want our server to refuse the connection.

I understand the Go std lib has such a fallback to avoid breaking old code/bad clients and to guarantee backward compatibility.

So maybe, we need a simple new proposal: a new boolean field 'EnforceALPN' in the http.Server struct and change the code near where validNextProto is called.

But it wouldn't work because of another thing: according to NextProtos ( https://pkg.go.dev/crypto/tls#Config.NextProtos ) , "the connection will fail if there is no mutually supported protocol"
yet it doesn't. 
Looking further, we found that there is a 'hardcoded exception' in Go crypto/tls stb lib, function negotiateALPN (see: https://cs.opensource.google/go/go/+/refs/tags/go1.19.4:src/crypto/tls/handshake_server.go;l=281

I just don't understand why this was done in crypto/tls which should be protocol agnostic...or at least it should have been done with a way to disable it (by adding a new field flag to tls.Config for instance).

To make it work the proposal should also change crypto/tls.

Robert Engels

unread,
Dec 7, 2022, 11:36:33 AM12/7/22
to Jean-François Giorgi, golang-dev
If the Go http2 support does not adhere to the “spec” it is arguably a bug. I would expect the api proposal to be backwards compatible so it should receive little friction. 

On Dec 7, 2022, at 9:52 AM, Jean-François Giorgi <jfgi...@gmail.com> wrote:


Reply all
Reply to author
Forward
0 new messages