Skip to content

refactor(server): serve unencrypted HTTP/2 natively - #1818

Merged
AmanGIT07 merged 1 commit into
mainfrom
refactor/server-native-http2
Jul 30, 2026
Merged

refactor(server): serve unencrypted HTTP/2 natively#1818
AmanGIT07 merged 1 commit into
mainfrom
refactor/server-native-http2

Conversation

@AmanGIT07

Copy link
Copy Markdown
Contributor

Summary

Replaces the deprecated golang.org/x/net/http2/h2c wrapper with the http.Server Protocols field (native since Go 1.24). The server serves HTTP/1.1 and unencrypted HTTP/2 itself and tracks the HTTP/2 connections, so graceful shutdown drains them the same way as HTTP/1.1 ones — the http2.ConfigureServer GOAWAY workaround (golang/go#26682) is removed.

Changes

  • pkg/server: set Protocols (HTTP1, HTTP2, UnencryptedHTTP2) on the connect server; drop h2c.NewHandler and the http2.ConfigureServer hook
  • Cleartext HTTP/2 is negotiated by prior knowledge only; the HTTP/1.1 Upgrade mechanism is not supported. gRPC and ConnectRPC clients use prior knowledge; for manual checks use curl --http2-prior-knowledge
  • HTTP2 keeps HTTP/2 enabled over TLS should a TLS listener ever front the handler
  • New tests: TestGracefulShutdownDrainsInflightHTTP2Requests (an in-flight HTTP/2 request survives shutdown to completion) and TestServeConnectServesBothProtocols (the real ServeConnect answers HTTP/1.1 and HTTP/2.0)
  • x/net/http2 is no longer imported by first-party code; the go.mod annotation cleanup rides in a follow-up

Technical Details

Verified against a running server: plain request → HTTP/1.1 200; curl --http2 (Upgrade dance) → HTTP/1.1 200 with the upgrade quietly declined; curl --http2-prior-knowledge → HTTP/2 200; grpcurl reflection works over plaintext HTTP/2; SIGTERM logs a complete graceful shutdown. HTTP/1.1 clients are unaffected.

Test Plan

  • go build ./... and go vet ./... pass
  • make lint passes (0 issues)
  • make test passes (race detector, -count 2)
  • make e2e-test passes against live Postgres + SpiceDB containers
  • Live probes on a running server: HTTP/1.1, HTTP/2 prior knowledge, Upgrade declined, gRPC reflection, SIGTERM graceful drain

🤖 Generated with Claude Code

@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview Jul 30, 2026 7:14am

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added support for serving requests over both HTTP/1.1 and unencrypted HTTP/2.
  • Bug Fixes
    • Improved graceful shutdown to wait for in-progress HTTP/2 requests to complete before the server stops.
  • Tests
    • Added coverage to verify both protocols are served correctly and that graceful shutdown drains in-flight HTTP/2 requests.

Walkthrough

ServeConnect replaces custom h2c wiring with native protocol configuration and adds tests for HTTP/1.1, unencrypted HTTP/2, and graceful draining of in-flight HTTP/2 requests.

Changes

ServeConnect HTTP/2 support

Layer / File(s) Summary
Native protocol configuration
pkg/server/server.go
ServeConnect replaces the h2c wrapper and HTTP/2 configuration hook with CORS-wrapped routing and native http.Server.Protocols configuration for HTTP/1.1, unencrypted HTTP/2, and HTTP/2.
Protocol and shutdown validation
pkg/server/server_test.go
Tests add a prior-knowledge HTTP/2 client, verify graceful draining of in-flight requests, and validate /ping over both HTTP/1.1 and unencrypted HTTP/2.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: rohilsurana

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3c6d582c-6c4b-4b6a-a088-42e83651274b

📥 Commits

Reviewing files that changed from the base of the PR and between d72c679 and 9b76974.

📒 Files selected for processing (2)
  • pkg/server/server.go
  • pkg/server/server_test.go

Comment thread pkg/server/server.go
Replaces the x/net h2c wrapper with the http.Server Protocols field.
The server now tracks HTTP/2 connections itself, so Shutdown drains
them like HTTP/1.1 ones and the GOAWAY workaround is no longer needed.
Cleartext HTTP/2 is negotiated by prior knowledge only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
pkg/server/server_test.go (1)

83-90: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Make the in-flight shutdown barrier deterministic.

A scheduler delay exceeding 300ms can let /slow finish before cancel(), so this test may pass without proving that shutdown waits for an active HTTP/2 request. Gate handler completion with a channel, assert wg.Wait() is still blocked after Serve returns, then release it.

Proposed fix
 entered := make(chan struct{})
+allowFinish := make(chan struct{})
 requestDone := make(chan struct{})
 ...
 	close(entered)
-	time.Sleep(300 * time.Millisecond)
+	<-allowFinish
 	close(requestDone)
 ...
 <-entered
 cancel()

 <-serveReturned
-wg.Wait()
+shutdownDone := make(chan struct{})
+go func() {
+	wg.Wait()
+	close(shutdownDone)
+}()
+select {
+case <-shutdownDone:
+	close(allowFinish)
+	t.Fatal("shutdown completed before the in-flight request finished")
+default:
+}
+close(allowFinish)
+<-shutdownDone

Also applies to: 131-145


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a585d945-4257-49f0-a8b3-11f9cdabc222

📥 Commits

Reviewing files that changed from the base of the PR and between 9b76974 and ed31d55.

📒 Files selected for processing (2)
  • pkg/server/server.go
  • pkg/server/server_test.go

@AmanGIT07
AmanGIT07 merged commit 9880871 into main Jul 30, 2026
8 checks passed
@AmanGIT07
AmanGIT07 deleted the refactor/server-native-http2 branch July 30, 2026 07:19
@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 30522242418

Warning

Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes.
Quick fix: rebase this PR. Learn more →

Coverage increased (+0.01%) to 46.99%

Details

  • Coverage increased (+0.01%) from the base build.
  • Patch coverage: 15 of 15 lines across 1 file are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 39089
Covered Lines: 18368
Line Coverage: 46.99%
Coverage Strength: 14.47 hits per line

💛 - Coveralls

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants