Skip to content

Commit

Permalink
move http server logic out to parapet (#108)
Browse files Browse the repository at this point in the history
* move http server logic out to parapet

* cleanup
  • Loading branch information
acoshift committed Sep 23, 2023
1 parent 5eaaa34 commit c92e114
Show file tree
Hide file tree
Showing 15 changed files with 56 additions and 738 deletions.
155 changes: 23 additions & 132 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,14 @@ import (
"html/template"
"net"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"time"

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

// App is the hime app
type App struct {
srv http.Server
srv parapet.Server
handler http.Handler
routes Routes
globals sync.Map
Expand All @@ -30,12 +24,7 @@ type App struct {
component map[string]*template.Template
templateFuncs []template.FuncMap

gs *GracefulShutdown
tcpKeepAlive time.Duration
reusePort bool

ETag bool
H2C bool
}

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

// Clone clones app
func (app *App) Clone() *App {
x := &App{
srv: http.Server{
Addr: app.srv.Addr,
ReadTimeout: app.srv.ReadTimeout,
ReadHeaderTimeout: app.srv.ReadHeaderTimeout,
WriteTimeout: app.srv.WriteTimeout,
IdleTimeout: app.srv.IdleTimeout,
MaxHeaderBytes: app.srv.MaxHeaderBytes,
TLSNextProto: cloneTLSNextProto(app.srv.TLSNextProto),
ConnState: app.srv.ConnState,
ErrorLog: app.srv.ErrorLog,
srv: parapet.Server{
Addr: app.srv.Addr,
ReadTimeout: app.srv.ReadTimeout,
ReadHeaderTimeout: app.srv.ReadHeaderTimeout,
WriteTimeout: app.srv.WriteTimeout,
IdleTimeout: app.srv.IdleTimeout,
MaxHeaderBytes: app.srv.MaxHeaderBytes,
TCPKeepAlivePeriod: app.srv.TCPKeepAlivePeriod,
GraceTimeout: app.srv.GraceTimeout,
WaitBeforeShutdown: app.srv.WaitBeforeShutdown,
ErrorLog: app.srv.ErrorLog,
TrustProxy: app.srv.TrustProxy,
H2C: app.srv.H2C,
ReusePort: app.srv.ReusePort,
ConnState: app.srv.ConnState,
TLSConfig: app.srv.TLSConfig.Clone(),
BaseContext: app.srv.BaseContext,
},
handler: app.handler,
routes: cloneRoutes(app.routes),
globals: cloneMap(&app.globals),
template: cloneTmpl(app.template),
templateFuncs: cloneFuncMaps(app.templateFuncs),
tcpKeepAlive: app.tcpKeepAlive,
reusePort: app.reusePort,
ETag: app.ETag,
H2C: app.H2C,
}
x.srv.Handler = x

if app.srv.TLSConfig != nil {
x.srv.TLSConfig = app.srv.TLSConfig.Clone()
}

if app.gs != nil {
x.gs = &GracefulShutdown{
timeout: app.gs.timeout,
wait: app.gs.wait,
notiFns: app.gs.notiFns,
}
}
return x
}

Expand All @@ -108,10 +88,6 @@ func (app *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
app.serveHandler = http.DefaultServeMux
}

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

app.serveHandler = app.ServeHandler(app.serveHandler)
})

Expand All @@ -128,98 +104,22 @@ func (app *App) ServeHandler(h http.Handler) http.Handler {
}

// Server returns server inside app
func (app *App) Server() *http.Server {
func (app *App) Server() *parapet.Server {
return &app.srv
}

// Shutdown shutdowns server
func (app *App) Shutdown(ctx context.Context) error {
if app.gs != nil {
for _, fn := range app.gs.notiFns {
go fn()
}

if app.gs.wait > 0 {
time.Sleep(app.gs.wait)
}

if app.gs.timeout > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, app.gs.timeout)
defer cancel()
}
}

return app.srv.Shutdown(ctx)
}

// TCPKeepAlive sets tcp keep-alive interval when using app.ListenAndServe
func (app *App) TCPKeepAlive(d time.Duration) *App {
app.tcpKeepAlive = d
return app
}

// ReusePort uses SO_REUSEPORT when create listener using app.ListenAndServe
func (app *App) ReusePort(enable bool) *App {
app.reusePort = enable
return app
}

func (app *App) listenAndServe() (err error) {
addr := app.srv.Addr
if addr == "" {
addr = ":http"
}

var ln net.Listener
if app.reusePort {
ln, err = reuseport.NewReusablePortListener("tcp", addr)
} else {
ln, err = net.Listen("tcp", addr)
}
if err != nil {
return
}

if d := app.tcpKeepAlive; d > 0 {
ln = tcpKeepAliveListener{ln.(*net.TCPListener), d}
}

return app.Serve(ln)
func (app *App) Shutdown() error {
return app.srv.Shutdown()
}

// ListenAndServe starts web server
func (app *App) ListenAndServe() error {
if app.gs != nil {
// graceful shutdown
errChan := make(chan error)

go func() {
if err := app.listenAndServe(); err != http.ErrServerClosed {
errChan <- err
}
}()

stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGTERM)

select {
case err := <-errChan:
return err
case <-stop:
return app.Shutdown(context.Background())
}
}

return app.listenAndServe()
return app.srv.ListenAndServe()
}

// Serve serves listener
func (app *App) Serve(l net.Listener) error {
if app.srv.TLSConfig != nil {
return app.srv.ServeTLS(l, "", "")
}

return app.srv.Serve(l)
}

Expand Down Expand Up @@ -253,15 +153,6 @@ func (app *App) SelfSign(s SelfSign) *App {
return app
}

// GracefulShutdown changes server to graceful shutdown mode
func (app *App) GracefulShutdown() *GracefulShutdown {
if app.gs == nil {
app.gs = &GracefulShutdown{}
}

return app.gs
}

func getApp(ctx context.Context) *App {
app, ok := ctx.Value(ctxKeyApp{}).(*App)
if !ok {
Expand Down
Loading

0 comments on commit c92e114

Please sign in to comment.