• Stars
    star
    110
  • Rank 315,591 (Top 7 %)
  • Language
    Go
  • Created over 5 years ago
  • Updated 6 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Example HTTP/2 Cleartext (H2C) server and client in golang

HTTP/2 Cleartext (H2C) golang example

Since the internet failed me, and the only workable example of a H2C client I can find was in the actual go code test suite I'm going to lay out what I discovered about H2C support in golang here.

First is that the standard golang code supports HTTP2 but does not directly support H2C. H2C support only exists in the golang.org/x/net/http2/h2c package. You can make your HTTP server H2C capable by wrapping your handler or mux with h2c.NewHandler() like so.

h2s := &http2.Server{}

handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %v, http: %v", r.URL.Path, r.TLS == nil)
})

server := &http.Server{
    Addr:    "0.0.0.0:1010",
    Handler: h2c.NewHandler(handler, h2s),
}

fmt.Printf("Listening [0.0.0.0:1010]...\n")
checkErr(server.ListenAndServe(), "while listening")

The above code allows the server to support H2C upgrade and H2C prior knowledge along with standard HTTP/2 and HTTP/1.1 that golang natively supports.

If you don't care about supporting HTTP/1.1 then you can run this code which only supports H2C prior knowledge.

server := http2.Server{}

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)
        }),
    })
}

Once you have a running server you can test your server by installing curl-openssl.

$ brew install curl-openssl

# Add curl-openssl to the front of your path
$ export PATH="/usr/local/opt/curl-openssl/bin:$PATH"

You can now use curl to test your H2C enabled server like so.

Connect via HTTP1.1 then upgrade to HTTP/2 (H2C)
curl -v --http2 http://localhost:1010
*   Trying ::1:1010...
* TCP_NODELAY set
* Connected to localhost (::1) port 1010 (#0)
> GET / HTTP/1.1
> Host: localhost:1010
> User-Agent: curl/7.65.0
> Accept: */*
> Connection: Upgrade, HTTP2-Settings
> Upgrade: h2c
> HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 101 Switching Protocols
< Connection: Upgrade
< Upgrade: h2c
* Received 101
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 200
< content-type: text/plain; charset=utf-8
< content-length: 20
< date: Wed, 05 Jun 2019 19:01:40 GMT
<
* Connection #0 to host localhost left intact
Hello, /, http: true
Connect via HTTP/2 (H2C)
curl -v --http2-prior-knowledge http://localhost:1010
*   Trying ::1:1010...
* TCP_NODELAY set
* Connected to localhost (::1) port 1010 (#0)
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fdab8007000)
> GET / HTTP/2
> Host: localhost:1010
> User-Agent: curl/7.65.0
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 200
< content-type: text/plain; charset=utf-8
< content-length: 20
< date: Wed, 05 Jun 2019 19:00:43 GMT
<
* Connection #0 to host localhost left intact
Hello, /, http: true

Now, Remember when I said that the golang standard library does not support H2C? While that is technically correct there is a workaround to get the golang standard http2 client to connect to an H2C enabled server.

To do so you have to override DialTLS and set the super secret AllowHTTP flag.

client := http.Client{
    Transport: &http2.Transport{
        // So http2.Transport doesn't complain the URL scheme isn't 'https'
        AllowHTTP: true,
        // Pretend we are dialing a TLS endpoint. (Note, we ignore the passed tls.Config)
        DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
            var d net.Dialer
            return d.DialContext(ctx, network, addr)
        },
    },
}

resp, _ := client.Get(url)
fmt.Printf("Client Proto: %d\n", resp.ProtoMajor)

Although this all looks a little wonky it actually works really well and performs nicely in production environments.

More Repositories

1

configmap-microservice-demo

A Kubernetes ConfigMap enabled GoLang Micro WebService Demo
Go
21
star
2

Spring-Jersey-Hibernate-DbUnit-Example

This is an example project for REST webservice builders that use Spring 3.0, Jersey, JPA, and Unit test it all with DbUnit
Java
13
star
3

hubble

Environment variable manager
Python
10
star
4

args

A modern golang CLI and sub command parser
Go
7
star
5

channel-stats

A slack bot to collect sentiment analysis and other statistics for a channel
JavaScript
5
star
6

android-service-dialog-demo

Example of getting a Service and Dialog working with orientation changes
Java
4
star
7

clip

Git Branch Tools
Go
4
star
8

dotfiles

my dotfiles
Python
3
star
9

queue-patterns.go

Benchmarking different batch queue implementations
Go
3
star
10

starwars-countdown

This is the code that runs starwars-countdown.com
Go
2
star
11

ollie

Ollie - The Embeddable Editor Interface
C++
2
star
12

etcd-template

A simple etcd implementation of consul-template or confd using the mailgun style json value
Go
2
star
13

k8-docker-host-dns

Allow Kubernetes for Docker to access kube-dns from the Docker host VM
Go
2
star
14

apple

Async Web Framework
Python
1
star
15

minimo

Go
1
star
16

dev-shell

A project to create and maintain a dev container for use in K8
Dockerfile
1
star
17

rewrite-args

Small tool to rewrite arguments before calling an executable
Go
1
star
18

mailgun-cli

Mailgun CLI
Go
1
star
19

argsini

Parse INI config files for use with args parser
Go
1
star
20

detka

Baby Mailgun
Go
1
star
21

discovery

Cluster agnostic service discovery
Go
1
star
22

leto

Entity/Component Library
C++
1
star
23

quartz-garden

TypeScript
1
star
24

python-bitcasaclient

Command line tool for Bitcasa
Python
1
star
25

humble

A Simple Python Implementation of the Active Record Pattern
Python
1
star
26

traefik-poc

Go
1
star
27

db-cost-estimation

PLpgSQL
1
star