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

x/net/http2: support h2c for http2 #14141

Closed
banks opened this Issue Jan 28, 2016 · 28 comments

Comments

Projects
None yet
@banks

banks commented Jan 28, 2016

Previous discussion: bradfitz/http2#59

h2c (http2 without TLS) is somewhat controversial part of the http2 spec. As discussed in the linked ticket above, major browser vendors are refusing to implement it.

I still think it is important to have first class support in Go standard library though.

The primary reason is that https is an ideal transport for inter-service RPC - c.f. grpc.io which in grpc/grpc-go has to re-implement large parts of http2 client and server transport to work around this issue.

But wanting to use http2 between internal services is not limited to gRPC.

My primary motivation is that I generally want my load balancer to terminate SSL. This is not just to reduce crypto overhead on individual application servers, but can actually be more secure.

To enable perfect forward secrecy, you have to keep tight controls over session ticket keys and rotate them frequently. This is much easier to achieve if only two (or a few) load balancer machines must terminate SSL rather than possibly hundreds or thousands of application servers, not to mention fewer places for an intruder to get hold of those keys.

Of course it's possible to terminate public SSL with perfect forward secrecy at LB and then have backend connections use internally signed certificates and forgo the same stringency, but in this setup TLS is pure overhead, and additional complexity to administer.

So I think it's pretty reasonable that Go, a language whose core competency is building network services, should at least provide a means to serve and consume h2c via the standard library http2 package. I'd even argue that it should be automatic, but if opinion is strong, I'd settle for just being possible without re-implementing large chunks of the http2 transports.

