README
¶
go-pkgs
Go library for personal use.
A collection of utility packages, helpers, and experiments written in Go.
Usage
go get github.com/en9inerd/go-pkgs
Documentation
The package documentation is auto-generated using gomarkdoc.
flagpair
import "github.com/en9inerd/go-pkgs/flagpair"
Package flagpair provides a thin wrapper over the standard library flag package that supports paired long/short flags (e.g. "-d, --directory") and renders grouped help output in declaration order.
It targets small CLIs that want POSIX-style short/long flags without pulling in an external dependency like spf13/pflag or spf13/cobra.
Index
- type Set
- func New(name string) *Set
- func (s *Set) Bool(long, short string, def bool, usage string) *bool
- func (s *Set) Duration(long, short string, def time.Duration, usage string) *time.Duration
- func (s *Set) FlagSet() *flag.FlagSet
- func (s *Set) Int(long, short string, def int, usage string) *int
- func (s *Set) Parse(args []string) error
- func (s *Set) String(long, short, def, usage string) *string
- func (s *Set) Var(value flag.Value, long, short, usage string)
type Set
Set wraps a flag.FlagSet and tracks long/short flag pairs so that help output can render them grouped on a single line.
type Set struct {
// contains filtered or unexported fields
}
func New
func New(name string) *Set
New creates a Set using flag.ContinueOnError.
func (*Set) Bool
func (s *Set) Bool(long, short string, def bool, usage string) *bool
Bool registers a bool flag.
func (*Set) Duration
func (s *Set) Duration(long, short string, def time.Duration, usage string) *time.Duration
Duration registers a time.Duration flag.
func (*Set) FlagSet
func (s *Set) FlagSet() *flag.FlagSet
FlagSet returns the underlying flag.FlagSet for escape-hatch access (e.g. SetOutput). Flags registered directly on it will not appear in grouped usage output.
func (*Set) Int
func (s *Set) Int(long, short string, def int, usage string) *int
Int registers an int flag.
func (*Set) Parse
func (s *Set) Parse(args []string) error
Parse parses args. Returns flag.ErrHelp on -h or --help.
func (*Set) String
func (s *Set) String(long, short, def, usage string) *string
String registers a string flag. Pass short="" to skip the short alias.
func (*Set) Var
func (s *Set) Var(value flag.Value, long, short, usage string)
Var registers a flag.Value. Both names share the same value.
healthcheck
import "github.com/en9inerd/go-pkgs/healthcheck"
Index
func Check
func Check(addr string, useTLS bool) error
Simple healthcheck for distroless images
httpclient
import "github.com/en9inerd/go-pkgs/httpclient"
Package httpclient provides HTTP client utilities for making API requests
Index
- func DecodeJSONResponse(resp *http.Response, target any) error
- type Client
- func New() *Client
- func NewWithConfig(cfg Config) *Client
- func (c *Client) Delete(ctx context.Context, path string) (*http.Response, error)
- func (c *Client) DeleteJSON(ctx context.Context, path string, target any) error
- func (c *Client) Do(ctx context.Context, req *http.Request) (*http.Response, error)
- func (c *Client) Get(ctx context.Context, path string) (*http.Response, error)
- func (c *Client) GetJSON(ctx context.Context, path string, target any) error
- func (c *Client) Patch(ctx context.Context, path string, body any) (*http.Response, error)
- func (c *Client) PatchJSON(ctx context.Context, path string, body any, target any) error
- func (c *Client) Post(ctx context.Context, path string, body any) (*http.Response, error)
- func (c *Client) PostJSON(ctx context.Context, path string, body any, target any) error
- func (c *Client) Put(ctx context.Context, path string, body any) (*http.Response, error)
- func (c *Client) PutJSON(ctx context.Context, path string, body any, target any) error
- func (c *Client) WithBaseURL(baseURL string) *Client
- func (c *Client) WithHTTPClient(client *http.Client) *Client
- func (c *Client) WithHeader(key, value string) *Client
- func (c *Client) WithHeaders(headers map[string]string) *Client
- func (c *Client) WithLogger(logger *slog.Logger) *Client
- func (c *Client) WithTimeout(timeout time.Duration) *Client
- type Config
func DecodeJSONResponse
func DecodeJSONResponse(resp *http.Response, target any) error
DecodeJSONResponse decodes a JSON response from an HTTP response
type Client
Client wraps http.Client with additional utilities
type Client struct {
// contains filtered or unexported fields
}
func New
func New() *Client
New creates a new HTTP client with default settings
func NewWithConfig
func NewWithConfig(cfg Config) *Client
NewWithConfig creates a new HTTP client with custom configuration
func (*Client) Delete
func (c *Client) Delete(ctx context.Context, path string) (*http.Response, error)
Delete performs a DELETE request
func (*Client) DeleteJSON
func (c *Client) DeleteJSON(ctx context.Context, path string, target any) error
DeleteJSON performs a DELETE request and decodes the JSON response
func (*Client) Do
func (c *Client) Do(ctx context.Context, req *http.Request) (*http.Response, error)
Do executes an HTTP request
func (*Client) Get
func (c *Client) Get(ctx context.Context, path string) (*http.Response, error)
Get performs a GET request
func (*Client) GetJSON
func (c *Client) GetJSON(ctx context.Context, path string, target any) error
GetJSON performs a GET request and decodes the JSON response
func (*Client) Patch
func (c *Client) Patch(ctx context.Context, path string, body any) (*http.Response, error)
Patch performs a PATCH request with JSON body
func (*Client) PatchJSON
func (c *Client) PatchJSON(ctx context.Context, path string, body any, target any) error
PatchJSON performs a PATCH request with JSON body and decodes the JSON response
func (*Client) Post
func (c *Client) Post(ctx context.Context, path string, body any) (*http.Response, error)
Post performs a POST request with JSON body
func (*Client) PostJSON
func (c *Client) PostJSON(ctx context.Context, path string, body any, target any) error
PostJSON performs a POST request with JSON body and decodes the JSON response
func (*Client) Put
func (c *Client) Put(ctx context.Context, path string, body any) (*http.Response, error)
Put performs a PUT request with JSON body
func (*Client) PutJSON
func (c *Client) PutJSON(ctx context.Context, path string, body any, target any) error
PutJSON performs a PUT request with JSON body and decodes the JSON response
func (*Client) WithBaseURL
func (c *Client) WithBaseURL(baseURL string) *Client
WithBaseURL sets the base URL for all requests
func (*Client) WithHTTPClient
func (c *Client) WithHTTPClient(client *http.Client) *Client
WithHTTPClient sets a custom HTTP client
func (*Client) WithHeader
func (c *Client) WithHeader(key, value string) *Client
WithHeader sets a header that will be included in all requests
func (*Client) WithHeaders
func (c *Client) WithHeaders(headers map[string]string) *Client
WithHeaders sets multiple headers
func (*Client) WithLogger
func (c *Client) WithLogger(logger *slog.Logger) *Client
WithLogger sets the logger
func (*Client) WithTimeout
func (c *Client) WithTimeout(timeout time.Duration) *Client
WithTimeout sets the request timeout
type Config
Config holds client configuration
type Config struct {
Timeout time.Duration
BaseURL string
Headers map[string]string
Logger *slog.Logger
}
httperrors
import "github.com/en9inerd/go-pkgs/httperrors"
Package httperrors provides structured error types and utilities for HTTP services
Index
- func IsAPIError(err error) bool
- func IsHTTPError(err error) bool
- func IsNetworkError(err error) bool
- func IsValidationError(err error) bool
- type APIError
- func NewAPIError(code int, message string) *APIError
- func NewAPIErrorWithDetails(code int, message, details string) *APIError
- func NewAPIErrorWithErr(code int, message string, err error) *APIError
- func (e *APIError) Error() string
- func (e *APIError) Unwrap() error
- func (e *APIError) WriteJSON(w http.ResponseWriter)
- type Error
- type NetworkError
- type ValidationError
func IsAPIError
func IsAPIError(err error) bool
IsAPIError checks if an error is an APIError
func IsHTTPError
func IsHTTPError(err error) bool
IsHTTPError checks if an error is an HTTP Error
func IsNetworkError
func IsNetworkError(err error) bool
IsNetworkError checks if an error is a NetworkError
func IsValidationError
func IsValidationError(err error) bool
IsValidationError checks if an error is a ValidationError
type APIError
APIError represents an error from an external API
type APIError struct {
Code int `json:"code"`
Message string `json:"message"`
Details string `json:"details,omitempty"`
Err error `json:"-"`
}
func NewAPIError
func NewAPIError(code int, message string) *APIError
NewAPIError creates a new API error
func NewAPIErrorWithDetails
func NewAPIErrorWithDetails(code int, message, details string) *APIError
NewAPIErrorWithDetails creates a new API error with details
func NewAPIErrorWithErr
func NewAPIErrorWithErr(code int, message string, err error) *APIError
NewAPIErrorWithErr creates a new API error wrapping an underlying error. The wrapped error is available via Unwrap for errors.Is/As but is NOT exposed in the JSON response. Use NewAPIErrorWithDetails to set user-visible details.
func (*APIError) Error
func (e *APIError) Error() string
Error implements the error interface
func (*APIError) Unwrap
func (e *APIError) Unwrap() error
Unwrap returns the underlying error
func (*APIError) WriteJSON
func (e *APIError) WriteJSON(w http.ResponseWriter)
WriteJSON writes the API error as JSON to the response
type Error
Error represents a structured HTTP error
type Error struct {
Code int `json:"code"`
Message string `json:"message"`
Details string `json:"details,omitempty"`
Err error `json:"-"`
}
func NewError
func NewError(code int, message string) *Error
NewError creates a new HTTP error
func NewErrorWithDetails
func NewErrorWithDetails(code int, message, details string) *Error
NewErrorWithDetails creates a new HTTP error with details
func NewErrorWithErr
func NewErrorWithErr(code int, message string, err error) *Error
NewErrorWithErr creates a new HTTP error wrapping an underlying error. The wrapped error is available via Unwrap for errors.Is/As but is NOT exposed in the JSON response. Use NewErrorWithDetails to set user-visible details.
func (*Error) Error
func (e *Error) Error() string
Error implements the error interface
func (*Error) Unwrap
func (e *Error) Unwrap() error
Unwrap returns the underlying error
func (*Error) WriteJSON
func (e *Error) WriteJSON(w http.ResponseWriter)
WriteJSON writes the error as JSON to the response
type NetworkError
NetworkError represents a network-related error
type NetworkError struct {
Message string `json:"message"`
Err error `json:"-"`
}
func NewNetworkError
func NewNetworkError(message string, err error) *NetworkError
NewNetworkError creates a new network error
func (*NetworkError) Error
func (e *NetworkError) Error() string
Error implements the error interface
func (*NetworkError) Unwrap
func (e *NetworkError) Unwrap() error
Unwrap returns the underlying error
type ValidationError
ValidationError represents a validation error
type ValidationError struct {
FieldErrors map[string][]string `json:"fieldErrors"`
NonFieldErrors []string `json:"nonFieldErrors"`
}
func NewValidationError
func NewValidationError(fieldErrors map[string][]string, nonFieldErrors []string) *ValidationError
NewValidationError creates a new validation error
func (*ValidationError) Error
func (e *ValidationError) Error() string
Error implements the error interface
func (*ValidationError) WriteJSON
func (e *ValidationError) WriteJSON(w http.ResponseWriter)
WriteJSON writes the validation error as JSON to the response
httpjson
import "github.com/en9inerd/go-pkgs/httpjson"
Package httpjson provides common helpers for JSON-based HTTP services
Index
- func DecodeJSON[T any](r *http.Request, target *T) error
- func DecodeJSONWithLimit[T any](r *http.Request, target *T, maxSize int64) error
- func ParseDateRange(r *http.Request) (from, to time.Time, err error)
- func SendErrorJSON(w http.ResponseWriter, r *http.Request, l *slog.Logger, code int, err error, msg string)
- func WriteJSON(w http.ResponseWriter, data any)
- func WriteJSONAllowHTML(w http.ResponseWriter, v any) error
- func WriteJSONBytes(w http.ResponseWriter, data []byte)
- func WriteJSONWithStatus(w http.ResponseWriter, code int, data any)
- type JSON
func DecodeJSON
func DecodeJSON[T any](r *http.Request, target *T) error
DecodeJSON decodes JSON from request body into the given struct. The request body should be limited using SizeLimit middleware or http.MaxBytesReader to prevent DoS attacks via large JSON payloads.
func DecodeJSONWithLimit
func DecodeJSONWithLimit[T any](r *http.Request, target *T, maxSize int64) error
DecodeJSONWithLimit decodes JSON from request body into the given struct with a size limit. This prevents DoS attacks via large JSON payloads. NOTE: ResponseWriter is passed as nil to MaxBytesReader because this function doesn't have access to it. The read still errors on oversized bodies, but the connection won't be flagged for close. Use SizeLimit middleware for that.
func ParseDateRange
func ParseDateRange(r *http.Request) (from, to time.Time, err error)
ParseDateRange extracts "from" and "to" query parameters and parses them as time.Time
func SendErrorJSON
func SendErrorJSON(w http.ResponseWriter, r *http.Request, l *slog.Logger, code int, err error, msg string)
SendErrorJSON logs the error and sends a JSON error response
func WriteJSON
func WriteJSON(w http.ResponseWriter, data any)
WriteJSON encodes and writes JSON to the response with HTTP 200
func WriteJSONAllowHTML
func WriteJSONAllowHTML(w http.ResponseWriter, v any) error
WriteJSONAllowHTML encodes and writes JSON with HTML characters unescaped.
SECURITY WARNING: This function does not escape HTML characters in JSON values. Only use this function if you are certain that:
- The JSON will be properly escaped when rendered in HTML on the client side
- The JSON data does not contain user-controlled content that could lead to XSS
For most use cases, use WriteJSON instead, which escapes HTML characters by default.
func WriteJSONBytes
func WriteJSONBytes(w http.ResponseWriter, data []byte)
WriteJSONBytes writes pre-encoded JSON bytes to the response
func WriteJSONWithStatus
func WriteJSONWithStatus(w http.ResponseWriter, code int, data any)
WriteJSONWithStatus encodes and writes JSON with the given HTTP status code
type JSON
JSON is a convenience alias for a generic JSON object
type JSON map[string]any
longpoll
import "github.com/en9inerd/go-pkgs/longpoll"
Package longpoll provides a generic client for long polling HTTP requests.
Long polling is a technique where the client makes a request to the server, and the server holds the request open until it has data to send or a timeout occurs. Once the server responds, the client immediately makes another request to continue receiving updates.
This package is designed to work with various long polling APIs, including: - Telegram Bot API getUpdates - Custom long polling endpoints - Server-sent events alternatives
Key features: - Dynamic URL updates (e.g., for offset parameters like Telegram Bot API) - Support for both GET and POST requests - Automatic retry with configurable backoff - Context cancellation support - Concurrent polling operations
Example usage with static URL:
client := longpoll.NewWithConfig(longpoll.Config{
PollTimeout: 60 * time.Second,
RetryDelay: 1 * time.Second,
MaxRetries: -1, // unlimited
})
ctx := context.Background()
err := client.Poll(ctx, "https://api.example.com/events", func(resp *http.Response) (string, bool, error) {
var data map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
return "", false, err
}
// Process the data...
fmt.Printf("Received: %+v\n", data)
// Return empty string to reuse URL, true to continue polling
return "", true, nil
})
Example with Telegram Bot API (dynamic URL updates):
client := longpoll.NewWithConfig(longpoll.Config{
PollTimeout: 50 * time.Second, // Telegram max is 50s
})
botToken := "YOUR_BOT_TOKEN"
baseURL := fmt.Sprintf("https://api.telegram.org/bot%s/getUpdates", botToken)
offset := 0
err := client.Poll(ctx, fmt.Sprintf("%s?timeout=50&offset=%d", baseURL, offset),
func(resp *http.Response) (string, bool, error) {
var result struct {
OK bool `json:"ok"`
Result []struct {
UpdateID int `json:"update_id"`
} `json:"result"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return "", false, err
}
// Process updates...
if len(result.Result) > 0 {
lastUpdateID := result.Result[len(result.Result)-1].UpdateID
offset = lastUpdateID + 1
}
// Return new URL with updated offset
nextURL := fmt.Sprintf("%s?timeout=50&offset=%d", baseURL, offset)
return nextURL, true, nil
})
Index
- type Client
- func New() *Client
- func NewWithConfig(cfg Config) *Client
- func (c *Client) ActiveCount() int
- func (c *Client) Poll(ctx context.Context, url string, handler ResponseHandler) error
- func (c *Client) PollSimple(ctx context.Context, url string, handler SimpleResponseHandler) error
- func (c *Client) StopAll()
- func (c *Client) WithBodyBuilder(builder func() (io.Reader, error)) *Client
- func (c *Client) WithHeader(key, value string) *Client
- func (c *Client) WithHeaders(headers map[string]string) *Client
- func (c *Client) WithLogger(logger *slog.Logger) *Client
- func (c *Client) WithMethod(method string) *Client
- type Config
- type ResponseHandler
- type SimpleResponseHandler
type Client
Client is a long polling HTTP client.
type Client struct {
// contains filtered or unexported fields
}
func New
func New() *Client
New creates a new long polling client with default settings.
func NewWithConfig
func NewWithConfig(cfg Config) *Client
NewWithConfig creates a new long polling client with custom configuration.
func (*Client) ActiveCount
func (c *Client) ActiveCount() int
ActiveCount returns the number of active polling operations.
func (*Client) Poll
func (c *Client) Poll(ctx context.Context, url string, handler ResponseHandler) error
Poll starts a long polling loop that continuously polls the given URL. The handler function is called for each response. Polling continues until: - The context is cancelled - The handler returns shouldContinue=false - The handler returns an error - MaxRetries is exceeded (if set)
The handler can return a new URL for the next request, or an empty string to reuse the same URL. This is useful for APIs like Telegram Bot API that require updating parameters (e.g., offset) between requests.
This method blocks until polling stops. To poll in the background, call it in a goroutine.
Example
// Create a long polling client
client := NewWithConfig(Config{
PollTimeout: 60 * time.Second, // Each poll can take up to 60 seconds
RetryDelay: 1 * time.Second, // Wait 1 second between retries
MaxRetries: -1, // Unlimited retries
Logger: slog.Default(),
})
// Start polling
ctx := context.Background()
err := client.Poll(ctx, "https://api.example.com/events", func(resp *http.Response) (string, bool, error) {
// Process the response
var data map[string]any
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
return "", false, err // Stop polling on decode error
}
fmt.Printf("Received: %+v\n", data)
// Continue polling with the same URL (empty string = reuse URL)
return "", true, nil
})
if err != nil {
fmt.Printf("Polling stopped with error: %v\n", err)
}
Example (Telegram Bot API)
// Example: Using longpoll client with Telegram Bot API getUpdates
client := NewWithConfig(Config{
PollTimeout: 50 * time.Second, // Telegram max timeout is 50 seconds
RetryDelay: 1 * time.Second,
MaxRetries: -1,
})
botToken := "YOUR_BOT_TOKEN"
baseURL := fmt.Sprintf("https://api.telegram.org/bot%s/getUpdates", botToken)
offset := 0
ctx := context.Background()
err := client.Poll(ctx, fmt.Sprintf("%s?timeout=50&offset=%d", baseURL, offset),
func(resp *http.Response) (string, bool, error) {
var result struct {
OK bool `json:"ok"`
Result []struct {
UpdateID int `json:"update_id"`
} `json:"result"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return "", false, err
}
if !result.OK {
return "", false, fmt.Errorf("telegram API error")
}
// Process updates
for _, update := range result.Result {
fmt.Printf("Received update: %d\n", update.UpdateID)
// Handle the update...
}
// Update offset for next request
if len(result.Result) > 0 {
lastUpdateID := result.Result[len(result.Result)-1].UpdateID
offset = lastUpdateID + 1
}
// Return new URL with updated offset
nextURL := fmt.Sprintf("%s?timeout=50&offset=%d", baseURL, offset)
return nextURL, true, nil
})
if err != nil {
fmt.Printf("Telegram polling error: %v\n", err)
}
Example (With Context)
client := New()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
// Poll with automatic cancellation after 5 minutes
err := client.Poll(ctx, "https://api.example.com/updates", func(resp *http.Response) (string, bool, error) {
// Process response...
// Return empty string to reuse URL, false to stop, true to continue
return "", true, nil
})
if err != nil {
fmt.Printf("Polling error: %v\n", err)
}
func (*Client) PollSimple
func (c *Client) PollSimple(ctx context.Context, url string, handler SimpleResponseHandler) error
PollSimple is a convenience method that uses a SimpleResponseHandler. The URL remains constant across all requests.
func (*Client) StopAll
func (c *Client) StopAll()
StopAll stops all active polling operations.
func (*Client) WithBodyBuilder
func (c *Client) WithBodyBuilder(builder func() (io.Reader, error)) *Client
WithBodyBuilder sets a function that builds the request body for each poll.
func (*Client) WithHeader
func (c *Client) WithHeader(key, value string) *Client
WithHeader adds a header that will be included in all polling requests.
func (*Client) WithHeaders
func (c *Client) WithHeaders(headers map[string]string) *Client
WithHeaders sets multiple headers for all polling requests.
func (*Client) WithLogger
func (c *Client) WithLogger(logger *slog.Logger) *Client
WithLogger sets the logger for the client.
func (*Client) WithMethod
func (c *Client) WithMethod(method string) *Client
WithMethod sets the HTTP method for polling requests (GET, POST, etc.).
type Config
Config holds configuration for the long polling client.
type Config struct {
// PollTimeout is the timeout for each individual poll request.
// Default: 60 seconds
PollTimeout time.Duration
// RetryDelay is the delay between retries when a request fails.
// Default: 1 second
RetryDelay time.Duration
// MaxRetries is the maximum number of consecutive retries before giving up.
// Set to -1 for unlimited retries, 0 for no retries.
// When using New(), defaults to -1 (unlimited).
MaxRetries int
// HTTPClient is the underlying HTTP client to use.
// If nil, a default client will be created.
HTTPClient *http.Client
// Logger is an optional logger for debugging.
Logger *slog.Logger
// Headers are additional headers to include in each request.
Headers map[string]string
// Method is the HTTP method to use for requests. Default: GET
Method string
// BodyBuilder returns the request body for each poll.
// If nil, no body is sent.
BodyBuilder func() (io.Reader, error)
}
type ResponseHandler
ResponseHandler is a function that processes a long polling response. It receives the HTTP response and should return: - nextURL: the URL to use for the next request (empty string to reuse the same URL) - shouldContinue: true to continue polling, false to stop - error: an error to stop polling with an error
This allows handlers to dynamically update request parameters (e.g., offset for Telegram Bot API).
type ResponseHandler func(*http.Response) (nextURL string, shouldContinue bool, err error)
type SimpleResponseHandler
SimpleResponseHandler is a simplified handler that doesn't modify the URL.
type SimpleResponseHandler func(*http.Response) (bool, error)
middleware
import "github.com/en9inerd/go-pkgs/middleware"
Index
- func CORS(cfg CORSConfig) func(http.Handler) http.Handler
- func GlobalThrottle(limit int64) func(http.Handler) http.Handler
- func GlobalThrottleWithConfig(cfg ThrottleConfig) func(http.Handler) http.Handler
- func Headers(headers ...string) func(http.Handler) http.Handler
- func Health(next http.Handler) http.Handler
- func Logger(logger *slog.Logger) func(http.Handler) http.Handler
- func RateLimit(cfg RateLimitConfig) func(http.Handler) http.Handler
- func RealIP(h http.Handler) http.Handler
- func RealIPWithTrustedProxies(trustedProxies []string, h http.Handler) http.Handler
- func Recoverer(logger *slog.Logger, includeStack bool) func(http.Handler) http.Handler
- func SizeLimit(size int64) func(http.Handler) http.Handler
- func StripSlashes(next http.Handler) http.Handler
- func Timeout(timeout time.Duration) func(http.Handler) http.Handler
- func TimeoutWithMessage(timeout time.Duration, message string) func(http.Handler) http.Handler
- type CORSConfig
- type HealthResponse
- type RateLimitConfig
- type ThrottleConfig
func CORS
func CORS(cfg CORSConfig) func(http.Handler) http.Handler
CORS returns a middleware that handles cross-origin requests. When cfg.Origin is empty the middleware is a no-op. Panics if Credentials is true and Origin is "*" (forbidden by Fetch Standard).
func GlobalThrottle
func GlobalThrottle(limit int64) func(http.Handler) http.Handler
GlobalThrottle returns a middleware that limits the total number of in-flight requests across all routes in the server.
func GlobalThrottleWithConfig
func GlobalThrottleWithConfig(cfg ThrottleConfig) func(http.Handler) http.Handler
GlobalThrottleWithConfig returns a throttle middleware with custom configuration.
func Headers
func Headers(headers ...string) func(http.Handler) http.Handler
Headers middleware adds headers to response. Header values are sanitized to prevent HTTP header injection attacks.
func Health
func Health(next http.Handler) http.Handler
func Logger
func Logger(logger *slog.Logger) func(http.Handler) http.Handler
Logger middleware logs each request with method, path, client IP, response status code, and duration.
func RateLimit
func RateLimit(cfg RateLimitConfig) func(http.Handler) http.Handler
RateLimit returns middleware that enforces per-IP rate limiting using a token bucket algorithm. Each unique client IP gets its own bucket. Stale entries are cleaned up automatically.
func RealIP
func RealIP(h http.Handler) http.Handler
RealIP is a middleware that sets a http.Request's RemoteAddr to the results of parsing either the X-Forwarded-For or X-Real-IP headers.
This middleware should only be used if you can trust the headers sent with the request. If reverse proxies are configured to pass along arbitrary header values from the client, or if this middleware is used without a reverse proxy, malicious clients could set anything as X-Forwarded-For header and attack the server in various ways.
For a secure version that validates proxy IPs, use RealIPWithTrustedProxies.
func RealIPWithTrustedProxies
func RealIPWithTrustedProxies(trustedProxies []string, h http.Handler) http.Handler
RealIPWithTrustedProxies is a secure version of RealIP that only trusts X-Forwarded-For and X-Real-IP headers when the request comes from a trusted proxy. This prevents IP spoofing attacks by validating that the RemoteAddr is from a trusted source.
trustedProxies can be:
- nil or empty: Only trust headers if RemoteAddr is a private IP (assumes behind reverse proxy). This default behavior is safe if:
- Your server is always behind a reverse proxy, AND
- Direct client connections from private networks are not possible.
- List of CIDR blocks: Only trust headers if RemoteAddr matches one of the CIDRs
- List of IP addresses: Only trust headers if RemoteAddr matches one of the IPs
Example usage:
// Explicit trusted proxies (most secure)
middleware.RealIPWithTrustedProxies([]string{"10.0.0.1", "10.0.0.2"}, handler)
// Trust all private IPs (safe if behind reverse proxy)
middleware.RealIPWithTrustedProxies(nil, handler)
func Recoverer
func Recoverer(logger *slog.Logger, includeStack bool) func(http.Handler) http.Handler
Recoverer is a middleware that recovers from panics, logs the panic and returns a HTTP 500 status if possible. If includeStack is true, full stack traces are logged. In production, set includeStack to false to prevent information disclosure if logs are exposed.
func SizeLimit
func SizeLimit(size int64) func(http.Handler) http.Handler
SizeLimit middleware rejects requests with bodies larger than size.
func StripSlashes
func StripSlashes(next http.Handler) http.Handler
StripSlashes removes trailing slashes from URLs
func Timeout
func Timeout(timeout time.Duration) func(http.Handler) http.Handler
Timeout creates a timeout middleware with the default message "Request timeout"
func TimeoutWithMessage
func TimeoutWithMessage(timeout time.Duration, message string) func(http.Handler) http.Handler
TimeoutWithMessage creates a timeout middleware with a custom message
type CORSConfig
CORSConfig defines the CORS policy applied by the CORS middleware.
type CORSConfig struct {
// Origin is the value for Access-Control-Allow-Origin.
// Use "*" to allow any origin. Empty string disables the middleware.
Origin string
// Methods lists the allowed HTTP methods for preflight requests.
Methods []string
// Headers lists the allowed request headers for preflight requests.
// Defaults to ["Content-Type"] when empty, because Content-Type
// with application/json is not CORS-safelisted and would otherwise
// silently block most JSON API requests.
Headers []string
// ExposedHeaders lists response headers the browser is allowed to read.
// Empty means no extra headers are exposed.
ExposedHeaders []string
// MaxAge is the preflight cache duration in seconds.
// Defaults to 86400 (24 hours) when zero.
MaxAge int
// Credentials sets Access-Control-Allow-Credentials to "true".
// Must not be used with Origin "*".
Credentials bool
}
type HealthResponse
type HealthResponse struct {
Status string `json:"status"`
}
type RateLimitConfig
RateLimitConfig configures the per-IP rate limiting middleware.
type RateLimitConfig struct {
// RPS is the sustained requests per second allowed per IP address.
RPS float64
// Burst is the maximum number of requests allowed in a burst above the
// sustained rate. When zero, defaults to max(1, int(RPS)).
Burst int
}
type ThrottleConfig
ThrottleConfig holds configuration for the throttle middleware
type ThrottleConfig struct {
Limit int64
Message string
}
promptio
import "github.com/en9inerd/go-pkgs/promptio"
Package promptio provides context-aware interactive terminal input.
Stdin reads are not cancellable, so helpers run the read in a goroutine and return early on ctx cancellation; the goroutine leaks until process exit.
Index
- func ReadLine(ctx context.Context, msg string) (string, error)
- func ReadPassword(ctx context.Context, msg string) (string, error)
func ReadLine
func ReadLine(ctx context.Context, msg string) (string, error)
func ReadPassword
func ReadPassword(ctx context.Context, msg string) (string, error)
ratelimit
import "github.com/en9inerd/go-pkgs/ratelimit"
Package ratelimit provides rate limiting utilities for API clients
Index
type FixedWindow
FixedWindow implements a fixed window rate limiter
type FixedWindow struct {
// contains filtered or unexported fields
}
func NewFixedWindow
func NewFixedWindow(limit int, window time.Duration) *FixedWindow
NewFixedWindow creates a new fixed window rate limiter limit: maximum requests per window window: time window duration
func (*FixedWindow) Allow
func (fw *FixedWindow) Allow() bool
Allow checks if a request is allowed without blocking
func (*FixedWindow) Wait
func (fw *FixedWindow) Wait(ctx context.Context) error
Wait blocks until a request is allowed or context is cancelled
type Limiter
Limiter provides rate limiting functionality
type Limiter interface {
// Wait blocks until the limiter allows the request
Wait(ctx context.Context) error
// Allow checks if a request is allowed without blocking
Allow() bool
}
type TokenBucket
TokenBucket implements a token bucket rate limiter
type TokenBucket struct {
// contains filtered or unexported fields
}
func NewTokenBucket
func NewTokenBucket(capacity float64, refillRate float64) *TokenBucket
NewTokenBucket creates a new token bucket limiter capacity: maximum number of tokens refillRate: tokens added per second
func (*TokenBucket) Allow
func (tb *TokenBucket) Allow() bool
Allow checks if a request is allowed without blocking
func (*TokenBucket) Wait
func (tb *TokenBucket) Wait(ctx context.Context) error
Wait blocks until a token is available or context is cancelled
realip
import "github.com/en9inerd/go-pkgs/realip"
Index
func Get
func Get(r *http.Request) (string, error)
Get extracts the "real" client IP from the request. It prefers the first public IP found scanning headers right-to-left, falls back to the first valid IP seen in headers, then to RemoteAddr.
func IsPrivateIP
func IsPrivateIP(ip net.IP) bool
IsPrivateIP returns true if the IP address is loopback or in a private subnet.
retry
import "github.com/en9inerd/go-pkgs/retry"
Package retry provides utilities for retrying operations with configurable strategies
Index
- func Do(ctx context.Context, strategy *Strategy, fn func() error) error
- func DoWithResult[T any](ctx context.Context, strategy *Strategy, fn func() (T, error)) (T, error)
- func ExponentialBackoff(attempt int, initialDelay time.Duration, maxDelay time.Duration, multiplier float64) time.Duration
- func IsRetryableError(err error) bool
- type Strategy
func Do
func Do(ctx context.Context, strategy *Strategy, fn func() error) error
Do executes a function with retry logic
func DoWithResult
func DoWithResult[T any](ctx context.Context, strategy *Strategy, fn func() (T, error)) (T, error)
DoWithResult executes a function that returns a result with retry logic
func ExponentialBackoff
func ExponentialBackoff(attempt int, initialDelay time.Duration, maxDelay time.Duration, multiplier float64) time.Duration
ExponentialBackoff calculates the delay for exponential backoff. This is a utility function for implementing custom retry logic. The delay is calculated as: initialDelay * (multiplier ^ attempt), capped at maxDelay.
func IsRetryableError
func IsRetryableError(err error) bool
IsRetryableError checks if an error should be retried. This is a utility function that can be used with Strategy.RetryableErrors. It returns false for context cancellation/timeout errors, true for all others. Common retryable errors: network errors, timeouts, 5xx status codes.
type Strategy
Strategy defines a retry strategy
type Strategy struct {
MaxAttempts int
InitialDelay time.Duration
MaxDelay time.Duration
Multiplier float64
Jitter bool
RetryableErrors func(error) bool
}
func DefaultStrategy
func DefaultStrategy() *Strategy
DefaultStrategy returns a default retry strategy with exponential backoff
router
import "github.com/en9inerd/go-pkgs/router"
Package router provides a simple way to group routes and apply middleware using Go's standard http.ServeMux (Go 1.22+). It supports:
- Grouping routes under a common base path
- Attaching middleware stacks at the root or per group
- Mounting static file handlers
- Registering handlers with or without HTTP method prefixes
- Defining custom NotFound (404) handlers
Example usage:
mux := http.NewServeMux()
r := router.New(mux)
// global middleware
r.Use(loggingMiddleware)
// mount API group
api := r.Mount("/api")
api.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("pong"))
})
// serve
http.ListenAndServe(":8080", r)
Middleware added to the root group executes for every request. Middleware added to a subgroup executes only for that group's routes. The order of middleware application is the same as the order they are added, i.e. first added runs outermost.
Route patterns may be plain paths ("/foo") or include an HTTP method prefix ("GET /foo"). Root "/" patterns are normalized to "/{$}" to avoid acting as a catch-all.
Package router provides a way to group routes and apply middleware. Works with Go's standard http.ServeMux (Go 1.22+).
Index
- func Wrap(handler http.Handler, mw1 func(http.Handler) http.Handler, mws ...func(http.Handler) http.Handler) http.Handler
- type Group
- func New(mux *http.ServeMux) *Group
- func RootGroup(mux *http.ServeMux, basePath string) *Group
- func (g *Group) Group() *Group
- func (g *Group) Handle(pattern string, handler http.Handler)
- func (g *Group) HandleFiles(pattern string, root http.FileSystem)
- func (g *Group) HandleFunc(pattern string, handler http.HandlerFunc)
- func (g *Group) HandleRoot(method string, handler http.Handler)
- func (g *Group) HandleRootFunc(method string, handler http.HandlerFunc)
- func (g *Group) Handler(r *http.Request) (h http.Handler, pattern string)
- func (g *Group) Mount(basePath string) *Group
- func (g *Group) NotFoundHandler(handler http.HandlerFunc)
- func (g *Group) Route(fn func(*Group))
- func (g *Group) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (g *Group) Use(mw func(http.Handler) http.Handler, more ...func(http.Handler) http.Handler)
- func (g *Group) With(mw func(http.Handler) http.Handler, more ...func(http.Handler) http.Handler) *Group
func Wrap
func Wrap(handler http.Handler, mw1 func(http.Handler) http.Handler, mws ...func(http.Handler) http.Handler) http.Handler
Wrap applies middleware(s) around a handler.
type Group
Group represents a collection of routes with optional middleware.
type Group struct {
// contains filtered or unexported fields
}
func New
func New(mux *http.ServeMux) *Group
New creates a new root Group bound to the given mux.
func RootGroup
func RootGroup(mux *http.ServeMux, basePath string) *Group
RootGroup creates a new root Group with a base path bound to the given mux.
func (*Group) Group
func (g *Group) Group() *Group
Group creates a new subgroup with the same middleware stack.
func (*Group) Handle
func (g *Group) Handle(pattern string, handler http.Handler)
Handle registers a route with middlewares applied.
func (*Group) HandleFiles
func (g *Group) HandleFiles(pattern string, root http.FileSystem)
HandleFiles serves static files.
func (*Group) HandleFunc
func (g *Group) HandleFunc(pattern string, handler http.HandlerFunc)
HandleFunc registers a route handler function.
func (*Group) HandleRoot
func (g *Group) HandleRoot(method string, handler http.Handler)
HandleRoot registers a handler for the group's root without redirect.
func (*Group) HandleRootFunc
func (g *Group) HandleRootFunc(method string, handler http.HandlerFunc)
HandleRootFunc registers a root handler func.
func (*Group) Handler
func (g *Group) Handler(r *http.Request) (h http.Handler, pattern string)
Handler proxies to mux.Handler.
func (*Group) Mount
func (g *Group) Mount(basePath string) *Group
Mount creates a new subgroup with a base path.
func (*Group) NotFoundHandler
func (g *Group) NotFoundHandler(handler http.HandlerFunc)
NotFoundHandler sets a custom 404 handler on the root group.
func (*Group) Route
func (g *Group) Route(fn func(*Group))
Route configures the group inside the provided function.
func (*Group) ServeHTTP
func (g *Group) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP implements http.Handler for the group.
func (*Group) Use
func (g *Group) Use(mw func(http.Handler) http.Handler, more ...func(http.Handler) http.Handler)
Use appends middleware(s) to the group.
func (*Group) With
func (g *Group) With(mw func(http.Handler) http.Handler, more ...func(http.Handler) http.Handler) *Group
With returns a new group with appended middleware(s).
validator
import "github.com/en9inerd/go-pkgs/validator"
Package validator provides functionality for validating and sanitizing data.
Index
- func Between(value, min, max int) bool
- func BetweenFloat(value, min, max float64) bool
- func Blank(value string) bool
- func InRange(value, min, max int) bool
- func InRangeFloat(value, min, max float64) bool
- func IsAlpha(value string) bool
- func IsAlphanumeric(value string) bool
- func IsEmail(value string) bool
- func IsHTTPURL(value string) bool
- func IsNumber(value string) bool
- func IsNumeric(value string) bool
- func IsURL(value string) bool
- func Matches(value string, pattern *regexp.Regexp) bool
- func MaxChars(value string, n int) bool
- func MaxDuration(d, maxDuration time.Duration) bool
- func MaxFloat(value, max float64) bool
- func MaxInt(value, max int) bool
- func MinChars(value string, n int) bool
- func MinDuration(d, minDuration time.Duration) bool
- func MinFloat(value, min float64) bool
- func MinInt(value, min int) bool
- func NotBlank(value string) bool
- func PermittedValue[T comparable](value T, permittedValues ...T) bool
- func ValidateRequest(req Validatable) error
- func ValidateRequestWithValidator(req Validatable, v *Validator) error
- type Validatable
- type Validator
func Between
func Between(value, min, max int) bool
Between returns true if the integer value is between min and max (inclusive) Alias for InRange for better readability in some contexts
func BetweenFloat
func BetweenFloat(value, min, max float64) bool
BetweenFloat returns true if the float value is between min and max (inclusive) Alias for InRangeFloat for better readability in some contexts
func Blank
func Blank(value string) bool
Blank returns true if the string is empty or contains only whitespace.
func InRange
func InRange(value, min, max int) bool
InRange returns true if the integer value is between min and max (inclusive)
func InRangeFloat
func InRangeFloat(value, min, max float64) bool
InRangeFloat returns true if the float value is between min and max (inclusive)
func IsAlpha
func IsAlpha(value string) bool
IsAlpha returns true if the string contains only alphabetic characters
func IsAlphanumeric
func IsAlphanumeric(value string) bool
IsAlphanumeric returns true if the string contains only alphanumeric characters
func IsEmail
func IsEmail(value string) bool
IsEmail returns true if the string is a valid email address
func IsHTTPURL
func IsHTTPURL(value string) bool
IsHTTPURL returns true if the string is a valid HTTP or HTTPS URL
func IsNumber
func IsNumber(value string) bool
IsNumber returns true if the string represents a valid integer.
func IsNumeric
func IsNumeric(value string) bool
IsNumeric returns true if the string contains only numeric characters
func IsURL
func IsURL(value string) bool
IsURL returns true if the string is a valid URL
func Matches
func Matches(value string, pattern *regexp.Regexp) bool
Matches returns true if the string matches the provided regular expression.
func MaxChars
func MaxChars(value string, n int) bool
MaxChars returns true if the string contains no more than n characters.
func MaxDuration
func MaxDuration(d, maxDuration time.Duration) bool
MaxDuration returns true if the duration is less than or equal to maxDuration.
func MaxFloat
func MaxFloat(value, max float64) bool
MaxFloat returns true if value is less than or equal to max.
func MaxInt
func MaxInt(value, max int) bool
MaxInt returns true if value is less than or equal to max.
func MinChars
func MinChars(value string, n int) bool
MinChars returns true if the string contains at least n characters.
func MinDuration
func MinDuration(d, minDuration time.Duration) bool
MinDuration returns true if the duration is greater than or equal to minDuration.
func MinFloat
func MinFloat(value, min float64) bool
MinFloat returns true if value is greater than or equal to min.
func MinInt
func MinInt(value, min int) bool
MinInt returns true if value is greater than or equal to min.
func NotBlank
func NotBlank(value string) bool
NotBlank returns true if the string is not empty or whitespace.
func PermittedValue
func PermittedValue[T comparable](value T, permittedValues ...T) bool
PermittedValue returns true if value is among the provided permittedValues.
func ValidateRequest
func ValidateRequest(req Validatable) error
ValidateRequest validates a request that implements the Validatable interface and returns an error if validation fails
func ValidateRequestWithValidator
func ValidateRequestWithValidator(req Validatable, v *Validator) error
ValidateRequestWithValidator validates a request using a provided validator This allows for custom validation logic or reusing a validator instance
type Validatable
Validatable defines an interface for structs that can validate themselves using a Validator.
type Validatable interface {
Validate(v *Validator)
}
type Validator
Validator collects field and non-field validation errors.
type Validator struct {
FieldErrors map[string][]string `json:"fieldErrors"`
NonFieldErrors []string `json:"nonFieldErrors"`
}
func (*Validator) AddFieldError
func (v *Validator) AddFieldError(key, message string)
AddFieldError adds an error message to a specific field.
func (*Validator) AddNonFieldError
func (v *Validator) AddNonFieldError(message string)
AddNonFieldError adds a general error message not associated with a specific field.
func (*Validator) CheckField
func (v *Validator) CheckField(ok bool, key, message string)
CheckField adds a field error if the provided condition is false.
func (*Validator) JSON
func (v *Validator) JSON() []byte
JSON returns the validation errors as a JSON byte slice.
func (*Validator) Valid
func (v *Validator) Valid() bool
Valid returns true if there are no field or non-field validation errors.
Generated by gomarkdoc
License
MIT
Directories
¶
| Path | Synopsis |
|---|---|
|
Package flagpair provides a thin wrapper over the standard library flag package that supports paired long/short flags (e.g.
|
Package flagpair provides a thin wrapper over the standard library flag package that supports paired long/short flags (e.g. |
|
Package httpclient provides HTTP client utilities for making API requests
|
Package httpclient provides HTTP client utilities for making API requests |
|
Package httperrors provides structured error types and utilities for HTTP services
|
Package httperrors provides structured error types and utilities for HTTP services |
|
Package httpjson provides common helpers for JSON-based HTTP services
|
Package httpjson provides common helpers for JSON-based HTTP services |
|
Package longpoll provides a generic client for long polling HTTP requests.
|
Package longpoll provides a generic client for long polling HTTP requests. |
|
Package promptio provides context-aware interactive terminal input.
|
Package promptio provides context-aware interactive terminal input. |
|
Package ratelimit provides rate limiting utilities for API clients
|
Package ratelimit provides rate limiting utilities for API clients |
|
Package retry provides utilities for retrying operations with configurable strategies
|
Package retry provides utilities for retrying operations with configurable strategies |
|
Package router provides a simple way to group routes and apply middleware using Go's standard http.ServeMux (Go 1.22+).
|
Package router provides a simple way to group routes and apply middleware using Go's standard http.ServeMux (Go 1.22+). |
|
Package validator provides functionality for validating and sanitizing data.
|
Package validator provides functionality for validating and sanitizing data. |