{"id":19251907,"url":"https://github.com/thrawn01/h2c-golang-example","last_synced_at":"2025-06-20T13:35:51.384Z","repository":{"id":66408590,"uuid":"190454897","full_name":"thrawn01/h2c-golang-example","owner":"thrawn01","description":"Example HTTP/2 Cleartext (H2C) server and client in golang ","archived":false,"fork":false,"pushed_at":"2024-04-19T20:37:02.000Z","size":5,"stargazers_count":115,"open_issues_count":3,"forks_count":25,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-21T13:38:20.138Z","etag":null,"topics":["example-project","golang","h2c","h2c-support"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thrawn01.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-05T19:19:41.000Z","updated_at":"2025-03-15T20:59:57.000Z","dependencies_parsed_at":"2024-04-19T21:51:24.966Z","dependency_job_id":null,"html_url":"https://github.com/thrawn01/h2c-golang-example","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/thrawn01/h2c-golang-example","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thrawn01%2Fh2c-golang-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thrawn01%2Fh2c-golang-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thrawn01%2Fh2c-golang-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thrawn01%2Fh2c-golang-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thrawn01","download_url":"https://codeload.github.com/thrawn01/h2c-golang-example/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thrawn01%2Fh2c-golang-example/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260953793,"owners_count":23088107,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["example-project","golang","h2c","h2c-support"],"created_at":"2024-11-09T18:24:31.146Z","updated_at":"2025-06-20T13:35:46.370Z","avatar_url":"https://github.com/thrawn01.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"## HTTP/2 Cleartext (H2C) golang example\n\nSince the internet failed me, and the only workable example of a H2C client I\ncan find was in the actual go code test suite I'm going to lay out what I\ndiscovered about H2C support in golang here.\n\nFirst is that the standard golang code supports HTTP2 but does not\ndirectly support H2C. H2C support only exists in the `golang.org/x/net/http2/h2c`\npackage. You can make your HTTP server H2C capable by wrapping your handler or\nmux with `h2c.NewHandler()` like so.\n\n```go\nh2s := \u0026http2.Server{}\n\nhandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n    fmt.Fprintf(w, \"Hello, %v, http: %v\", r.URL.Path, r.TLS == nil)\n})\n\nserver := \u0026http.Server{\n    Addr:    \"0.0.0.0:1010\",\n    Handler: h2c.NewHandler(handler, h2s),\n}\n\n// Call ConfigureServer() to register graceful shutdown handlers which allows\n// the server to terminate client streams when calling server.Shutdown()\ncheckErr(http2.ConfigureServer(server, h2s), \"during call to ConfigureServer()\")\n\nfmt.Printf(\"Listening [0.0.0.0:1010]...\\n\")\ncheckErr(server.ListenAndServe(), \"while listening\")\n```\nThe above code allows the server to support\n[H2C upgrade](https://http2.github.io/http2-spec/#discover-http) and\n[H2C prior knowledge](https://http2.github.io/http2-spec/#known-http) along with\n standard HTTP/2 and HTTP/1.1 that golang natively supports.\n\nIf you don't care about supporting HTTP/1.1 then you can run this code which only\nsupports H2C prior knowledge.\n\n```go\nserver := http2.Server{}\n\nl, err := net.Listen(\"tcp\", \"0.0.0.0:1010\")\ncheckErr(err, \"while listening\")\n\nfmt.Printf(\"Listening [0.0.0.0:1010]...\\n\")\nfor {\n    conn, err := l.Accept()\n    checkErr(err, \"during accept\")\n\n    server.ServeConn(conn, \u0026http2.ServeConnOpts{\n        Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n            fmt.Fprintf(w, \"Hello, %v, http: %v\", r.URL.Path, r.TLS == nil)\n        }),\n    })\n}\n```\n\nOnce you have a running server you can test your server by installing `curl-openssl`.\n\n```\n$ brew install curl-openssl\n\n# Add curl-openssl to the front of your path\n$ export PATH=\"/usr/local/opt/curl-openssl/bin:$PATH\"\n```\n\nYou can now use curl to test your H2C enabled server like so.\n\n##### Connect via HTTP1.1 then upgrade to HTTP/2 (H2C)\n```\ncurl -v --http2 http://localhost:1010\n*   Trying ::1:1010...\n* TCP_NODELAY set\n* Connected to localhost (::1) port 1010 (#0)\n\u003e GET / HTTP/1.1\n\u003e Host: localhost:1010\n\u003e User-Agent: curl/7.65.0\n\u003e Accept: */*\n\u003e Connection: Upgrade, HTTP2-Settings\n\u003e Upgrade: h2c\n\u003e HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA\n\u003e\n* Mark bundle as not supporting multiuse\n\u003c HTTP/1.1 101 Switching Protocols\n\u003c Connection: Upgrade\n\u003c Upgrade: h2c\n* Received 101\n* Using HTTP2, server supports multi-use\n* Connection state changed (HTTP/2 confirmed)\n* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0\n* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!\n\u003c HTTP/2 200\n\u003c content-type: text/plain; charset=utf-8\n\u003c content-length: 20\n\u003c date: Wed, 05 Jun 2019 19:01:40 GMT\n\u003c\n* Connection #0 to host localhost left intact\nHello, /, http: true\n```\n\n##### Connect via HTTP/2 (H2C)\n```\ncurl -v --http2-prior-knowledge http://localhost:1010\n*   Trying ::1:1010...\n* TCP_NODELAY set\n* Connected to localhost (::1) port 1010 (#0)\n* Using HTTP2, server supports multi-use\n* Connection state changed (HTTP/2 confirmed)\n* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0\n* Using Stream ID: 1 (easy handle 0x7fdab8007000)\n\u003e GET / HTTP/2\n\u003e Host: localhost:1010\n\u003e User-Agent: curl/7.65.0\n\u003e Accept: */*\n\u003e\n* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!\n\u003c HTTP/2 200\n\u003c content-type: text/plain; charset=utf-8\n\u003c content-length: 20\n\u003c date: Wed, 05 Jun 2019 19:00:43 GMT\n\u003c\n* Connection #0 to host localhost left intact\nHello, /, http: true\n```\n\nNow, Remember when I said that the golang standard library does not support H2C?\nWhile that is technically correct there is a workaround to get the golang\nstandard http2 client to connect to an H2C enabled server.\n\n\nTo do so you have to override `DialTLS` and set the super secret `AllowHTTP` flag.\n\n```go\nclient := http.Client{\n    Transport: \u0026http2.Transport{\n        // So http2.Transport doesn't complain the URL scheme isn't 'https'\n        AllowHTTP: true,\n        // Pretend we are dialing a TLS endpoint. (Note, we ignore the passed tls.Config)\n        DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {\n            var d net.Dialer\n            return d.DialContext(ctx, network, addr)\n        },\n    },\n}\n\nresp, _ := client.Get(url)\nfmt.Printf(\"Client Proto: %d\\n\", resp.ProtoMajor)\n```\n\nAlthough this all looks a little wonky it actually works really well and\nperforms nicely in production environments.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthrawn01%2Fh2c-golang-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthrawn01%2Fh2c-golang-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthrawn01%2Fh2c-golang-example/lists"}