Documentation
¶
Overview ¶
Package server provides utilities for creating and running HTTP servers with environment-based configuration and graceful shutdown handling.
The server package integrates with the config package to automatically load server settings (port, timeouts) from environment variables. It provides graceful shutdown on interrupt signals with proper resource cleanup.
Basic usage:
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
})
if err := server.Run(context.Background(), mux); err != nil {
log.Fatal(err)
}
}
The Run function handles all server lifecycle management including:
- Loading configuration from environment variables
- Starting the HTTP server in a goroutine
- Listening for interrupt signals (SIGINT, SIGTERM)
- Performing graceful shutdown with a 10-second timeout
For more control over the server instance, use NewServerWithConfig to create an *http.Server and manage its lifecycle manually.
All functions are safe for concurrent use.
Example ¶
Example demonstrates basic usage of the server package with Run.
package main
import (
"fmt"
"net/http"
"os"
)
func main() {
// Set environment variables for configuration
os.Setenv("PORT", "8080")
os.Setenv("ENVIRONMENT", "local")
// Create a simple HTTP handler
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
})
// Note: In a real application, this would block and run the server
// For this example, we just demonstrate the API
fmt.Println("Server would start on port 8080")
// In production:
// if err := server.Run(context.Background(), mux); err != nil {
// log.Fatal(err)
// }
}
Output: Server would start on port 8080
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewServerWithConfig ¶
NewServerWithConfig creates a new http.Server configured from environment variables.
The server is configured using config.ServerConfig, which loads settings for:
- Port (env: PORT, default: 8080)
- ReadTimeout (env: READ_TIMEOUT, default: 5 seconds)
- WriteTimeout (env: WRITE_TIMEOUT, default: 10 seconds)
- IdleTimeout (env: IDLE_TIMEOUT, default: 120 seconds)
- Environment (env: ENVIRONMENT, default: Local)
The function returns an error if the configuration cannot be parsed or validated. Common error cases include invalid port numbers or timeout values.
The returned server is ready to use with ListenAndServe or Shutdown methods. For automatic lifecycle management with graceful shutdown, use the Run function instead.
This function is safe for concurrent use.
Example ¶
ExampleNewServerWithConfig shows how to create a server with custom configuration.
package main
import (
"fmt"
"io"
"log"
"log/slog"
"net/http"
"os"
"github.com/harrydayexe/GoWebUtilities/server"
)
func main() {
// Suppress log output for example
slog.SetDefault(slog.New(slog.NewTextHandler(io.Discard, nil)))
// Set custom configuration via environment
os.Setenv("PORT", "3000")
os.Setenv("READ_TIMEOUT", "30")
os.Setenv("WRITE_TIMEOUT", "30")
os.Setenv("ENVIRONMENT", "production")
// Create a handler
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
})
// Create server with configuration
srv, err := server.NewServerWithConfig(handler)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Server configured on %s\n", srv.Addr)
fmt.Printf("Read timeout: %v\n", srv.ReadTimeout)
fmt.Printf("Write timeout: %v\n", srv.WriteTimeout)
}
Output:
Example (Defaults) ¶
ExampleNewServerWithConfig_defaults shows server creation with default configuration.
package main
import (
"fmt"
"io"
"log"
"log/slog"
"net/http"
"os"
"github.com/harrydayexe/GoWebUtilities/server"
)
func main() {
// Suppress log output for example
slog.SetDefault(slog.New(slog.NewTextHandler(io.Discard, nil)))
// Clear environment to use defaults
os.Unsetenv("PORT")
os.Unsetenv("ENVIRONMENT")
os.Unsetenv("READ_TIMEOUT")
os.Unsetenv("WRITE_TIMEOUT")
os.Unsetenv("IDLE_TIMEOUT")
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
srv, err := server.NewServerWithConfig(handler)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Server address: %s\n", srv.Addr)
fmt.Printf("Read timeout: %v\n", srv.ReadTimeout)
fmt.Printf("Idle timeout: %v\n", srv.IdleTimeout)
}
Output:
Example (ErrorHandling) ¶
ExampleNewServerWithConfig_errorHandling demonstrates error handling for invalid configuration.
package main
import (
"fmt"
"net/http"
"os"
"github.com/harrydayexe/GoWebUtilities/server"
)
func main() {
// Set invalid environment
os.Setenv("ENVIRONMENT", "staging")
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
srv, err := server.NewServerWithConfig(handler)
if err != nil {
fmt.Println("Configuration error occurred")
fmt.Println("Using fallback configuration")
// Could create default server or handle error differently
return
}
fmt.Printf("Server created: %v\n", srv)
}
Output: Configuration error occurred Using fallback configuration
func Run ¶
Run starts the HTTP server with the provided handler and manages its lifecycle.
This function handles the complete server lifecycle including:
- Loading configuration from environment variables via NewServerWithConfig
- Starting the HTTP server in a background goroutine
- Listening for interrupt signals (SIGINT) on the provided context
- Performing graceful shutdown with a 10-second timeout when interrupted
The function blocks until the server is shut down, either by:
- An interrupt signal (Ctrl+C)
- Cancellation of the provided context
- A fatal error during server creation
Returns an error only if server creation fails (e.g., invalid configuration). Errors during ListenAndServe or Shutdown are logged to stderr but do not cause the function to return an error, as they may occur during normal shutdown.
Example usage:
mux := http.NewServeMux()
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
if err := server.Run(context.Background(), mux); err != nil {
log.Fatal(err)
}
This function is safe for concurrent use.
Example ¶
ExampleRun shows the complete pattern for running a server with graceful shutdown.
package main
import (
"fmt"
"net/http"
"os"
)
func main() {
// For example purposes, we'll demonstrate the pattern
// without actually starting a long-running server
// Set configuration
os.Setenv("PORT", "8080")
os.Setenv("ENVIRONMENT", "local")
// Create handler
mux := http.NewServeMux()
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("healthy"))
})
fmt.Println("Server configuration loaded")
fmt.Println("Handler registered at /health")
fmt.Println("Server would run until interrupted (Ctrl+C)")
// In production:
// if err := server.Run(context.Background(), mux); err != nil {
// log.Fatal(err)
// }
}
Output: Server configuration loaded Handler registered at /health Server would run until interrupted (Ctrl+C)
Example (HealthCheck) ¶
ExampleRun_healthCheck demonstrates a realistic health check endpoint pattern.
package main
import (
"fmt"
"net/http"
"os"
)
func main() {
// For documentation, show the pattern
os.Setenv("PORT", "8080")
os.Setenv("ENVIRONMENT", "production")
mux := http.NewServeMux()
// Health check endpoint
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status":"healthy","timestamp":"2024-01-01T00:00:00Z"}`))
})
// API endpoints
mux.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"message":"API endpoint"}`))
})
fmt.Println("Health check available at /health")
fmt.Println("API available at /api/")
fmt.Println("Server ready to accept requests")
// In production:
// if err := server.Run(context.Background(), mux); err != nil {
// log.Fatal(err)
// }
}
Output: Health check available at /health API available at /api/ Server ready to accept requests
Example (WithContext) ¶
ExampleRun_withContext shows context-based cancellation for graceful shutdown.
package main
import (
"context"
"fmt"
"io"
"log/slog"
"net/http"
"os"
"time"
"github.com/harrydayexe/GoWebUtilities/server"
)
func main() {
// Suppress log output for example
slog.SetDefault(slog.New(slog.NewTextHandler(io.Discard, nil)))
os.Setenv("PORT", "0") // Use any available port
os.Setenv("ENVIRONMENT", "test")
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
// Create cancellable context
ctx, cancel := context.WithCancel(context.Background())
// Run server in background
done := make(chan error, 1)
go func() {
done <- server.Run(ctx, handler)
}()
// Simulate doing work
time.Sleep(100 * time.Millisecond)
// Cancel context to trigger graceful shutdown
cancel()
// Wait for server to shut down
err := <-done
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Println("Server shut down gracefully")
}
}
Output:
Types ¶
This section is empty.