go-pkgs

module
v0.5.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 5, 2026 License: MIT

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

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

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

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

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

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

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

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

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

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

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL