Skip to content

Commit

Permalink
add h2c support
Browse files Browse the repository at this point in the history
  • Loading branch information
acoshift committed Apr 18, 2021
1 parent 6467968 commit 2281714
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 14 deletions.
32 changes: 23 additions & 9 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ import (
"time"

reuseport "github.com/kavu/go_reuseport"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
)

// App is the hime app
type App struct {
srv http.Server
handler http.Handler
routes Routes
globals sync.Map
srv http.Server
handler http.Handler
routes Routes
globals sync.Map
onceServeHTTP sync.Once
serveHandler http.Handler

template map[string]*tmpl
templateFuncs []template.FuncMap
Expand All @@ -30,6 +34,7 @@ type App struct {
reusePort bool

ETag bool
H2C bool
}

type ctxKeyApp struct{}
Expand All @@ -39,6 +44,7 @@ func New() *App {
app := &App{}
app.srv.Handler = app
app.tcpKeepAlive = 3 * time.Minute
app.H2C = true
return app
}

Expand All @@ -64,6 +70,7 @@ func (app *App) Clone() *App {
tcpKeepAlive: app.tcpKeepAlive,
reusePort: app.reusePort,
ETag: app.ETag,
H2C: app.H2C,
}
x.srv.Handler = x

Expand Down Expand Up @@ -94,15 +101,22 @@ func (app *App) Handler(h http.Handler) *App {
}

func (app *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
app.onceServeHTTP.Do(func() {
app.serveHandler = app.handler
if app.serveHandler == nil {
app.serveHandler = http.DefaultServeMux
}

if app.H2C {
app.serveHandler = h2c.NewHandler(app.serveHandler, &http2.Server{})
}
})

ctx := r.Context()
ctx = context.WithValue(ctx, ctxKeyApp{}, app)
r = r.WithContext(ctx)

if app.handler == nil {
http.DefaultServeMux.ServeHTTP(w, r)
return
}
app.handler.ServeHTTP(w, r)
app.serveHandler.ServeHTTP(w, r)
}

// Server returns server inside app
Expand Down
80 changes: 75 additions & 5 deletions app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import (
"context"
"crypto/tls"
"html/template"
"net"
"net/http"
"testing"
"time"

"github.com/stretchr/testify/assert"
"golang.org/x/net/http2"
)

func TestApp(t *testing.T) {
Expand Down Expand Up @@ -129,10 +131,12 @@ func TestApp(t *testing.T) {
app.Address(":8081")

go app.ListenAndServe()
defer app.Shutdown(context.Background())

time.Sleep(time.Second)

http.Get("http://localhost:8081")
app.Shutdown(context.Background())
_, err := http.Get("http://localhost:8081")
assert.NoError(t, err)
assert.True(t, called)
})

Expand All @@ -150,12 +154,15 @@ func TestApp(t *testing.T) {
app.Address(":8082")

go app.ListenAndServe()
defer app.Shutdown(context.Background())

time.Sleep(time.Second)

(&http.Client{Transport: &http.Transport{
client := http.Client{Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}}).Get("https://localhost:8082")
app.Shutdown(context.Background())
}}
_, err := client.Get("https://localhost:8082")
assert.NoError(t, err)
assert.True(t, called)
})

Expand Down Expand Up @@ -213,4 +220,67 @@ func TestApp(t *testing.T) {
time.Sleep(time.Second)
assert.True(t, called)
})

t.Run("H2C", func(t *testing.T) {
t.Parallel()

app := New()
app.H2C = true
called := false
app.Handler(Handler(func(ctx *Context) error {
called = true
assert.Equal(t, "HTTP/2.0", ctx.Request.Proto)
return ctx.String("Hello")
}))
app.Address(":8085")

go app.ListenAndServe()
defer app.Shutdown(context.Background())

time.Sleep(time.Second)

client := http.Client{
Transport: &http2.Transport{
AllowHTTP: true,
DisableCompression: true,
DialTLS: func(network, addr string, _ *tls.Config) (net.Conn, error) {
return net.Dial(network, addr)
},
},
}
_, err := client.Get("http://localhost:8085")
assert.NoError(t, err)
assert.True(t, called)
})

t.Run("Disable H2C", func(t *testing.T) {
t.Parallel()

app := New()
app.H2C = false
app.Handler(Handler(func(ctx *Context) error {
return ctx.String("Hello")
}))
app.Address(":8086")

go app.ListenAndServe()
defer app.Shutdown(context.Background())

time.Sleep(time.Second)

client := http.Client{
Transport: &http2.Transport{
AllowHTTP: true,
DisableCompression: true,
DialTLS: func(network, addr string, _ *tls.Config) (net.Conn, error) {
return net.Dial(network, addr)
},
},
}
_, err := client.Get("http://localhost:8086")
assert.Error(t, err)

_, err = http.Get("http://localhost:8086")
assert.NoError(t, err)
})
}
5 changes: 5 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type AppConfig struct {
ReusePort *bool `yaml:"reusePort" json:"reusePort"`
TCPKeepAlive string `yaml:"tcpKeepAlive" json:"tcpKeepAlive"`
ETag *bool `yaml:"eTag" json:"eTag"`
H2C *bool `yaml:"h2c" json:"h2c"`
GracefulShutdown *GracefulShutdown `yaml:"gracefulShutdown" json:"gracefulShutdown"`
TLS *TLS `yaml:"tls" json:"tls"`
HTTPSRedirect *HTTPSRedirect `yaml:"httpsRedirect" json:"httpsRedirect"`
Expand Down Expand Up @@ -67,6 +68,7 @@ func parseDuration(s string, t *time.Duration) {
// writeTimeout: 5s
// idleTimeout: 30s
// eTag: true
// h2c: true
// gracefulShutdown:
// timeout: 1m
// wait: 5s
Expand Down Expand Up @@ -97,6 +99,9 @@ func (app *App) Config(config AppConfig) *App {
if server.ETag != nil {
app.ETag = *server.ETag
}
if server.H2C != nil {
app.H2C = *server.H2C
}

if t := server.TLS; t != nil {
app.srv.TLSConfig = server.TLS.config()
Expand Down
1 change: 1 addition & 0 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func TestConfig(t *testing.T) {
assert.Equal(t, app.tcpKeepAlive, time.Minute)
assert.True(t, app.reusePort)
assert.True(t, app.ETag)
assert.True(t, app.H2C)
assert.Len(t, app.srv.TLSConfig.Certificates, 1)

// graceful
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/stretchr/testify v1.7.0
github.com/tdewolff/minify/v2 v2.9.16
github.com/tdewolff/parse/v2 v2.5.15 // indirect
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,18 @@ github.com/tdewolff/parse/v2 v2.5.15 h1:hYZKJZ0KfHMGhN3+hER4R9gQM/umJThkeeyJNtsO
github.com/tdewolff/parse/v2 v2.5.15/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d h1:BgJvlyh+UqCUaPlscHJ+PN8GcpfrFdr7NHjd1JL0+Gs=
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
1 change: 1 addition & 0 deletions testdata/config1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ server:
idleTimeout: 30s
reusePort: true
eTag: true
h2c: true
tcpKeepAlive: 1m
gracefulShutdown:
timeout: 1m
Expand Down

0 comments on commit 2281714

Please sign in to comment.