Documentation
¶
Overview ¶
Package types provides core type definitions and constants for HTTP server implementations.
Overview ¶
This package defines foundational types, constants, and utilities used across the httpserver ecosystem. It serves as a shared vocabulary for server configuration, handler registration, and field identification in server management operations.
The package provides:
- Field type enumeration for server property filtering
- Handler registration function signatures
- Fallback error handlers for misconfigured servers
- Timeout constants for server lifecycle management
Design Philosophy ¶
The types package follows these design principles:
1. Minimal Dependencies: The package depends only on the standard library, making it suitable as a foundation for higher-level packages without circular dependencies.
2. Type Safety: Custom types like FieldType provide compile-time safety for server filtering operations, preventing invalid field specifications.
3. Fail-Safe Defaults: The BadHandler provides a safe fallback that always returns HTTP 500, ensuring misconfigured servers fail visibly rather than silently.
4. Constants Over Magic Values: Named constants like TimeoutWaitingStop and HandlerDefault improve code readability and maintainability.
Key Features ¶
Field Type System: FieldType enumeration enables type-safe server filtering by name, bind address, or expose URL. This is primarily used by the pool package for server management.
Handler Registration: FuncHandler defines the contract for dynamic handler registration, allowing multiple named handlers per server instance.
Fallback Handling: BadHandler provides a safe default when no valid handler is configured, preventing nil pointer panics and providing clear error signals.
Timeout Management: Pre-defined timeout constants standardize server lifecycle operations like graceful shutdown and port availability checks.
Architecture ¶
The package structure is intentionally flat, providing building blocks for higher-level abstractions:
┌────────────────────────────────────────────────────┐
│ httpserver/types │
├────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Field Types │ │ Handler Types │ │
│ ├──────────────────┤ ├──────────────────┤ │
│ │ FieldType │ │ FuncHandler │ │
│ │ - FieldName │ │ BadHandler │ │
│ │ - FieldBind │ │ NewBadHandler() │ │
│ │ - FieldExpose │ │ │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Constants │ │ Handler Keys │ │
│ ├──────────────────┤ ├──────────────────┤ │
│ │ TimeoutWaiting │ │ HandlerDefault │ │
│ │ PortFreeing │ │ BadHandlerName │ │
│ │ TimeoutWaiting │ │ │ │
│ │ Stop │ │ │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
└────────────────────────────────────────────────────┘
│
▼
Used by httpserver, httpserver/pool,
and other HTTP server components
Usage Patterns ¶
## Field Type Filtering
FieldType enables type-safe filtering of servers by specific attributes:
switch filterField {
case types.FieldName:
// Filter by server name
case types.FieldBind:
// Filter by bind address
case types.FieldExpose:
// Filter by expose URL
}
## Handler Registration
FuncHandler defines how handlers are registered with servers:
handlerFunc := func() map[string]http.Handler {
return map[string]http.Handler{
types.HandlerDefault: myDefaultHandler,
"api": myAPIHandler,
"admin": myAdminHandler,
}
}
## Fallback Handler
BadHandler provides safe error handling for misconfigured servers:
handler := types.NewBadHandler() // Returns HTTP 500 for all requests
## Timeout Constants
Standard timeouts for server lifecycle operations:
ctx, cancel := context.WithTimeout(ctx, types.TimeoutWaitingStop) defer cancel() server.Shutdown(ctx)
Field Types ¶
FieldType is an enumeration for identifying server properties in filtering and listing operations:
FieldName: Server name identifier FieldBind: Server listen address (e.g., ":8080", "127.0.0.1:9000") FieldExpose: Server public expose URL (e.g., "https://example.com")
These types are primarily used by the pool package to filter and retrieve servers based on specific criteria.
Handler Types ¶
## FuncHandler
FuncHandler defines the signature for handler registration functions. It returns a map of handler identifiers to http.Handler instances:
Key patterns: - "" or "default": Default handler for unmatched routes - "api": API-specific handler - "admin": Administrative interface handler - Custom keys: Application-specific handler identifiers
## BadHandler
BadHandler is a fallback http.Handler that returns HTTP 500 Internal Server Error for all requests. It's used when:
- No handler is registered for a server
- Handler registration fails
- Configuration errors prevent proper handler setup
The handler serves as a visible failure indicator rather than panicking or silently ignoring requests.
Constants ¶
## Timeout Constants
TimeoutWaitingPortFreeing (250µs):
- Duration for polling port availability before binding
- Used in port conflict detection and retry logic
- Short duration suitable for tight polling loops
TimeoutWaitingStop (5s):
- Default timeout for graceful server shutdown
- Allows ongoing requests to complete before forced termination
- Balances graceful shutdown with reasonable wait times
## Handler Identifier Constants
HandlerDefault ("default"):
- Standard key for default handler registration
- Used when no specific handler key is configured
- Fallback handler for unmatched routes
BadHandlerName ("no handler"):
- Identifier string for BadHandler instances
- Used in logging and monitoring
- Indicates misconfigured or missing handler
Integration with httpserver Ecosystem ¶
This package integrates with:
httpserver: Core server implementation uses these types httpserver/pool: Server pooling uses FieldType for filtering httpserver/config: Configuration uses timeout constants
Performance Considerations ¶
Type Overhead:
- FieldType is a uint8 (1 byte), minimal memory overhead
- Handler maps are created on-demand, not stored statically
- BadHandler is stateless, safe to create multiple instances
Constant Access:
- All constants are compile-time values
- Zero runtime overhead for constant access
- No initialization required
Handler Performance:
- BadHandler.ServeHTTP is a trivial function (single line)
- No allocations, no blocking operations
- Suitable for high-frequency error scenarios
Limitations ¶
1. No Dynamic Field Types: FieldType is a closed enumeration. Adding new field types requires modifying this package.
2. No Handler Lifecycle Management: BadHandler does not implement graceful shutdown or resource cleanup. It's stateless by design.
3. Fixed Timeout Values: Timeout constants are not configurable at runtime. Applications needing custom timeouts should define their own.
4. No Validation: The package does not validate handler maps returned by FuncHandler. Validation is the responsibility of consuming packages.
5. Single Error Status: BadHandler always returns 500. It does not support custom error codes or messages.
Use Cases ¶
## Server Pool Management
Filter servers in a pool by specific attributes:
// Find all servers listening on a specific address servers := pool.FilterByField(types.FieldBind, ":8080")
## Multi-Handler Server Configuration
Register multiple handlers for different routes or purposes:
cfg.HandlerFunc = func() map[string]http.Handler {
return map[string]http.Handler{
types.HandlerDefault: webHandler,
"api": apiHandler,
"metrics": metricsHandler,
}
}
## Graceful Shutdown
Use standard timeout for server shutdown:
shutdownCtx, cancel := context.WithTimeout(
context.Background(),
types.TimeoutWaitingStop,
)
defer cancel()
server.Shutdown(shutdownCtx)
## Safe Default Handler
Provide fallback when handler registration fails:
handler := getConfiguredHandler()
if handler == nil {
handler = types.NewBadHandler()
}
Thread Safety ¶
All types in this package are safe for concurrent use:
- FieldType is a simple enumeration (immutable)
- Constants are immutable by definition
- BadHandler is stateless and safe for concurrent ServeHTTP calls
- FuncHandler signature does not enforce thread safety; implementations must ensure their own thread safety if called concurrently
Error Handling ¶
The package provides minimal error handling as it defines types rather than implementing complex logic:
- BadHandler signals errors via HTTP 500 status code
- No errors are returned by package functions
- Invalid FieldType values are not validated at runtime
Consumer packages are responsible for validating inputs and handling errors appropriately.
Best Practices ¶
DO:
- Use FieldType constants for server filtering to avoid typos
- Use HandlerDefault for primary handler registration
- Use NewBadHandler() as a safe fallback for missing handlers
- Use timeout constants for consistent server lifecycle management
- Document custom handler keys in application code
DON'T:
- Don't cast arbitrary integers to FieldType
- Don't rely on BadHandler for production traffic
- Don't modify timeout constants (they're package-level)
- Don't assume FuncHandler implementations are thread-safe
- Don't use BadHandler for intentional error responses
Testing ¶
The package includes comprehensive testing:
- Field type enumeration and uniqueness
- Constant value verification
- BadHandler HTTP response validation
- FuncHandler signature compliance
- Integration with http.Handler interface
See fields_test.go and handler_test.go for detailed test specifications.
Related Packages ¶
- net/http: Standard library HTTP server interfaces
- github.com/nabbar/golib/httpserver: HTTP server implementation
- github.com/nabbar/golib/httpserver/pool: Server pool management
For usage examples, see example_test.go in this package.
Example (BadHandlerMultipleRequests) ¶
Example_badHandlerMultipleRequests demonstrates BadHandler with multiple requests. Shows that BadHandler consistently returns 500 for all requests.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
handler := types.NewBadHandler()
methods := []string{http.MethodGet, http.MethodPost, http.MethodPut}
for _, method := range methods {
req := httptest.NewRequest(method, "/test", nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
fmt.Printf("%s: %d\n", method, w.Code)
}
}
Output: GET: 500 POST: 500 PUT: 500
Example (BadHandlerName) ¶
Example_badHandlerName demonstrates the BadHandlerName constant. This shows the identifier used for BadHandler instances.
package main
import (
"fmt"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
handlerName := types.BadHandlerName
fmt.Printf("Bad handler identifier: %s\n", handlerName)
}
Output: Bad handler identifier: no handler
Example (Complete) ¶
Example_complete demonstrates combining all types package features. This is the most complex example showing realistic integration.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
type srvCfg struct {
name string
bind string
expose string
handler http.Handler
}
createServerConfig := func(name, bind, expose string, handlerFunc types.FuncHandler) srvCfg {
handlers := handlerFunc()
handler := handlers[types.HandlerDefault]
if handler == nil {
handler = types.NewBadHandler()
}
return srvCfg{
name: name,
bind: bind,
expose: expose,
handler: handler,
}
}
handlerFunc := func() map[string]http.Handler {
return map[string]http.Handler{
types.HandlerDefault: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}),
}
}
cfg := createServerConfig("api-server", ":8080", "https://api.example.com", handlerFunc)
fmt.Printf("Server: %s\n", cfg.name)
fmt.Printf("Bind: %s\n", cfg.bind)
fmt.Printf("Expose: %s\n", cfg.expose)
req := httptest.NewRequest(http.MethodGet, "/", nil)
w := httptest.NewRecorder()
cfg.handler.ServeHTTP(w, req)
fmt.Printf("Handler status: %d\n", w.Code)
}
Output: Server: api-server Bind: :8080 Expose: https://api.example.com Handler status: 200
Example (FieldTypeComparison) ¶
Example_fieldTypeComparison demonstrates comparing FieldType values. This is useful for validation and conditional logic.
package main
import (
"fmt"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
field1 := types.FieldName
field2 := types.FieldName
field3 := types.FieldBind
fmt.Printf("field1 == field2: %t\n", field1 == field2)
fmt.Printf("field1 == field3: %t\n", field1 == field3)
}
Output: field1 == field2: true field1 == field3: false
Example (FieldTypeMap) ¶
Example_fieldTypeMap demonstrates using FieldType as map keys. This pattern is useful for storing field-specific configurations.
package main
import (
"fmt"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
fieldNames := map[types.FieldType]string{
types.FieldName: "server-name",
types.FieldBind: "bind-address",
types.FieldExpose: "expose-url",
}
fields := []types.FieldType{types.FieldName, types.FieldBind, types.FieldExpose}
for _, field := range fields {
fmt.Printf("Field %d: %s\n", field, fieldNames[field])
}
}
Output: Field 0: server-name Field 1: bind-address Field 2: expose-url
Example (FieldTypeSwitch) ¶
Example_fieldTypeSwitch demonstrates using FieldType in switch statements. This shows how to handle different field types in filtering operations.
package main
import (
"fmt"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
fields := []types.FieldType{
types.FieldName,
types.FieldBind,
types.FieldExpose,
}
for _, field := range fields {
switch field {
case types.FieldName:
fmt.Println("Filtering by name")
case types.FieldBind:
fmt.Println("Filtering by bind address")
case types.FieldExpose:
fmt.Println("Filtering by expose URL")
}
}
}
Output: Filtering by name Filtering by bind address Filtering by expose URL
Example (FuncHandler) ¶
Example_funcHandler demonstrates implementing FuncHandler. This is a simple example of handler registration function.
package main
import (
"fmt"
"net/http"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
var handlerFunc types.FuncHandler
handlerFunc = func() map[string]http.Handler {
return map[string]http.Handler{
types.HandlerDefault: http.NotFoundHandler(),
}
}
handlers := handlerFunc()
fmt.Printf("Handlers returned: %d\n", len(handlers))
}
Output: Handlers returned: 1
Example (FuncHandlerMultiple) ¶
Example_funcHandlerMultiple demonstrates multiple handler registration. This shows how to register multiple named handlers.
package main
import (
"fmt"
"net/http"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
var handlerFunc types.FuncHandler
handlerFunc = func() map[string]http.Handler {
return map[string]http.Handler{
types.HandlerDefault: types.NewBadHandler(),
"api": http.NotFoundHandler(),
"admin": http.NotFoundHandler(),
}
}
handlers := handlerFunc()
fmt.Printf("Total handlers: %d\n", len(handlers))
keys := []string{types.HandlerDefault, "api", "admin"}
for _, key := range keys {
if _, exists := handlers[key]; exists {
fmt.Printf("Handler key: %s\n", key)
}
}
}
Output: Total handlers: 3 Handler key: default Handler key: api Handler key: admin
Example (HandlerDefault) ¶
Example_handlerDefault demonstrates using the HandlerDefault constant. This shows the standard key for default handler registration.
package main
import (
"fmt"
"net/http"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
handlers := map[string]http.Handler{
types.HandlerDefault: http.NotFoundHandler(),
}
if handler, exists := handlers[types.HandlerDefault]; exists {
fmt.Printf("Default handler registered: %t\n", handler != nil)
}
}
Output: Default handler registered: true
Example (HandlerRegistration) ¶
Example_handlerRegistration demonstrates complete handler registration. This shows a realistic multi-handler server configuration.
package main
import (
"fmt"
"net/http"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
createHandlers := func() types.FuncHandler {
return func() map[string]http.Handler {
apiHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
webHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
return map[string]http.Handler{
types.HandlerDefault: webHandler,
"api": apiHandler,
}
}
}
handlerFunc := createHandlers()
handlers := handlerFunc()
fmt.Printf("Registered handlers: %d\n", len(handlers))
if _, exists := handlers[types.HandlerDefault]; exists {
fmt.Println("Default handler: configured")
}
if _, exists := handlers["api"]; exists {
fmt.Println("API handler: configured")
}
}
Output: Registered handlers: 2 Default handler: configured API handler: configured
Example (HandlerValidation) ¶
Example_handlerValidation demonstrates validating handler registration. This shows a pattern for checking handler configuration.
package main
import (
"fmt"
"net/http"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
validateHandlers := func(handlerFunc types.FuncHandler) error {
if handlerFunc == nil {
return fmt.Errorf("handler function is nil")
}
handlers := handlerFunc()
if handlers == nil {
return fmt.Errorf("handlers map is nil")
}
if handlers[types.HandlerDefault] == nil {
return fmt.Errorf("default handler not configured")
}
return nil
}
validFunc := func() map[string]http.Handler {
return map[string]http.Handler{
types.HandlerDefault: http.NotFoundHandler(),
}
}
if err := validateHandlers(validFunc); err != nil {
fmt.Printf("Validation failed: %v\n", err)
} else {
fmt.Println("Validation passed")
}
}
Output: Validation passed
Example (HandlerWithFallback) ¶
Example_handlerWithFallback demonstrates using BadHandler as fallback. This pattern provides safe defaults when handler registration fails.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
var handlerFunc types.FuncHandler
handlerFunc = func() map[string]http.Handler {
return nil
}
handlers := handlerFunc()
var handler http.Handler
if handlers == nil || handlers[types.HandlerDefault] == nil {
handler = types.NewBadHandler()
fmt.Println("Using fallback handler")
} else {
handler = handlers[types.HandlerDefault]
fmt.Println("Using configured handler")
}
req := httptest.NewRequest(http.MethodGet, "/", nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
fmt.Printf("Status: %d\n", w.Code)
}
Output: Using fallback handler Status: 500
Example (ServerFiltering) ¶
Example_serverFiltering demonstrates filtering pattern with FieldType. This shows a realistic server filtering scenario.
package main
import (
"fmt"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
type srv struct {
name string
bind string
expose string
}
servers := []srv{
{name: "api-server", bind: ":8080", expose: "https://api.example.com"},
{name: "web-server", bind: ":8081", expose: "https://www.example.com"},
{name: "admin-server", bind: ":8082", expose: "https://admin.example.com"},
}
filterByField := func(field types.FieldType, value string) []srv {
var result []srv
for _, s := range servers {
var match bool
switch field {
case types.FieldName:
match = s.name == value
case types.FieldBind:
match = s.bind == value
case types.FieldExpose:
match = s.expose == value
}
if match {
result = append(result, s)
}
}
return result
}
results := filterByField(types.FieldBind, ":8080")
for _, s := range results {
fmt.Printf("Found: %s\n", s.name)
}
}
Output: Found: api-server
Example (TimeoutConstants) ¶
Example_timeoutConstants demonstrates using timeout constants. This shows how to access and use predefined timeout values.
package main
import (
"fmt"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
portTimeout := types.TimeoutWaitingPortFreeing
stopTimeout := types.TimeoutWaitingStop
fmt.Printf("Port freeing timeout: %v\n", portTimeout)
fmt.Printf("Stop timeout: %v\n", stopTimeout)
}
Output: Port freeing timeout: 3s Stop timeout: 5s
Index ¶
Examples ¶
- Package (BadHandlerMultipleRequests)
- Package (BadHandlerName)
- Package (Complete)
- Package (FieldTypeComparison)
- Package (FieldTypeMap)
- Package (FieldTypeSwitch)
- Package (FuncHandler)
- Package (FuncHandlerMultiple)
- Package (HandlerDefault)
- Package (HandlerRegistration)
- Package (HandlerValidation)
- Package (HandlerWithFallback)
- Package (ServerFiltering)
- Package (TimeoutConstants)
- FieldType
- NewBadHandler
Constants ¶
const ( // TimeoutWaitingPortFreeing is the timeout duration for checking if a port becomes available. // Used when verifying port availability before binding. TimeoutWaitingPortFreeing = 3 * time.Second // TimeoutWaitingStop is the default timeout for graceful server shutdown. // Servers have 5 seconds to complete ongoing requests before forced termination. TimeoutWaitingStop = 5 * time.Second // BadHandlerName is the identifier string for the BadHandler. // Used in logging and monitoring to indicate no valid handler is configured. BadHandlerName = "no handler" )
const HandlerDefault = "default"
HandlerDefault is the default key used for handler registration when no specific key is provided.
Variables ¶
This section is empty.
Functions ¶
func NewBadHandler ¶
NewBadHandler creates a default error handler that returns HTTP 500 Internal Server Error. This handler is used as a fallback when no valid handler is registered for a server.
Returns:
- http.Handler: A handler that always returns 500 status code
Example ¶
ExampleNewBadHandler demonstrates creating a fallback error handler. This is the simplest way to create a safe default handler.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
handler := types.NewBadHandler()
req := httptest.NewRequest(http.MethodGet, "/", nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
fmt.Printf("Status: %d\n", w.Code)
}
Output: Status: 500
Types ¶
type BadHandler ¶
type BadHandler struct{}
BadHandler is a default HTTP handler that returns 500 Internal Server Error for all requests. It's used as a fallback when no proper handler is configured for a server instance.
func (BadHandler) ServeHTTP ¶
func (o BadHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request)
ServeHTTP implements http.Handler interface, returning HTTP 500 for all requests. This indicates that no valid handler was configured for the server.
type FieldType ¶
type FieldType uint8
FieldType identifies server fields for filtering and listing operations. Used primarily by the pool package to filter servers by specific attributes.
Example ¶
ExampleFieldType demonstrates basic usage of FieldType enumeration. This is the simplest use case for field identification.
package main
import (
"fmt"
"github.com/nabbar/golib/httpserver/types"
)
func main() {
field := types.FieldName
fmt.Printf("Field type: %d\n", field)
}
Output: Field type: 0
type FuncHandler ¶
FuncHandler is the function signature for handler registration. It returns a map where keys are handler identifiers and values are http.Handler instances. The "default" key or empty string "" is used when no specific handler key is configured.
Example:
func() map[string]http.Handler {
return map[string]http.Handler{
"": defaultHandler,
"api": apiHandler,
"admin": adminHandler,
}
}