- FIX: potential CWE-400 with bufio.ReadBytes & bufio.ReadSlices, with no limited read buffer - ADD: test to check overflow buffer with discard or error - REFACTOR: all buffering package, parsing process - UPDATE: doc, examples, test following changes - OPTIMIZE: rework code to optimize process - REWORK: benchmark to check benefice of optimization - FIX: wording error Package IOUtils/Multi: - REWORK: re-design all package to allow sequential/parallel mode - UPDATE: package with adaptive mode to allow switch automaticly between sequential and parallel mode following measurment of sample - OPTIMIZE: code to maximize bandwith and reduce time of write - UPDATE: documentation, test and comments - REWORK: testing organization and benchmark aggregation Package HttpServer: - FIX: bug with dial addr rewrite for healtcheck & testing PortUse Package Logger/HookFile: - FIX: bug with race condition on aggregator counter file Other: - Bump dependencies - FIX: format / import file
HTTP Server Package
Production-grade HTTP server management for Go with lifecycle control, TLS support, pool orchestration, and integrated monitoring.
Table of Contents
- Overview
- Key Features
- Installation
- Architecture
- Quick Start
- Performance
- Use Cases
- Core Package
- Subpackages
- Best Practices
- Testing
- Contributing
- Future Enhancements
- License
Overview
The httpserver package provides a robust abstraction layer for managing HTTP/HTTPS servers in Go applications. It emphasizes production readiness with comprehensive lifecycle management, configuration validation, TLS support, and the ability to orchestrate multiple servers through a unified pool interface.
Design Philosophy
- Lifecycle Management: Full control over server start, stop, and restart operations
- Configuration-Driven: Declarative configuration with validation
- Thread-Safe: Atomic operations and proper synchronization for concurrent use
- Production-Ready: Monitoring, logging, and error handling built-in
- Composable: Pool management for coordinating multiple server instances
Key Features
- Complete Lifecycle Control: Start, stop, restart servers with context-aware operations
- Configuration Validation: Built-in validation with detailed error reporting
- TLS/HTTPS Support: Integrated certificate management with optional/mandatory modes
- Pool Management: Coordinate multiple servers with unified operations and filtering
- Handler Management: Dynamic handler registration with key-based routing
- Monitoring Integration: Built-in health checks and metrics collection
- Thread-Safe Operations: Atomic values and mutex protection for concurrent access
- Port Conflict Detection: Automatic port availability checking before binding
- Graceful Shutdown: Context-aware shutdown with configurable timeouts
Installation
go get github.com/nabbar/golib/httpserver
Architecture
Package Structure
The package is organized into three main components:
httpserver/
├── httpserver # Core server implementation
│ ├── config.go # Configuration and validation
│ ├── server.go # Server lifecycle management
│ ├── run.go # Start/stop execution logic
│ ├── handler.go # Handler registration
│ ├── monitor.go # Monitoring integration
│ └── interface.go # Public interfaces
├── pool/ # Multi-server orchestration
│ ├── interface.go # Pool interfaces
│ ├── server.go # Pool operations
│ ├── list.go # Filtering and listing
│ └── config.go # Pool configuration
└── types/ # Shared type definitions
├── handler.go # Handler types
├── fields.go # Field type constants
└── const.go # Package constants
Component Diagram
┌─────────────────────────────────────────────────────┐
│ Application Layer │
│ (Your HTTP Handlers & Routes) │
└──────────────────┬──────────────────────────────────┘
│
┌─────────▼─────────┐
│ httpserver │
│ Package API │
└─────────┬─────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌───▼───┐ ┌────▼────┐ ┌───▼────┐
│Server │ │ Pool │ │ Types │
│ │ │ │ │ │
│Config │◄───┤ Manager │ │Handler │
│Run │ │ Filter │ │Fields │
│Monitor│ │ Clone │ │Const │
└───┬───┘ └────┬────┘ └────────┘
│ │
└──────┬──────┘
│
┌──────▼──────┐
│ Go stdlib │
│ http.Server │
└─────────────┘
Thread Safety Architecture
| Component | Mechanism | Concurrency Model |
|---|---|---|
| Server State | atomic.Value |
Lock-free reads, atomic writes |
| Pool Map | sync.RWMutex |
Multiple readers, exclusive writers |
| Handler Registry | atomic.Value |
Lock-free handler swapping |
| Logger | atomic.Value |
Thread-safe logging |
| Runner | atomic.Value + sync.WaitGroup |
Lifecycle synchronization |
Performance
Server Operations
| Operation | Time | Memory | Notes |
|---|---|---|---|
| Config Validation | ~100ns | O(1) | Field validation only |
| Server Creation | <1ms | ~5KB | Includes initialization |
| Start Server | 1-5ms | ~10KB | Port binding overhead |
| Stop Server | <5s | O(1) | Graceful shutdown timeout |
| Pool Operations | O(n) | ~1KB/server | Linear scaling |
Throughput
- HTTP Requests: Limited by Go's
http.Server(typically 50k+ req/s) - HTTPS/TLS: ~20-30k req/s depending on cipher suite
- Pool Management: Negligible overhead (<1% per server)
Memory Usage
- Single Server: ~10-15KB baseline + handler memory
- Pool with 10 Servers: ~150KB baseline
- Scale: Linear growth with server count
Use Cases
This package is designed for applications requiring robust HTTP server management:
Microservices Architecture
- Run multiple API versions simultaneously (v1, v2, v3)
- Separate admin and public endpoints on different ports
- Blue-green deployments with gradual traffic shifting
Multi-Tenant Systems
- Dedicated server per tenant with isolated configuration
- Different TLS certificates per customer domain
- Per-tenant rate limiting and monitoring
Development & Testing
- Start/stop servers dynamically in integration tests
- Multiple test environments on different ports
- Mock servers with configurable behavior
API Gateways
- Route traffic to multiple backend servers
- Health checking and automatic failover
- Centralized monitoring and logging
Production Deployments
- Graceful shutdown during rolling updates
- TLS certificate rotation without downtime
- Structured logging and monitoring integration
Quick Start
Single Server
package main
import (
"context"
"net/http"
"github.com/nabbar/golib/httpserver"
)
func main() {
// Create server configuration
cfg := httpserver.Config{
Name: "api-server",
Listen: "127.0.0.1:8080",
Expose: "http://localhost:8080",
}
// Register handler (required)
cfg.RegisterHandlerFunc(func() map[string]http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
})
return map[string]http.Handler{
"": mux, // Default handler
}
})
// Validate configuration
if err := cfg.Validate(); err != nil {
panic(err)
}
// Create and start server
srv, err := httpserver.New(cfg, nil)
if err != nil {
panic(err)
}
ctx := context.Background()
if err := srv.Start(ctx); err != nil {
panic(err)
}
// Server is now running...
// Graceful shutdown
defer srv.Stop(ctx)
}
Server Pool
package main
import (
"context"
"net/http"
"github.com/nabbar/golib/httpserver"
"github.com/nabbar/golib/httpserver/pool"
)
func main() {
// Create handler function
handlerFunc := func() map[string]http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello from pool!"))
})
return map[string]http.Handler{"": mux}
}
// Create pool with handler
p := pool.New(nil, handlerFunc)
// Add multiple servers
configs := []httpserver.Config{
{Name: "api-v1", Listen: "127.0.0.1:8080", Expose: "http://localhost:8080"},
{Name: "api-v2", Listen: "127.0.0.1:8081", Expose: "http://localhost:8081"},
{Name: "admin", Listen: "127.0.0.1:8082", Expose: "http://localhost:8082"},
}
for _, cfg := range configs {
if err := p.StoreNew(cfg, nil); err != nil {
panic(err)
}
}
// Start all servers
ctx := context.Background()
if err := p.Start(ctx); err != nil {
panic(err)
}
// All servers running...
// Stop all servers gracefully
defer p.Stop(ctx)
}
Core Package: httpserver
The core package provides the foundational server abstraction with configuration, lifecycle management, and monitoring.
Configuration
The Config struct defines all server parameters with validation:
type Config struct {
// Name identifies the server instance (required)
Name string `validate:"required"`
// Listen is the bind address - format: "ip:port" or "host:port" (required)
// Examples: "127.0.0.1:8080", "0.0.0.0:443", "localhost:3000"
Listen string `validate:"required,hostname_port"`
// Expose is the public-facing URL for this server (required)
// Used for generating URLs, monitoring, and service discovery
// Examples: "http://localhost:8080", "https://api.example.com"
Expose string `validate:"required,url"`
// HandlerKey associates this server with a specific handler from the handler map
// Allows multiple servers to use different handlers from a shared registry
HandlerKey string
// Disabled allows disabling a server without removing its configuration
// Useful for maintenance mode or gradual rollout
Disabled bool
// Monitor configuration for health checks and metrics
Monitor moncfg.Config
// TLSMandatory requires valid TLS configuration to start the server
// If true, server will fail to start without proper TLS setup
TLSMandatory bool
// TLS certificate configuration (optional)
// If InheritDefault is true, uses default TLS config
TLS libtls.Config
// Additional HTTP/2 and timeout configuration...
}
Configuration Methods:
// Validate performs comprehensive validation on all fields
func (c Config) Validate() error
// Clone creates a deep copy of the configuration
func (c Config) Clone() Config
// RegisterHandlerFunc sets the handler function for this server
func (c *Config) RegisterHandlerFunc(f FuncHandler)
// SetDefaultTLS sets the default TLS configuration provider
func (c *Config) SetDefaultTLS(f FctTLSDefault)
// SetContext sets the parent context provider
func (c *Config) SetContext(f FuncContext)
// Server creates a new server instance from this configuration
func (c Config) Server(defLog FuncLog) (Server, error)
Server Interface
The Server interface provides full lifecycle and configuration control:
type Server interface {
// Lifecycle Management
Start(ctx context.Context) error // Start the HTTP server
Stop(ctx context.Context) error // Gracefully stop the server
Restart(ctx context.Context) error // Stop then start the server
IsRunning() bool // Check if server is running
Uptime() time.Duration // Get server uptime
// Server Information
GetName() string // Get server name
GetBindable() string // Get bind address (Listen)
GetExpose() string // Get expose URL
IsDisable() bool // Check if server is disabled
IsTLS() bool // Check if TLS is configured
// Configuration Management
GetConfig() *Config // Get current configuration
SetConfig(cfg Config, defLog FuncLog) error // Update configuration
// Handler Management
Handler(h FuncHandler) // Set handler function
Merge(s Server, def FuncLog) error // Merge another server's config
// Monitoring
Monitor(vrs Version) (Monitor, error) // Get monitoring data
MonitorName() string // Get monitor identifier
}
Usage Examples
Basic HTTP Server
package main
import (
"context"
"net/http"
"github.com/nabbar/golib/httpserver"
)
func main() {
// Configure server
cfg := httpserver.Config{
Name: "web-server",
Listen: "0.0.0.0:8080",
Expose: "http://api.example.com",
}
// Register HTTP handler
cfg.RegisterHandlerFunc(func() map[string]http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World"))
})
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
return map[string]http.Handler{"": mux}
})
// Create and start server
srv, _ := httpserver.New(cfg, nil)
srv.Start(context.Background())
}
HTTPS Server with TLS
package main
import (
"context"
"github.com/nabbar/golib/certificates"
"github.com/nabbar/golib/httpserver"
)
func main() {
cfg := httpserver.Config{
Name: "secure-server",
Listen: "0.0.0.0:8443",
Expose: "https://secure.example.com",
TLSMandatory: true,
TLS: certificates.Config{
CertPEM: "/path/to/cert.pem",
KeyPEM: "/path/to/key.pem",
// Additional TLS configuration...
},
}
cfg.RegisterHandlerFunc(func() map[string]http.Handler {
// Your HTTPS handler
return map[string]http.Handler{"": yourHandler}
})
srv, _ := httpserver.New(cfg, nil)
srv.Start(context.Background())
}
Multiple Handlers with Keys
package main
import (
"context"
"net/http"
"github.com/nabbar/golib/httpserver"
)
func main() {
// Create handler registry
handlerFunc := func() map[string]http.Handler {
return map[string]http.Handler{
"api-v1": createAPIv1Handler(),
"api-v2": createAPIv2Handler(),
"admin": createAdminHandler(),
"default": createDefaultHandler(),
}
}
// Server using api-v1 handler
cfg := httpserver.Config{
Name: "api-v1-server",
Listen: "127.0.0.1:8080",
Expose: "http://localhost:8080",
HandlerKey: "api-v1", // Select specific handler
}
cfg.RegisterHandlerFunc(handlerFunc)
srv, _ := httpserver.New(cfg, nil)
srv.Start(context.Background())
}
Disabled Server (Maintenance Mode)
cfg := httpserver.Config{
Name: "maintenance-server",
Listen: "127.0.0.1:8080",
Expose: "http://localhost:8080",
Disabled: true, // Server won't start, but config is preserved
}
srv, _ := httpserver.New(cfg, nil)
// Server will not start due to Disabled flag
srv.Start(context.Background()) // Returns immediately without error
Dynamic Restart with New Configuration
package main
import (
"context"
"github.com/nabbar/golib/httpserver"
)
func main() {
// Initial configuration
cfg1 := httpserver.Config{
Name: "dynamic-server",
Listen: "127.0.0.1:8080",
Expose: "http://localhost:8080",
}
cfg1.RegisterHandlerFunc(handlerFunc)
srv, _ := httpserver.New(cfg1, nil)
srv.Start(context.Background())
// Later: update configuration (e.g., enable TLS)
cfg2 := cfg1.Clone()
cfg2.TLSMandatory = true
cfg2.TLS = newTLSConfig
cfg2.Expose = "https://localhost:8443"
// Update and restart
srv.SetConfig(cfg2, nil)
srv.Restart(context.Background())
}
Subpackages
pool Subpackage
Multi-server orchestration with unified lifecycle management and advanced filtering capabilities.
Purpose: Coordinate multiple HTTP servers as a single unit with shared handlers, monitoring, and control operations.
Features
- Unified Lifecycle: Start, stop, restart all servers with a single call
- Dynamic Management: Add/remove servers at runtime
- Advanced Filtering: Query servers by name, bind address, or expose URL
- Pattern Matching: Support for glob patterns and regex filtering
- Pool Operations: Clone, merge, and walk through server collections
- Shared Handlers: Register handlers once for all servers
- Aggregated Monitoring: Collect metrics from all servers
- Thread-Safe: RWMutex protection for concurrent access
Pool Interface
type Pool interface {
// Lifecycle Management (inherited from libsrv.Server)
Start(ctx context.Context) error
Stop(ctx context.Context) error
Restart(ctx context.Context) error
IsRunning() bool
Uptime() time.Duration
// Management Operations
Walk(fct FuncWalk) bool // Iterate over all servers
WalkLimit(fct FuncWalk, onlyBindAddress ...string) bool // Iterate over specific servers
Clean() // Remove all servers
Load(bindAddress string) Server // Get server by bind address
Store(srv Server) // Add/update server
Delete(bindAddress string) // Remove server
StoreNew(cfg Config, defLog FuncLog) error // Add new server from config
LoadAndDelete(bindAddress string) (Server, bool) // Atomic load-and-delete
MonitorNames() []string // List all monitor names
// Filtering Operations
Has(bindAddress string) bool // Check if server exists
Len() int // Get server count
List(fieldFilter, fieldReturn FieldType, pattern, regex string) []string
Filter(field FieldType, pattern, regex string) Pool // Create filtered view
// Advanced Operations
Clone(ctx context.Context) Pool // Deep copy pool
Merge(p Pool, def FuncLog) error // Merge another pool
Handler(fct FuncHandler) // Set global handler
Monitor(vrs Version) ([]Monitor, error) // Get all monitors
}
Config Type
Pool configuration as a slice of server configs:
type Config []httpserver.Config
// Set global handler for all servers
func (p Config) SetHandlerFunc(hdl FuncHandler)
// Set global TLS configuration
func (p Config) SetDefaultTLS(f FctTLSDefault)
// Set global context provider
func (p Config) SetContext(f FuncContext)
// Validate all configurations
func (p Config) Validate() error
// Create pool from configurations
func (p Config) Pool(ctx FuncContext, hdl FuncHandler, defLog FuncLog) (Pool, error)
// Iterate over configurations
func (p Config) Walk(fct FuncWalkConfig)
Usage Examples
Basic Pool Management:
package main
import (
"context"
"net/http"
"github.com/nabbar/golib/httpserver"
"github.com/nabbar/golib/httpserver/pool"
)
func main() {
// Create shared handler
handler := func() map[string]http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello from pool"))
})
return map[string]http.Handler{"": mux}
}
// Create pool
p := pool.New(nil, handler)
// Add servers dynamically
servers := []httpserver.Config{
{Name: "api-v1", Listen: "127.0.0.1:8080", Expose: "http://localhost:8080"},
{Name: "api-v2", Listen: "127.0.0.1:8081", Expose: "http://localhost:8081"},
{Name: "admin", Listen: "127.0.0.1:9000", Expose: "http://localhost:9000"},
}
for _, cfg := range servers {
if err := p.StoreNew(cfg, nil); err != nil {
panic(err)
}
}
// Start all servers
ctx := context.Background()
if err := p.Start(ctx); err != nil {
panic(err)
}
// Check status
println("Running servers:", p.Len())
println("All running:", p.IsRunning())
// Stop all
defer p.Stop(ctx)
}
Pool from Configuration:
package main
import (
"context"
"github.com/nabbar/golib/httpserver"
"github.com/nabbar/golib/httpserver/pool"
)
func main() {
// Define configurations
configs := pool.Config{
httpserver.Config{
Name: "web-frontend",
Listen: "0.0.0.0:8080",
Expose: "http://example.com",
},
httpserver.Config{
Name: "api-backend",
Listen: "0.0.0.0:8081",
Expose: "http://api.example.com",
},
}
// Set global handler
configs.SetHandlerFunc(createHandler)
// Validate all configurations
if err := configs.Validate(); err != nil {
panic(err)
}
// Create pool
p, err := configs.Pool(nil, nil, nil)
if err != nil {
panic(err)
}
// Start all
p.Start(context.Background())
defer p.Stop(context.Background())
}
Advanced Filtering:
package main
import (
"github.com/nabbar/golib/httpserver/pool"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
p := pool.New(nil, handler)
// ... add servers ...
// Filter by name pattern
apiServers := p.Filter(types.FieldName, "api-*", "")
// Filter by bind address
localServers := p.Filter(types.FieldBind, "127.0.0.1:*", "")
// Filter by expose URL with regex
httpsServers := p.Filter(types.FieldExpose, "", `^https://`)
// List server names
names := p.List(types.FieldName, types.FieldName, "*", "")
for _, name := range names {
println("Server:", name)
}
// Walk through servers
p.Walk(func(bindAddr string, srv httpserver.Server) bool {
println(srv.GetName(), "at", bindAddr)
return true // continue iteration
})
}
Pool Cloning and Merging:
package main
import (
"context"
"github.com/nabbar/golib/httpserver/pool"
)
func main() {
// Original pool
p1 := pool.New(nil, handler)
// ... add servers ...
// Clone for different context
ctx2 := context.Background()
p2 := p1.Clone(ctx2) // Independent copy
// Create another pool
p3 := pool.New(nil, handler)
// ... add different servers ...
// Merge p3 into p1
if err := p1.Merge(p3, nil); err != nil {
panic(err)
}
// p1 now contains servers from both pools
}
types Subpackage
Shared type definitions and constants used across the package.
Handler Types
// FuncHandler is the function signature for handler registration
// Returns a map of handler keys to http.Handler instances
type FuncHandler func() map[string]http.Handler
// BadHandler is a default handler that returns 500 Internal Server Error
type BadHandler struct{}
// NewBadHandler creates a new BadHandler instance
func NewBadHandler() http.Handler
Field Types
// FieldType identifies server fields for filtering operations
type FieldType uint8
const (
FieldName FieldType = iota // Server name field
FieldBind // Bind address field
FieldExpose // Expose URL field
)
Constants
const (
// HandlerDefault is the default handler key
HandlerDefault = "default"
// BadHandlerName is the identifier for the bad handler
BadHandlerName = "no handler"
// TimeoutWaitingPortFreeing is the timeout for port availability checks
TimeoutWaitingPortFreeing = 250 * time.Microsecond
// TimeoutWaitingStop is the default graceful shutdown timeout
TimeoutWaitingStop = 5 * time.Second
)
Best Practices
Configuration Management
// ✅ Good: Validate before use
cfg := httpserver.Config{
Name: "production-api",
Listen: "0.0.0.0:8080",
Expose: "https://api.production.com",
}
if err := cfg.Validate(); err != nil {
log.Fatalf("Invalid config: %v", err)
}
srv, err := httpserver.New(cfg, logger.Default)
if err != nil {
log.Fatalf("Failed to create server: %v", err)
}
// ❌ Bad: Skip validation
srv, _ := httpserver.New(cfg, nil) // May fail at runtime
Graceful Shutdown
// ✅ Good: Context with timeout
func main() {
srv, _ := httpserver.New(cfg, nil)
srv.Start(context.Background())
// Wait for signal
<-shutdownChan
// Graceful shutdown with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := srv.Stop(ctx); err != nil {
log.Printf("Error stopping server: %v", err)
}
}
// ❌ Bad: Abrupt termination
srv.Stop(context.Background()) // No timeout, may hang
os.Exit(0) // Abrupt exit without cleanup
Error Handling
// ✅ Good: Check all errors
if err := srv.Start(ctx); err != nil {
log.Printf("Failed to start: %v", err)
return err
}
if srv.IsError() {
log.Printf("Server error: %v", srv.GetError())
}
// ❌ Bad: Ignore errors
srv.Start(ctx) // Silent failure
Pool Management
// ✅ Good: Centralized error handling
if err := pool.Start(ctx); err != nil {
// Aggregated errors from all servers
log.Fatalf("Pool start failed: %v", err)
}
// ✅ Good: Check individual servers
pool.Walk(func(bind string, srv httpserver.Server) bool {
if !srv.IsRunning() {
log.Printf("Server %s not running", srv.GetName())
}
return true
})
// ❌ Bad: Assume all started
pool.Start(ctx)
// No verification
Handler Registration
// ✅ Good: Register before creation
cfg.RegisterHandlerFunc(handlerFunc)
srv, _ := httpserver.New(cfg, nil)
// ✅ Good: Register after creation
srv.Handler(handlerFunc)
// ❌ Bad: No handler registered
srv, _ := httpserver.New(cfg, nil)
srv.Start(ctx) // Will use BadHandler (returns 500)
Testing
The package includes comprehensive test coverage using Ginkgo v2 and Gomega.
Test Statistics
| Package | Tests | Coverage | Status |
|---|---|---|---|
| httpserver | 83/84 | 53.8% | ✅ 98.8% Pass (1 skipped) |
| httpserver/pool | 79/79 | 63.7% | ✅ All Pass |
| httpserver/types | 32/32 | 100.0% | ✅ All Pass |
| Total | 194/195 | ~60% | ✅ 99.5% Pass |
Test Categories
- Configuration Tests: Validation, cloning, edge cases
- Server Tests: Lifecycle, info methods, TLS detection
- Handler Tests: Registration, execution, replacement
- Pool Tests: CRUD operations, filtering, merging
- Integration Tests: Actual HTTP servers (build tag:
integration)
Running Tests
# Run all unit tests
go test -v ./...
# With coverage
go test -v -cover ./...
# Generate coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# Run integration tests (starts actual servers)
go test -tags=integration -v -timeout 120s ./...
# Using Ginkgo CLI
ginkgo -v -r
# With race detection
go test -race -v ./...
# Ginkgo with integration tests
ginkgo -v -r --tags=integration --timeout=2m
See TESTING.md for detailed testing documentation.
Monitoring
Single Server Monitoring
// Basic server information
monitorName := srv.MonitorName() // Unique monitor identifier
name := srv.GetName() // Server name
bind := srv.GetBindable() // Bind address
expose := srv.GetExpose() // Expose URL
isRunning := srv.IsRunning() // Running state
uptime := srv.Uptime() // Time since start
// Monitor interface (requires version info)
monitor, err := srv.Monitor(version)
if err != nil {
log.Printf("Monitor error: %v", err)
}
Pool Monitoring
// Aggregate pool information
names := pool.MonitorNames() // All monitor names
isRunning := pool.IsRunning() // True if any server running
maxUptime := pool.Uptime() // Maximum uptime across servers
poolSize := pool.Len() // Number of servers
// Iterate through servers
pool.Walk(func(bindAddr string, srv httpserver.Server) bool {
log.Printf("Server: %s, Running: %v, Uptime: %v",
srv.GetName(), srv.IsRunning(), srv.Uptime())
return true // continue
})
// Get all monitoring data
monitors, err := pool.Monitor(version)
if err != nil {
log.Printf("Pool monitor error: %v", err)
}
Error Handling
The package uses typed errors with diagnostic codes:
Error Types
| Error Code | Description | Context |
|---|---|---|
ErrorParamEmpty |
Required parameter missing | Configuration |
ErrorHTTP2Configure |
HTTP/2 setup failed | Server initialization |
ErrorServerValidate |
Invalid server configuration | Validation |
ErrorServerStart |
Failed to start server | Startup |
ErrorPortUse |
Port already in use | Port binding |
ErrorPoolAdd |
Failed to add server to pool | Pool management |
ErrorPoolValidate |
Pool configuration invalid | Validation |
ErrorPoolStart |
Pool start failed | Startup |
ErrorPoolStop |
Pool stop failed | Shutdown |
ErrorPoolRestart |
Pool restart failed | Restart |
ErrorPoolMonitor |
Monitoring failed | Monitoring |
Error Handling Examples
// Check specific error types
if err := srv.Start(ctx); err != nil {
if errors.Is(err, ErrorPortUse) {
log.Println("Port already in use")
} else if errors.Is(err, ErrorServerValidate) {
log.Println("Configuration invalid")
}
return err
}
// Pool error aggregation
if err := pool.Start(ctx); err != nil {
// err contains all individual server errors
log.Printf("Pool start errors: %v", err)
}
// Check server error state
if srv.IsError() {
log.Printf("Server error: %v", srv.GetError())
}
Troubleshooting
Server Won't Start
// Check disabled flag
if srv.IsDisable() {
log.Println("Server is disabled in configuration")
}
// Check TLS configuration
if cfg.TLSMandatory && !srv.IsTLS() {
log.Println("TLS is mandatory but not properly configured")
}
// Check port availability
if err := srv.PortInUse(ctx, cfg.Listen); err == nil {
log.Println("Port is already in use")
}
// Check if already running
if srv.IsRunning() {
log.Println("Server is already running")
}
Pool Issues
// Check pool state
log.Printf("Pool size: %d servers", pool.Len())
// Verify server exists
if !pool.Has("127.0.0.1:8080") {
log.Println("Server not found at this address")
}
// List all servers
names := pool.List(types.FieldName, types.FieldName, "*", "")
for _, name := range names {
log.Printf("Found server: %s", name)
}
// Check individual server status
pool.Walk(func(bind string, srv httpserver.Server) bool {
if !srv.IsRunning() {
log.Printf("Server %s at %s is not running", srv.GetName(), bind)
}
return true
})
Configuration Errors
cfg := httpserver.Config{
Name: "test-server",
// Missing required fields: Listen, Expose
}
if err := cfg.Validate(); err != nil {
// err contains detailed validation failures
log.Printf("Configuration errors: %v", err)
// Example output: "Listen: required field missing"
}
Contributing
Contributions are welcome! Please follow these guidelines:
Code Contributions
- Do not use AI to generate package implementation code
- AI may assist with tests, documentation, and bug fixing
- All contributions must be thread-safe
- Pass all tests including race detection:
go test -race ./... - Maintain or improve test coverage (≥40%)
- Follow existing code style and patterns
Documentation
- Update README.md for new features
- Add code examples for common use cases
- Keep TESTING.md synchronized with test changes
- Include GoDoc comments for all public APIs
Testing
- Write tests for all new features (Ginkgo/Gomega)
- Test edge cases and error conditions
- Verify thread safety with race detector
- Add integration tests with
integrationbuild tag when appropriate
Pull Requests
- Provide clear description of changes
- Reference related issues
- Include test results (unit + integration + race)
- Update documentation
Future Enhancements
Potential improvements for future versions:
Protocol Support
- HTTP/3 (QUIC) support
- WebSocket upgrade handling
- Server-Sent Events (SSE)
Advanced Features
- Hot reload configuration without restart
- Dynamic TLS certificate rotation
- Request/response middleware chain
- Rate limiting per server
- Automatic Let's Encrypt integration
Monitoring & Observability
- Prometheus metrics endpoint
- Distributed tracing integration (OpenTelemetry)
- Structured access logs
- Performance profiling endpoints
High Availability
- Health check probes (liveness, readiness)
- Circuit breaker integration
- Automatic failover in pool
- Load balancing across pool members
Developer Experience
- Configuration hot-reload watcher
- CLI tool for server management
- Web UI for pool visualization
- More integration test helpers
Suggestions are welcome via GitHub issues.
AI Transparency Notice
In accordance with Article 50.4 of the EU AI Act, AI assistance has been used for testing, documentation, and bug fixing under human supervision.
License
MIT License - See LICENSE file for details.
Resources
- Package Documentation: GoDoc
- Testing Guide: TESTING.md
- Issues: GitHub Issues
- Contributing: CONTRIBUTING.md