Tuesday, September 29, 2015

Day 20 - A go http/2 server example

I was really excited when i read that support for http2 is now on golang.org! I was thinking about checking out SPDY in go for a long time, but now HTTP/2 support moved one important step further.

I think its one of the "Next big Things" on the interweb (the last change to http was 18! years ago)

So i skip SPDY and go directly for HTTP/2 - this post is about how to get a first grip and test it serverside as a go html server example.

Test preparations clientside:
  1. Get a browser that has http/2 support enabled by default: Chrome Canary
  2. Download the http2 and spdy indicator addon for chrome 
  3. Test your browsers http2 support at: https://http2.akamai.com/demo
  1. Cd into your GOPATH/src
  2. Run go get golang.org/x/net/http2
  3. Check if the package has been downloaded
Create, build and run:
  1. Create a new go project, name it eg. testHttp2Server
  2. Create a ssl certificate (localhost.cert and localhost.key)
    You can ether generate a self signed cert: selfsignedcertificate
    or use mine: testHttp2Server
    This two files must be in the same directory as the server binary.
  3. Code for http2Server.go:

    package main
    import (
    func main() {
        var srv http.Server
        http2.VerboseLogs = true
        srv.Addr = ":8080"
        // This enables http2 support
        http2.ConfigureServer(&srv, nil)
        // Plain text test handler
        // Open https://localhost:8080/randomtest
        // in your Chrome Canary browser
        http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
            fmt.Fprintf(w, "Hi tester %q\n", html.EscapeString(r.URL.Path))
            ShowRequestInfoHandler(w, r)
        // Listen as https ssl server
        // To self generate a test ssl cert/key you could go to
        // http://www.selfsignedcertificate.com/
        // or read the openssl manual
        log.Fatal(srv.ListenAndServeTLS("localhost.cert", "localhost.key"))
    func ShowRequestInfoHandler(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "text/plain")
        fmt.Fprintf(w, "Method: %s\n", r.Method)
        fmt.Fprintf(w, "Protocol: %s\n", r.Proto)
        fmt.Fprintf(w, "Host: %s\n", r.Host)
        fmt.Fprintf(w, "RemoteAddr: %s\n", r.RemoteAddr)
        fmt.Fprintf(w, "RequestURI: %q\n", r.RequestURI)
        fmt.Fprintf(w, "URL: %#v\n", r.URL)
        fmt.Fprintf(w, "Body.ContentLength: %d (-1 means unknown)\n", r.ContentLength)
        fmt.Fprintf(w, "Close: %v (relevant for HTTP/1 only)\n", r.Close)
        fmt.Fprintf(w, "TLS: %#v\n", r.TLS)
        fmt.Fprintf(w, "\nHeaders:\n")
  4. Compile:  go build http2Server.go
  5. Run the http2Server binary

Test it:
  1. Open in Chrome Canary: https://localhost:8080/kim
  2. Accept the certificate security error, continue to localhost
  3. Check if Protocol is HTTP/2.0, example output should be like:

    Hello tester "/kim"
    Method: GET
    Protocol: HTTP/2.0
    Host: localhost:8080

NOTE: http/2 in go ONLY WORKS WITH SSL/TLS! (currently)
I not sure why, but i did not got the server to support http/2 without TLS!
This could be a clientside or serverside issue - i dont know currently.

Some discussions about this:
daniel.haxx.se tls-in-http2
arstechnica http2-finished-coming-to-browsers-within-weeks

As far as i have understood it, the spec says it "should be" possible to use it without the need to buy SSL certificates:
rfc7540 - html2


  1. Yes, it should be possible. But the problem relays on the fact of discovering the support for it. Over plain TCP, HTTP has a mechanism to upgrade to a different protocol. I don't think that get implemented in that library yet.

    Unless a client know for sure the server supports HTTP/2, it shouldn't perform a direct connection to it using HTTP/2. HTTP protocol should work, independent of what's the working protocol underneath (HTTP/1.1 or HTTP/2). You shouldn't connect to a server asking directly with an HTTP/2 request because the server might not be able to respond. But it could be other reasons like the network failing, that's why first you need to establish a HTTP/1.1. Anything listening in port 80 "should" speak HTTP/1.1.

  2. Browsers have only implemented http2 over TLS. I have lost the reference to this, but that's the current limitation. The spec has not made TLS a requirement for http2.

  3. Thanks for your comments, you are both right: The Spec says it should be possible without TSL, but the major browsers (Chrome, Firefox) refuse to support HTTP2 without TLS. Im Sure they have some good reason for this. In the meanwhile im on a search for free certs for FOSS projects - until then i just dont turn on HTTP2 support on my servers. Thats a pity because it only hurts the clients, the traffic volume on the server side stays exactly the same as with HTTP1.1

  4. The Http2 standard itself does not specify tls must be used. BUT major browsers make it a must to do.

    You can use nghttp2 to test h2c (Http2 cleartext) request.