feat: configure application transports and unify graceful shutdown

This commit is contained in:
2026-09-23 12:49:56 +08:00
parent 59bf04a461
commit 8e18528fc2
18 changed files with 842 additions and 96 deletions

View File

@@ -31,7 +31,7 @@ type App struct {
}
// Runner is the small lifecycle boundary used by an HTTP server
// implementation. The default App.RunContext uses net/http; a custom runner
// implementation. App.RunContext selects a configured built-in server; a custom runner
// can own the listener, protocol parser and connection lifecycle.
type Runner interface {
Run(context.Context, http.Handler, string) error
@@ -133,7 +133,8 @@ func (a *App) Run(address ...string) error {
return a.RunContext(ctx, address...)
}
// NewServer creates a standard HTTP server around the application.
// NewServer creates a plain standard HTTP server around the application.
// Use RunContext or ServeContext to apply the configured transport and TLS.
func (a *App) NewServer(address string) *http.Server {
if address == "" {
address = a.config.Address
@@ -152,23 +153,21 @@ func (a *App) RunContext(ctx context.Context, address ...string) error {
if ctx.Err() != nil {
return a.Close()
}
listenOn := a.config.Address
if listenOn == "" {
listenOn = ":8080"
}
if len(address) > 0 && address[0] != "" {
listenOn = address[0]
}
server := a.NewServer(listenOn)
listener, err := net.Listen("tcp", server.Addr)
serve, err := a.prepareTransport()
if err != nil {
return errors.Join(err, a.Close())
}
return a.serveContext(ctx, server, listener)
listener, err := net.Listen("tcp", a.listenAddress(address...))
if err != nil {
return errors.Join(err, a.Close())
}
defer listener.Close()
return errors.Join(serve(ctx, listener), a.Close())
}
// RunWith starts a custom server runner while keeping App-owned cleanup in the
// same lifecycle. The runner receives the transport-neutral App handler.
// same lifecycle. The runner receives the transport-neutral App handler and
// owns transport/TLS settings; built-in transport selection is bypassed.
func (a *App) RunWith(ctx context.Context, runner Runner, address ...string) error {
if runner == nil {
return errors.Join(errors.New("go-omni: nil runner"), a.Close())
@@ -176,6 +175,13 @@ func (a *App) RunWith(ctx context.Context, runner Runner, address ...string) err
if ctx == nil {
ctx = context.Background()
}
if ctx.Err() != nil {
return a.Close()
}
return errors.Join(runner.Run(ctx, a, a.listenAddress(address...)), a.Close())
}
func (a *App) listenAddress(address ...string) string {
listenOn := a.config.Address
if listenOn == "" {
listenOn = ":8080"
@@ -183,12 +189,18 @@ func (a *App) RunWith(ctx context.Context, runner Runner, address ...string) err
if len(address) > 0 && address[0] != "" {
listenOn = address[0]
}
return errors.Join(runner.Run(ctx, a, listenOn), a.Close())
return listenOn
}
func (a *App) serveContext(ctx context.Context, server *http.Server, listener net.Listener) error {
serveErr := make(chan error, 1)
go func() { serveErr <- server.Serve(listener) }()
go func() {
if server.TLSConfig != nil {
serveErr <- server.ServeTLS(listener, "", "")
} else {
serveErr <- server.Serve(listener)
}
}()
select {
case err := <-serveErr: