Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
1801754a9b
|
|||
|
5ab0120597
|
|||
|
69cea64ea0
|
21
CHANGELOG.md
21
CHANGELOG.md
@@ -5,6 +5,25 @@ All notable changes to this module will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this module adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.9.2] - 2026-03-25
|
||||
|
||||
### Fixed
|
||||
|
||||
- `OnStart` now binds the TCP listener synchronously via `net.Listen` before
|
||||
launching the serve goroutine. A port-in-use (or any other bind) error is
|
||||
returned immediately from `OnStart`, allowing the launcher to treat it as a
|
||||
fatal startup failure and trigger a clean shutdown. Previously the error only
|
||||
appeared in a log line while the application continued running without an
|
||||
HTTP server.
|
||||
|
||||
## [0.9.1] - 2026-03-21
|
||||
|
||||
### Fixed
|
||||
|
||||
- `OnStop` now returns `nil` immediately when `s.srv == nil`, preventing a nil pointer
|
||||
panic when the launcher calls cleanup on a server component whose `OnStart` was never
|
||||
reached (e.g. because an earlier component failed during startup).
|
||||
|
||||
## [0.9.0] - 2026-03-18
|
||||
|
||||
### Added
|
||||
@@ -25,4 +44,6 @@ and this module adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.
|
||||
- No middleware is installed by default; the full middleware stack is composed explicitly via `WithMiddleware` at construction time, keeping the stack visible and ordering unambiguous in the application source
|
||||
- chi was chosen as the underlying router because it uses stdlib `http.Handler` throughout, making it fully compatible with `httpmw` middleware and `httputil` handler adapters without any wrapper code at the boundary
|
||||
|
||||
[0.9.2]: https://code.nochebuena.dev/go/httpserver/releases/tag/v0.9.2
|
||||
[0.9.1]: https://code.nochebuena.dev/go/httpserver/releases/tag/v0.9.1
|
||||
[0.9.0]: https://code.nochebuena.dev/go/httpserver/releases/tag/v0.9.0
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@@ -80,7 +81,10 @@ func (s *httpServer) OnInit() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnStart starts the HTTP server in a background goroutine.
|
||||
// OnStart binds the TCP listener synchronously so that a port conflict returns
|
||||
// an error immediately — allowing the launcher to trigger a clean shutdown
|
||||
// instead of running silently without an HTTP server. The accepted connections
|
||||
// are then served in a background goroutine.
|
||||
func (s *httpServer) OnStart() error {
|
||||
host := s.cfg.Host
|
||||
if host == "" {
|
||||
@@ -90,16 +94,23 @@ func (s *httpServer) OnStart() error {
|
||||
if port == 0 {
|
||||
port = 8080
|
||||
}
|
||||
addr := fmt.Sprintf("%s:%d", host, port)
|
||||
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("httpserver: bind %s: %w", addr, err)
|
||||
}
|
||||
|
||||
s.srv = &http.Server{
|
||||
Addr: fmt.Sprintf("%s:%d", host, port),
|
||||
Addr: addr,
|
||||
Handler: s.Router,
|
||||
ReadTimeout: s.cfg.ReadTimeout,
|
||||
WriteTimeout: s.cfg.WriteTimeout,
|
||||
IdleTimeout: s.cfg.IdleTimeout,
|
||||
}
|
||||
s.logger.Info("httpserver: starting", "addr", s.srv.Addr)
|
||||
s.logger.Info("httpserver: starting", "addr", addr)
|
||||
go func() {
|
||||
if err := s.srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
if err := s.srv.Serve(ln); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
s.logger.Error("httpserver: fatal error", err)
|
||||
}
|
||||
}()
|
||||
@@ -107,8 +118,12 @@ func (s *httpServer) OnStart() error {
|
||||
}
|
||||
|
||||
// OnStop performs a graceful shutdown, waiting up to 10 seconds for in-flight
|
||||
// requests to complete.
|
||||
// requests to complete. If OnStart was never called (e.g. a prior component
|
||||
// failed during startup), this is a no-op.
|
||||
func (s *httpServer) OnStop() error {
|
||||
if s.srv == nil {
|
||||
return nil
|
||||
}
|
||||
s.logger.Info("httpserver: shutting down gracefully")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
Reference in New Issue
Block a user