If there is agreement from core (@bradfitz has most insight I'd guess) on how this could be introduced, I'd be really happy to contribute the code. How about if it was a separate package x/net/http2/h2c and for now it's required to manually choose h2c transport in client/server?

If not, this could be built as a (standalone) third party library that anyone who needs this can use for now.

Thanks for you help with this.

@bradfitz

This comment has been minimized.

Show comment
Hide comment
@bradfitz

bradfitz Jan 28, 2016

Member

gRPC reimplemented http2 because they had to. We started with the same http2.Framer code and they wrote their gRPC-specific http2 client & server in parallel with me writing net/http's http2 client and server. I'm currently working on unifying them (see grpc/grpc-go#75).

I might accept h2c as a separate package under x/net/http2, but it won't be part of the Go standard library or on by default. Some of the work was already done as part of golang/net@d62542d

Member

bradfitz commented Jan 28, 2016

gRPC reimplemented http2 because they had to. We started with the same http2.Framer code and they wrote their gRPC-specific http2 client & server in parallel with me writing net/http's http2 client and server. I'm currently working on unifying them (see grpc/grpc-go#75).

I might accept h2c as a separate package under x/net/http2, but it won't be part of the Go standard library or on by default. Some of the work was already done as part of golang/net@d62542d

@bradfitz bradfitz added this to the Unreleased milestone Jan 28, 2016

@banks

This comment has been minimized.

Show comment
Hide comment
@banks

banks Jan 29, 2016

@bradfitz thanks.

gRPC reimplemented http2 because they had to

My thought is that eventually, another framework like gRPC should not have to reimplement http2 because the standard (or at least x/net) package would support all valid protocol use-cases. I realise gRPC came before your implementation so it was not an option there, but long term it seems bad that anyone else (like me) who wants to use HTTP/2 for inter-service communication (according to spec) has to reinvent the wheel despite "HTTP/2" implementation existing in standard lib.

To be clear, for my use-case I'm mostly interested in inter-service communication where I assume I control both client and server, so I really want HTTP/2 over TCP with "prior knowledge" (section 3.4) rather than HTTP/1.1 Upgrade.

But I think it makes sense that there is at least a somewhat official library (even if it stays in x) that supports the whole spec.

I looked at the old PR for upgrade support which seems a little hairy in the way it integrates with http server - I can see it's not a simple request to support.

I might accept h2c as a separate package under x/net/http2, but it won't be part of the Go standard library

Fair enough. Can I ask how the two interact? I'm using 1.6rc which uses http2 automatically when serving TLS via net/http (standard library) but the http2 implementation appears to still be this one in x/net. Do you consider http2 to be "standard library" due to its integration with net/http even though the implementation is still in x/net and still has TODO items on public interface or did I misunderstand something (like a subset got copied over)?

I will take a look through the commit you linked and get a better idea what the options are in terms of optional h2c upgrade and/or prior knowledge support with net/http.

Thanks again for your help.

banks commented Jan 29, 2016

@bradfitz thanks.

gRPC reimplemented http2 because they had to

My thought is that eventually, another framework like gRPC should not have to reimplement http2 because the standard (or at least x/net) package would support all valid protocol use-cases. I realise gRPC came before your implementation so it was not an option there, but long term it seems bad that anyone else (like me) who wants to use HTTP/2 for inter-service communication (according to spec) has to reinvent the wheel despite "HTTP/2" implementation existing in standard lib.

To be clear, for my use-case I'm mostly interested in inter-service communication where I assume I control both client and server, so I really want HTTP/2 over TCP with "prior knowledge" (section 3.4) rather than HTTP/1.1 Upgrade.

But I think it makes sense that there is at least a somewhat official library (even if it stays in x) that supports the whole spec.

I looked at the old PR for upgrade support which seems a little hairy in the way it integrates with http server - I can see it's not a simple request to support.

I might accept h2c as a separate package under x/net/http2, but it won't be part of the Go standard library

Fair enough. Can I ask how the two interact? I'm using 1.6rc which uses http2 automatically when serving TLS via net/http (standard library) but the http2 implementation appears to still be this one in x/net. Do you consider http2 to be "standard library" due to its integration with net/http even though the implementation is still in x/net and still has TODO items on public interface or did I misunderstand something (like a subset got copied over)?

I will take a look through the commit you linked and get a better idea what the options are in terms of optional h2c upgrade and/or prior knowledge support with net/http.

Thanks again for your help.

@banks

This comment has been minimized.

Show comment
Hide comment
@banks

banks Jan 29, 2016

Hmm I see your linked commit is against net/http2 not x/net/http2 implying it does exist in standard library, but if I run godoc on my local 1.6rc http2 is still in x in the Release Candidate despite being integrated with net/http.

banks commented Jan 29, 2016

Hmm I see your linked commit is against net/http2 not x/net/http2 implying it does exist in standard library, but if I run godoc on my local 1.6rc http2 is still in x in the Release Candidate despite being integrated with net/http.

@bradfitz

This comment has been minimized.

Show comment
Hide comment
@bradfitz

bradfitz Jan 29, 2016

Member

We vendor a copy of x/net/http2 into net/http so http2 can be automatic for the standard library. What I mean is that I will not enable h2c automatically for the standard library, but h2c work can happen in a subpackage of x/net, like x/net/http2/h2c.

Member

bradfitz commented Jan 29, 2016

We vendor a copy of x/net/http2 into net/http so http2 can be automatic for the standard library. What I mean is that I will not enable h2c automatically for the standard library, but h2c work can happen in a subpackage of x/net, like x/net/http2/h2c.

@banks

This comment has been minimized.

Show comment
Hide comment
@banks

banks Jan 29, 2016

Thanks Brad, that's clear now.

On 29 Jan 2016, at 18:03, Brad Fitzpatrick notifications@github.com wrote:

We vendor a copy of x/net/http2 into net/http so http2 can be automatic for the standard library. What I mean is that I will not enable h2c automatically for the standard library, but h2c work can happen in a subpackage of x/net, like x/net/http2/h2c.


Reply to this email directly or view it on GitHub.

banks commented Jan 29, 2016

Thanks Brad, that's clear now.

On 29 Jan 2016, at 18:03, Brad Fitzpatrick notifications@github.com wrote:

We vendor a copy of x/net/http2 into net/http so http2 can be automatic for the standard library. What I mean is that I will not enable h2c automatically for the standard library, but h2c work can happen in a subpackage of x/net, like x/net/http2/h2c.


Reply to this email directly or view it on GitHub.

@gopherbot

This comment has been minimized.

Show comment
Hide comment
@gopherbot

gopherbot commented Feb 3, 2016

CL https://golang.org/cl/19176 mentions this issue.

gopherbot pushed a commit to golang/net that referenced this issue Feb 3, 2016

http2: export Server.ServeConn
Fixes golang/go#12737
Updates golang/go#14141

Change-Id: I552b603b63a7c87d7fcdb4eb09f96ab9fd0ec0aa
Reviewed-on: https://go-review.googlesource.com/19176
Reviewed-by: Andrew Gerrand <adg@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
@gopherbot

This comment has been minimized.

Show comment
Hide comment
@gopherbot

gopherbot commented Mar 31, 2016

CL https://golang.org/cl/21327 mentions this issue.

gopherbot pushed a commit that referenced this issue Mar 31, 2016

net/http: allow Handlers to handle http2 upgrade PRI requests
The http2 spec defines a magic string which initates an http2 session:

    "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"

It was intentionally chosen to kinda look like an HTTP request, but
just different enough to break things not ready for it. This change
makes Go ready for it.

Notably: Go now accepts the request header (the prefix "PRI *
HTTP/2.0\r\n\r\n") as a valid request, even though it doesn't have a
Host header. But we now mark it as "Connection: close" and teach the
Server to never read a second request from the connection once that's
seen. If the http.Handler wants to deal with the upgrade, it has to
hijack the request, read out the "body", compare it against
"SM\r\n\r\n", and then speak http2. One of the new tests demonstrates
that hijacking.

Fixes #14451
Updates #14141 (h2c)

Change-Id: Ib46142f31c55be7d00c56fa2624ec8a232e00c43
Reviewed-on: https://go-review.googlesource.com/21327
Reviewed-by: Andrew Gerrand <adg@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
@ayanamist

This comment has been minimized.

Show comment
Hide comment
@ayanamist

ayanamist May 14, 2016

Sorry to bother, but i found it's quite easy to make a h2c client with a little modification to golang.org/x/net/http2

package main

import (
    "crypto/tls"
    "fmt"
    "io/ioutil"
    "log"
    "net"
    "net/http"

    "golang.org/x/net/http2"
)

func main() {
    client := http.Client{
        // Skip TLS dial
        Transport: &http2.Transport{
            DialTLS: func(netw, addr string, cfg *tls.Config) (net.Conn, error) {
                return net.Dial(netw, addr)
            },
        },
    }

    resp, err := client.Get("http://localhost:8080/test")
    if err != nil {
        log.Fatal(err)
        return
    }

    fmt.Printf("resp: %#v\n", resp)

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
        return
    }

    fmt.Println(string(body))
}

then remove the restriction from golang.org/x/net/http2/transport.go in func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error)

    //if req.URL.Scheme != "https" {
    //  return nil, errors.New("http2: unsupported scheme")
    //}

I agree that nearly all browsers will not support h2c, but there are some h2c server serving traffic as another RPC service (not gRPC), so can https restriction be loosen a little to support h2c client?

ayanamist commented May 14, 2016

Sorry to bother, but i found it's quite easy to make a h2c client with a little modification to golang.org/x/net/http2

package main

import (
    "crypto/tls"
    "fmt"
    "io/ioutil"
    "log"
    "net"
    "net/http"

    "golang.org/x/net/http2"
)

func main() {
    client := http.Client{
        // Skip TLS dial
        Transport: &http2.Transport{
            DialTLS: func(netw, addr string, cfg *tls.Config) (net.Conn, error) {
                return net.Dial(netw, addr)
            },
        },
    }

    resp, err := client.Get("http://localhost:8080/test")
    if err != nil {
        log.Fatal(err)
        return
    }

    fmt.Printf("resp: %#v\n", resp)

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
        return
    }

    fmt.Println(string(body))
}

then remove the restriction from golang.org/x/net/http2/transport.go in func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error)

    //if req.URL.Scheme != "https" {
    //  return nil, errors.New("http2: unsupported scheme")
    //}

