caddyserver / caddy

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
https://caddyserver.com
Apache License 2.0
55.71k stars 3.92k forks source link

Add http/2 cleartext server (h2c) support #3227

Closed Zetanova closed 4 years ago

Zetanova commented 4 years ago

The normal case is always to encrypt http2 network connections, but since there are cases in gRPC and service-meshes where the upstream is over an unix domain stocket (UDS), http/2 cleartext (h2c) would be required.

In a TLS connection the client can negate the h2 protocol with the help of TLS-ALPN header.

But in the case of http/2 cleartext (h2c) there are two different methods: 1) h2c upgrade 2) h2c prior knowledge

The golang native server can negate it already by inserting the h2c middleware Handler from golang.org/x/net/http2/h2c with h2c.NewHandler(handler, h2s) h2c-golang-example

By simply extending the listener factory caddy2 could then serve h2c connections by h2c update and also at the same time by h2c prior knowledge

I think, the h2c-handler can be added here: https://github.com/caddyserver/caddy/blob/84c729e96a32a52a41195fe3414fd0801e779f34/modules/caddyhttp/app.go#L188-L217

If required http1 coud be disabled to accept only h2c-prior-knowledge, but then caddy would need to bind the http2 server directly to the tcp-connection like

l, err := net.Listen("tcp", "0.0.0.0:1010")
checkErr(err, "while listening")

fmt.Printf("Listening [0.0.0.0:1010]...\n")
for {
    conn, err := l.Accept()
    checkErr(err, "during accept")

    server.ServeConn(conn, &http2.ServeConnOpts{
        Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            fmt.Fprintf(w, "Hello, %v, http: %v", r.URL.Path, r.TLS == nil)
        }),
    })
}

Because the h2c-handler covers both options, this feature could also be omitted.

Maybe the h2c handler can be added by extending the JSON Config Structure › apps › http › servers › listener_wrappers

Demo Caddyfile for UDS

http://

bind unix//tmp/caddy.sock

reverse_proxy {
    to unix//temp/upstream.sock
    transport http {
        versions h2c
    }
}

Demo Caddyfile for TCP

http://

bind 127.0.0.1

reverse_proxy {
    to http://127.0.0.1:2012
    transport http {
        versions h2c
    }
}
mholt commented 4 years ago

To clarify:

service-meshes where the upstream is over an unix domain stocket (UDS), http/2 cleartext (h2c) would be required.

The socket used (UDS or otherwise) has nothing to do with whether TLS is used. It's a totally separate thing.

@Zetanova Also, I don't believe we need to use the method with server.ServeConn() nor mess with listeners at all, since the h2c.NewHandler() method supports both upgrade and prior knowledge.

Zetanova commented 4 years ago

@mholt yes, the method with server.ServeConn()should not be required. Most UDS stream setups are unencrypted.

mholt commented 4 years ago

@Zetanova Alrighty, I have pushed experimental support for h2c server in the h2c branch: b61e1a2

It is enabled by setting "insecure_h2c": true in the server struct (so, for example, adjacent to "listen"). (Sorry, didn't have time to whip up Caddyfile support yet.)

As before, I haven't tested it; will you try it out and see how it goes?

Zetanova commented 4 years ago

@mholt thx a lot!

looks good, the listener is working.

But the gRPC-response error/bug from the other new "h2c upstream" feature still breaks the gRPC communication. I hope that it will work with an upstream over normal tcp-h2

Test setup: [gloang gRPC client] <- uds-h2c -> [caddy] <- tcp-h2c -> [kestrel gRPC server]

mholt commented 4 years ago

@Zetanova

But the gRPC-response error/bug from the other new "h2c upstream" feature still breaks the gRPC communication.

Do you mean this one?

Zetanova commented 4 years ago

@mholt No, this one

the gRPC Response in caddy gets somehow to transfered correctly back to the gRPC-client Behavior the same with [golang gRPC client] <h2c> [caddy] <h2c> [dotnet gRPC server] and [dotnet gRPC client] <h2|h2c> [caddy] <h2c> [dotnet gRPC server]

i will test now [dotnet gRPC client] <h2|h2c> [caddy] <h2> [dotnet gRPC server]

mholt commented 4 years ago

Gotcha...

I'm afraid I don't know enough about gRPC to debug that one :( I'll need some hints to make much more progress. (Hopefully it is simple!)

Zetanova commented 4 years ago

@mholt gRPC info both gRPC clients (dotnet and golang) complaining about a missing response status code

Listening with h2c looks like to work now! So this feature looks to be implemented.

I will write about the gRPC Response the the other git-issue