I agree that nearly all browsers will not support h2c, but there are some h2c server serving traffic as another RPC service (not gRPC), so can https restriction be loosen a little to support h2c client?

@bradfitz

This comment has been minimized.

Show comment
Hide comment
@bradfitz

bradfitz May 16, 2016

Member

I'd be fine with an http2.Transport.AllowHTTPScheme bool knob.

Member

bradfitz commented May 16, 2016

I'd be fine with an http2.Transport.AllowHTTPScheme bool knob.

@ayanamist

This comment has been minimized.

Show comment
Hide comment
@ayanamist

ayanamist May 16, 2016

@bradfitz I'd be fine wth the knob too. So, will it be shipped?

ayanamist commented May 16, 2016

@bradfitz I'd be fine wth the knob too. So, will it be shipped?

@bradfitz

This comment has been minimized.

Show comment
Hide comment
@bradfitz

bradfitz May 16, 2016

Member

If somebody sends me a change.

Member

bradfitz commented May 16, 2016

If somebody sends me a change.

@ayanamist

This comment has been minimized.

Show comment
Hide comment
@ayanamist

ayanamist May 17, 2016

@bradfitz It seems golang.org/x/net/http2 not hosted on github?

Here is a diff. Since http port is not 443, i add some if-else to use 80

diff --git a/http2/configure_transport.go b/http2/configure_transport.go
index daa17f5..dde578a 100644
--- a/http2/configure_transport.go
+++ b/http2/configure_transport.go
@@ -32,7 +32,7 @@ func configureTransport(t1 *http.Transport) (*Transport, error) {
        t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1")
    }
    upgradeFn := func(authority string, c *tls.Conn) http.RoundTripper {
-       addr := authorityAddr(authority)
+       addr := authorityAddr(authority, "https")
        if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil {
            go c.Close()
            return erringRoundTripper{err}
diff --git a/http2/transport.go b/http2/transport.go
index a7ada8f..a9f24dd 100644
--- a/http2/transport.go
+++ b/http2/transport.go
@@ -75,6 +75,9 @@ type Transport struct {
    // explicitly requested gzip it is not automatically
    // uncompressed.
    DisableCompression bool
+   
+   // Allow plain text HTTP/2
+   AllowHTTPScheme bool

    // MaxHeaderListSize is the http2 SETTINGS_MAX_HEADER_LIST_SIZE to
    // send in the initial settings frame. It is how many bytes
@@ -268,20 +271,27 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {

 // authorityAddr returns a given authority (a host/IP, or host:port / ip:port)
 // and returns a host:port. The port 443 is added if needed.
-func authorityAddr(authority string) (addr string) {
+func authorityAddr(authority string, scheme string) (addr string) {
    if _, _, err := net.SplitHostPort(authority); err == nil {
        return authority
    }
-   return net.JoinHostPort(authority, "443")
+   var defaultPort string
+   if scheme == "http" {
+       defaultPort = "80"
+   } else {
+       defaultPort = "443"
+   }
+   return net.JoinHostPort(authority, defaultPort)
 }

 // RoundTripOpt is like RoundTrip, but takes options.
 func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) {
-   if req.URL.Scheme != "https" {
+   if !(req.URL.Scheme == "https" || (req.URL.Scheme == "http" && t.AllowHTTPScheme)) {
        return nil, errors.New("http2: unsupported scheme")
    }

-   addr := authorityAddr(req.URL.Host)
+    
+   addr := authorityAddr(req.URL.Host, req.URL.Scheme)
    for {
        cc, err := t.connPool().GetClientConn(req, addr)
        if err != nil {

ayanamist commented May 17, 2016

@bradfitz It seems golang.org/x/net/http2 not hosted on github?

Here is a diff. Since http port is not 443, i add some if-else to use 80

diff --git a/http2/configure_transport.go b/http2/configure_transport.go
index daa17f5..dde578a 100644
--- a/http2/configure_transport.go
+++ b/http2/configure_transport.go
@@ -32,7 +32,7 @@ func configureTransport(t1 *http.Transport) (*Transport, error) {
        t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1")
    }
    upgradeFn := func(authority string, c *tls.Conn) http.RoundTripper {
-       addr := authorityAddr(authority)
+       addr := authorityAddr(authority, "https")
        if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil {
            go c.Close()
            return erringRoundTripper{err}
diff --git a/http2/transport.go b/http2/transport.go
index a7ada8f..a9f24dd 100644
--- a/http2/transport.go
+++ b/http2/transport.go
@@ -75,6 +75,9 @@ type Transport struct {
    // explicitly requested gzip it is not automatically
    // uncompressed.
    DisableCompression bool
+   
+   // Allow plain text HTTP/2
+   AllowHTTPScheme bool

    // MaxHeaderListSize is the http2 SETTINGS_MAX_HEADER_LIST_SIZE to
    // send in the initial settings frame. It is how many bytes
@@ -268,20 +271,27 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {

 // authorityAddr returns a given authority (a host/IP, or host:port / ip:port)
 // and returns a host:port. The port 443 is added if needed.
-func authorityAddr(authority string) (addr string) {
+func authorityAddr(authority string, scheme string) (addr string) {
    if _, _, err := net.SplitHostPort(authority); err == nil {
        return authority
    }
-   return net.JoinHostPort(authority, "443")
+   var defaultPort string
+   if scheme == "http" {
+       defaultPort = "80"
+   } else {
+       defaultPort = "443"
+   }
+   return net.JoinHostPort(authority, defaultPort)
 }

 // RoundTripOpt is like RoundTrip, but takes options.
 func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) {
-   if req.URL.Scheme != "https" {
+   if !(req.URL.Scheme == "https" || (req.URL.Scheme == "http" && t.AllowHTTPScheme)) {
        return nil, errors.New("http2: unsupported scheme")
    }

-   addr := authorityAddr(req.URL.Host)
+    
+   addr := authorityAddr(req.URL.Host, req.URL.Scheme)
    for {
        cc, err := t.connPool().GetClientConn(req, addr)
        if err != nil {
@bradfitz

This comment has been minimized.

Show comment
Hide comment
@bradfitz

bradfitz May 17, 2016

Member

@ayanamist, we don't accept patches on Github or in comments. See https://golang.org/doc/contribute.html#Code_review

That is a wall of text, but there's only like 3 real steps there.

Member

bradfitz commented May 17, 2016

@ayanamist, we don't accept patches on Github or in comments. See https://golang.org/doc/contribute.html#Code_review

That is a wall of text, but there's only like 3 real steps there.

@gopherbot

This comment has been minimized.

Show comment
Hide comment
@gopherbot

gopherbot commented May 17, 2016

CL https://golang.org/cl/23181 mentions this issue.

@ejholmes

This comment has been minimized.

Show comment
Hide comment
@ejholmes

ejholmes Aug 25, 2016

Since CL https://golang.org/cl/23181 was merged, what is the correct way to configure the server side for this?

Given this server:

package main

import (
    "fmt"
    "io"
    "log"
    "net/http"

    "golang.org/x/net/http2"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Println(r)
    io.WriteString(w, "hello\n")
}

func main() {
    s := &http.Server{
        Addr:    ":8543",
        Handler: http.HandlerFunc(handler),
    }
    http2.ConfigureServer(s, &http2.Server{})
    log.Fatal(s.ListenAndServe())
}

And this client:

package main

import (
    "crypto/tls"
    "fmt"
    "log"
    "net"
    "net/http"

    "golang.org/x/net/http2"
)

func main() {
    client := http.Client{
        // Skip TLS dial
        Transport: &http2.Transport{
            AllowHTTP: true,
            DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
                return net.Dial(network, addr)
            },
        },
    }

    resp, err := client.Get("http://localhost:8543")
    if err != nil {
        log.Fatal(fmt.Errorf("error making request: %v", err))
    }
    fmt.Println(resp.StatusCode)
    fmt.Println(resp.Proto)
}

When the client makes a request, the output is:

$ go run client/main.go
2016/08/24 22:31:07 error making request: Get http://localhost:8543: unexpected EOF
exit status 1

And the server output from the handler is:

$ go run server/main.go
&{PRI * HTTP/2.0 2 0 map[] 0x3bd4e0 -1 [] true  map[] map[] <nil> map[] [::1]:63792 * <nil> <nil> <nil> 0xc4200744c0}

If I switch the server to use http2.Server.ServeConn directly, like in the tests in the above CL, it works as expected.

ejholmes commented Aug 25, 2016

Since CL https://golang.org/cl/23181 was merged, what is the correct way to configure the server side for this?

Given this server:

package main

import (
    "fmt"
    "io"
    "log"
    "net/http"

    "golang.org/x/net/http2"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Println(r)
    io.WriteString(w, "hello\n")
}

func main() {
    s := &http.Server{
        Addr:    ":8543",
        Handler: http.HandlerFunc(handler),
    }
    http2.ConfigureServer(s, &http2.Server{})
    log.Fatal(s.ListenAndServe())
}

And this client:

package main

import (
    "crypto/tls"
    "fmt"
    "log"
    "net"
    "net/http"

    "golang.org/x/net/http2"
)

func main() {
    client := http.Client{
        // Skip TLS dial
        Transport: &http2.Transport{
            AllowHTTP: true,
            DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
                return net.Dial(network, addr)
            },
        },
    }

    resp, err := client.Get("http://localhost:8543")
    if err != nil {
        log.Fatal(fmt.Errorf("error making request: %v", err))
    }
    fmt.Println(resp.StatusCode)
    fmt.Println(resp.Proto)
}

When the client makes a request, the output is:

$ go run client/main.go
2016/08/24 22:31:07 error making request: Get http://localhost:8543: unexpected EOF
exit status 1

And the server output from the handler is:

$ go run server/main.go
&{PRI * HTTP/2.0 2 0 map[] 0x3bd4e0 -1 [] true  map[] map[] <nil> map[] [::1]:63792 * <nil> <nil> <nil> 0xc4200744c0}

If I switch the server to use http2.Server.ServeConn directly, like in the tests in the above CL, it works as expected.

@tamird

This comment has been minimized.

Show comment
Hide comment
@tamird

tamird Aug 25, 2016

Contributor

I believe this is due to how http2 is implemented in the standard library: https://github.com/golang/go/blob/release-branch.go1.7/src/net/http/server.go#L1500:L1520

HTTP2 dispatch is implemented in net/http.Server just like any other ALPN-based dispatch, which means it only works with TLS connections (ALPN is part of the TLS spec).

It would take some additional non-ALPN logic to make h2c work the way you describe, and to my understanding @bradfitz has indicated that such additional logic will not be accepted.

Contributor

tamird commented Aug 25, 2016

I believe this is due to how http2 is implemented in the standard library: https://github.com/golang/go/blob/release-branch.go1.7/src/net/http/server.go#L1500:L1520

HTTP2 dispatch is implemented in net/http.Server just like any other ALPN-based dispatch, which means it only works with TLS connections (ALPN is part of the TLS spec).

It would take some additional non-ALPN logic to make h2c work the way you describe, and to my understanding @bradfitz has indicated that such additional logic will not be accepted.

@bradfitz

This comment has been minimized.

Show comment
Hide comment
@bradfitz

bradfitz Aug 25, 2016

Member

I've already accepted a ton of logic for h2c. My policy has been and remains: it should be possible for people to do h2c via a separate package, but it won't be supported by default.

All the pieces should be there now for you to glue it together yourself.

For server-side support you'll need to hijack in the PRI * HTTP/2.0 Handler and then call http2.Server.ServerConn.

Somebody should make a package to make that easier. I have other priorities, but can review CLs.

Member

bradfitz commented Aug 25, 2016

I've already accepted a ton of logic for h2c. My policy has been and remains: it should be possible for people to do h2c via a separate package, but it won't be supported by default.

All the pieces should be there now for you to glue it together yourself.

For server-side support you'll need to hijack in the PRI * HTTP/2.0 Handler and then call http2.Server.ServerConn.

Somebody should make a package to make that easier. I have other priorities, but can review CLs.

@ejholmes

This comment has been minimized.

Show comment
Hide comment
@ejholmes

ejholmes Aug 26, 2016

Somebody should make a package to make that easier. I have other priorities, but can review CLs.

Awesome, thanks @bradfitz. Would the Go team accept a CL to add h2c support, if it was opt-in (e.g. an AllowH2C flag on Server)?

I'd imagine a lot of people writing backend services are pretty excited about the performance advantages of http/2, but would rather terminate TLS at a load balancer.

Thanks for all your hard work on http/2 thus far!

ejholmes commented Aug 26, 2016

Somebody should make a package to make that easier. I have other priorities, but can review CLs.

Awesome, thanks @bradfitz. Would the Go team accept a CL to add h2c support, if it was opt-in (e.g. an AllowH2C flag on Server)?

I'd imagine a lot of people writing backend services are pretty excited about the performance advantages of http/2, but would rather terminate TLS at a load balancer.

Thanks for all your hard work on http/2 thus far!

@bradfitz

This comment has been minimized.

Show comment
Hide comment
@bradfitz

bradfitz Sep 8, 2016

Member

Awesome, thanks @bradfitz. Would the Go team accept a CL to add h2c support, if it was opt-in (e.g. an AllowH2C flag on Server)?

I'd prefer it to be in its own package, hidden away and very much opt-in.

Member

bradfitz commented Sep 8, 2016

Awesome, thanks @bradfitz. Would the Go team accept a CL to add h2c support, if it was opt-in (e.g. an AllowH2C flag on Server)?

I'd prefer it to be in its own package, hidden away and very much opt-in.

@mdempsky

This comment has been minimized.

Show comment
Hide comment
@mdempsky

mdempsky Sep 8, 2016

Member
import "cellar/stairs/lavatory/cabinet/leopard/h2c"
Member

mdempsky commented Sep 8, 2016

import "cellar/stairs/lavatory/cabinet/leopard/h2c"
@agirbal

This comment has been minimized.

Show comment
Hide comment
@agirbal

agirbal Dec 31, 2016

@ejholmes it's been a few months, any update on getting h2c to work with http.Server? I think it'd be very useful to have plain http2 as an option for testing and backend services. Thanks

agirbal commented Dec 31, 2016

@ejholmes it's been a few months, any update on getting h2c to work with http.Server? I think it'd be very useful to have plain http2 as an option for testing and backend services. Thanks

@cstrahan

This comment has been minimized.

Show comment
Hide comment
@cstrahan

cstrahan Jul 26, 2017

To save others some time, @hkwi wrote an implementation here: https://github.com/hkwi/h2c

cstrahan commented Jul 26, 2017

To save others some time, @hkwi wrote an implementation here: https://github.com/hkwi/h2c

@ldemailly ldemailly referenced this issue Nov 6, 2017

Closed

fortio improvements #2

7 of 14 tasks complete

c3mb0 pushed a commit to c3mb0/net that referenced this issue Apr 2, 2018

http2: export Server.ServeConn
Fixes golang/go#12737
Updates golang/go#14141

Change-Id: I552b603b63a7c87d7fcdb4eb09f96ab9fd0ec0aa
Reviewed-on: https://go-review.googlesource.com/19176
Reviewed-by: Andrew Gerrand <adg@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
@misterwilliam

This comment has been minimized.

Show comment
Hide comment
@misterwilliam

misterwilliam May 8, 2018

I'm pretty far in getting this done. I expect I will be able submit a PR this weekend.

misterwilliam commented May 8, 2018

I'm pretty far in getting this done. I expect I will be able submit a PR this weekend.

@gopherbot

This comment has been minimized.

Show comment
Hide comment
@gopherbot

gopherbot May 13, 2018

Change https://golang.org/cl/112997 mentions this issue: Add implementation of h2c into x/net/http2/h2c.

gopherbot commented May 13, 2018

Change https://golang.org/cl/112997 mentions this issue: Add implementation of h2c into x/net/http2/h2c.

@gopherbot

This comment has been minimized.

Show comment
Hide comment
@gopherbot

gopherbot May 13, 2018

Change https://golang.org/cl/112999 mentions this issue: http2/h2c: Implementation of http2 h2c.

gopherbot commented May 13, 2018

Change https://golang.org/cl/112999 mentions this issue: http2/h2c: Implementation of http2 h2c.

@veqryn

This comment has been minimized.

Show comment
Hide comment
@veqryn

veqryn Jul 20, 2018

I too wanted h2c for traffic between load balancers that were doing SSL Termination and my golang servers.

I adapted the h2c implementation in Traefik into a standalone http.Handler: https://github.com/veqryn/h2c

package main

import (
	"fmt"
	"net/http"

	"github.com/veqryn/h2c"
	"golang.org/x/net/http2"
)

func main() {

	// Router/Mux (can use any http.Handler)
	router := http.NewServeMux()

	// Handlers...
	router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "Hello World")
	})

	// Wrap the Router
	h2cWrapper := &h2c.HandlerH2C{
		Handler:  router,
		H2Server: &http2.Server{},
	}

	// Server
	srv := http.Server{
		Addr:    ":8080",
		Handler: h2cWrapper,
	}

	srv.ListenAndServe()
}

veqryn commented Jul 20, 2018

I too wanted h2c for traffic between load balancers that were doing SSL Termination and my golang servers.

I adapted the h2c implementation in Traefik into a standalone http.Handler: https://github.com/veqryn/h2c

package main

import (
	"fmt"
	"net/http"

	"github.com/veqryn/h2c"
	"golang.org/x/net/http2"
)

func main() {

	// Router/Mux (can use any http.Handler)
	router := http.NewServeMux()

	// Handlers...
	router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "Hello World")
	})

	// Wrap the Router
	h2cWrapper := &h2c.HandlerH2C{
		Handler:  router,
		H2Server: &http2.Server{},
	}

	// Server
	srv := http.Server{
		Addr:    ":8080",
		Handler: h2cWrapper,
	}

	srv.ListenAndServe()
}
@mpuncel

This comment has been minimized.

Show comment
Hide comment
@mpuncel

mpuncel Sep 4, 2018

I'm happy to see that there is now an h2c package in golang.org/x/net/http2/h2c! I would like to add a random data point however that there are legitimate use cases for h2c. In our case, when proxying gRPC through a local Envoy sidecar over a unix socket protected with filesystem permissions. For reasons like that having it in the standard library might be nice, but of course should not be a default. The existing conclusion also is reasonable.

mpuncel commented Sep 4, 2018

I'm happy to see that there is now an h2c package in golang.org/x/net/http2/h2c! I would like to add a random data point however that there are legitimate use cases for h2c. In our case, when proxying gRPC through a local Envoy sidecar over a unix socket protected with filesystem permissions. For reasons like that having it in the standard library might be nice, but of course should not be a default. The existing conclusion also is reasonable.

@glerchundi

This comment has been minimized.

Show comment
Hide comment
@glerchundi

glerchundi Sep 5, 2018

I would like to say THANK YOU @misterwilliam (and @bradfitz for reviewing), fantastic job.

glerchundi commented Sep 5, 2018

I would like to say THANK YOU @misterwilliam (and @bradfitz for reviewing), fantastic job.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment