Documentation
¶
Overview ¶
Package httputil provides common HTTP utilities for service handlers.
Index ¶
- func BadRequest(w http.ResponseWriter, message string)
- func CanonicalizeServiceID(raw string) string
- func ClientIP(r *http.Request) string
- func Conflict(w http.ResponseWriter, message string)
- func CopyHTTPClientWithTimeout(base *http.Client, timeout time.Duration, force bool) *http.Client
- func DecodeJSON(w http.ResponseWriter, r *http.Request, v interface{}) bool
- func DecodeJSONOptional(w http.ResponseWriter, r *http.Request, v interface{}) bool
- func DefaultTransportWithMinTLS12() http.RoundTripper
- func Forbidden(w http.ResponseWriter, message string)
- func GetServiceID(r *http.Request) string
- func GetUserID(r *http.Request) string
- func GetUserRole(r *http.Request) string
- func InternalError(w http.ResponseWriter, message string)
- func NewClient(cfg ClientConfig, defaults ClientDefaults) (*http.Client, error)
- func NewClientWithBaseURL(cfg ClientConfig, defaults ClientDefaults) (*http.Client, string, error)
- func NormalizeBaseURL(raw string, opts BaseURLOptions) (string, *url.URL, error)
- func NormalizeServiceBaseURL(raw string) (string, *url.URL, error)
- func NotFound(w http.ResponseWriter, message string)
- func PaginationParams(r *http.Request, defaultLimit, maxLimit int) (offset, limit int)
- func PathParam(path, prefix, suffix string) string
- func PathParamAt(path string, index int) string
- func QueryBool(r *http.Request, key string, defaultVal bool) bool
- func QueryInt(r *http.Request, key string, defaultVal int) int
- func QueryInt64(r *http.Request, key string, defaultVal int64) int64
- func QueryString(r *http.Request, key, defaultVal string) string
- func ReadAllStrict(r io.Reader, limit int64) ([]byte, error)
- func ReadAllWithLimit(r io.Reader, limit int64) (body []byte, truncated bool, err error)
- func RequireAdminRole(w http.ResponseWriter, r *http.Request) bool
- func RequireServiceID(w http.ResponseWriter, r *http.Request) (string, bool)
- func RequireUserID(w http.ResponseWriter, r *http.Request) (string, bool)
- func ResolveMaxBodyBytes(cfg int64, defaultBytes int64) int64
- func ResolveServiceID(serviceID string) string
- func SecureCipherSuites() []uint16
- func SecureTLSConfig() *tls.Config
- func ServiceUnavailable(w http.ResponseWriter, message string)
- func StrictIdentityMode() bool
- func Unauthorized(w http.ResponseWriter, message string)
- func WrapError(err error, message string) error
- func WriteError(w http.ResponseWriter, status int, message string)
- func WriteErrorResponse(w http.ResponseWriter, r *http.Request, status int, code, message string, ...)
- func WriteErrorWithCode(w http.ResponseWriter, status int, code, message string)
- func WriteJSON(w http.ResponseWriter, status int, data interface{})
- type BaseURLOptions
- type BodyTooLargeError
- type ClientConfig
- type ClientDefaults
- type ErrorResponse
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BadRequest ¶
func BadRequest(w http.ResponseWriter, message string)
BadRequest writes a 400 Bad Request response.
func CanonicalizeServiceID ¶
CanonicalizeServiceID maps legacy service IDs and DNS names (e.g. "accountpool") to the canonical service ID used throughout the service layer.
func ClientIP ¶
ClientIP extracts the best-effort client IP address from the request.
Security model:
- If the direct peer is on a private network (typical for ingress/proxy), trust X-Forwarded-For / X-Real-IP.
- If the request comes directly from the internet, ignore spoofable forwarded headers and fall back to RemoteAddr.
func Conflict ¶
func Conflict(w http.ResponseWriter, message string)
Conflict writes a 409 Conflict response.
func CopyHTTPClientWithTimeout ¶
CopyHTTPClientWithTimeout returns a shallow copy of base with its Timeout set.
It is safe to use with shared clients (e.g., Marble HTTP clients) because it never mutates the caller-provided instance.
If base is nil, it returns a new http.Client. If base.Timeout is zero, the timeout is always set. If force is true, the timeout is set even when base.Timeout is non-zero.
func DecodeJSON ¶
func DecodeJSON(w http.ResponseWriter, r *http.Request, v interface{}) bool
DecodeJSON decodes a JSON request body into the provided struct. Returns false and writes an error response if decoding fails.
func DecodeJSONOptional ¶
func DecodeJSONOptional(w http.ResponseWriter, r *http.Request, v interface{}) bool
DecodeJSONOptional decodes a JSON request body into the provided struct when present. It returns true when the body is empty and no decoding is needed.
func DefaultTransportWithMinTLS12 ¶
func DefaultTransportWithMinTLS12() http.RoundTripper
DefaultTransportWithMinTLS12 clones http.DefaultTransport (when possible) and enforces a modern TLS baseline for outbound calls.
This helper is used by multiple clients (Supabase, chain RPC, external API integrations) to avoid duplicating transport-cloning logic and to ensure TLS 1.2+ is consistently enforced.
SECURITY: Restricts cipher suites to AEAD ciphers with forward secrecy.
func Forbidden ¶
func Forbidden(w http.ResponseWriter, message string)
Forbidden writes a 403 Forbidden response.
func GetServiceID ¶
GetServiceID returns the authenticated caller service ID. In strict environments (production/SGX/MarbleRun TLS), this is derived from the verified mTLS peer identity to avoid header spoofing.
func GetUserID ¶
GetUserID extracts the user ID from the X-User-ID header. Returns empty string if not present.
func GetUserRole ¶
GetUserRole extracts the user role from the X-User-Role header.
func InternalError ¶
func InternalError(w http.ResponseWriter, message string)
InternalError writes a 500 Internal Server Error response.
func NewClient ¶
func NewClient(cfg ClientConfig, defaults ClientDefaults) (*http.Client, error)
NewClient creates an HTTP client with standardized configuration. It handles: - Base URL normalization (optional) - Timeout handling with defaults - Max body size limits - Service ID trimming
Example:
client, err := NewClient(ClientConfig{
BaseURL: cfg.ServiceURL,
ServiceID: "my-service",
HTTPClient: marble.HTTPClient(),
}, ClientDefaults{
Timeout: 15 * time.Second,
})
func NewClientWithBaseURL ¶
func NewClientWithBaseURL(cfg ClientConfig, defaults ClientDefaults) (*http.Client, string, error)
NewClientWithBaseURL creates a client with base URL normalization. This is the most common pattern for service-to-service clients. Returns the HTTP client and normalized base URL.
func NormalizeBaseURL ¶
NormalizeBaseURL normalizes and validates a base URL used for service-to-service calls.
It trims whitespace, removes trailing slashes, validates scheme/host, disallows user info, and optionally enforces https in strict identity mode.
func NormalizeServiceBaseURL ¶
NormalizeServiceBaseURL is the standard normalization used by service clients. It enforces https whenever strict identity mode is enabled.
func NotFound ¶
func NotFound(w http.ResponseWriter, message string)
NotFound writes a 404 Not Found response.
func PaginationParams ¶
PaginationParams extracts pagination parameters from the request.
func PathParam ¶
PathParam extracts a path parameter from the URL. Example: PathParam("/users/123/orders", "/users/", "/orders") returns "123"
func PathParamAt ¶
PathParamAt extracts a path parameter at the given index (0-based). Example: PathParamAt("/users/123/orders/456", 1) returns "123"
func QueryInt64 ¶
QueryInt64 extracts an int64 query parameter with a default value.
func QueryString ¶
QueryString extracts a string query parameter with a default value.
func ReadAllStrict ¶
ReadAllStrict reads the full body from r up to limit bytes. If the body exceeds limit, it returns a *BodyTooLargeError.
func ReadAllWithLimit ¶
ReadAllWithLimit reads up to limit bytes from r. It returns the bytes read, whether the body exceeded the limit, and any I/O error.
This is useful for logging or building error messages without risking OOM.
func RequireAdminRole ¶
func RequireAdminRole(w http.ResponseWriter, r *http.Request) bool
RequireAdminRole verifies the user role is admin or super_admin. Returns false and writes a 403 Forbidden response if the role check fails.
func RequireServiceID ¶
RequireServiceID extracts the service ID from the request context. Returns false and writes an error response if not present.
func RequireUserID ¶
RequireUserID extracts the user ID from the X-User-ID header. Returns false and writes an error response if not present.
func ResolveMaxBodyBytes ¶
ResolveMaxBodyBytes returns the effective max body size from config and defaults.
func ResolveServiceID ¶
ResolveServiceID returns a trimmed service ID or empty string.
func SecureCipherSuites ¶
func SecureCipherSuites() []uint16
SecureCipherSuites returns the list of secure TLS 1.2 cipher suites. SECURITY: Only includes AEAD ciphers with forward secrecy (ECDHE). TLS 1.3 cipher suites are managed by Go automatically.
func SecureTLSConfig ¶
SecureTLSConfig returns a secure TLS configuration. SECURITY: Enforces TLS 1.2 minimum with secure cipher suites only.
func ServiceUnavailable ¶
func ServiceUnavailable(w http.ResponseWriter, message string)
ServiceUnavailable writes a 503 Service Unavailable response.
func StrictIdentityMode ¶
func StrictIdentityMode() bool
StrictIdentityMode returns true when the service should only trust identity headers that are protected by verified mTLS.
func Unauthorized ¶
func Unauthorized(w http.ResponseWriter, message string)
Unauthorized writes a 401 Unauthorized response.
func WriteError ¶
func WriteError(w http.ResponseWriter, status int, message string)
WriteError writes a JSON error response.
func WriteErrorResponse ¶
func WriteErrorResponse(w http.ResponseWriter, r *http.Request, status int, code, message string, details interface{})
WriteErrorResponse writes a standard JSON error response envelope.
func WriteErrorWithCode ¶
func WriteErrorWithCode(w http.ResponseWriter, status int, code, message string)
WriteErrorWithCode writes a JSON error response with an error code.
func WriteJSON ¶
func WriteJSON(w http.ResponseWriter, status int, data interface{})
WriteJSON writes a JSON response with the given status code.
Types ¶
type BaseURLOptions ¶
type BaseURLOptions struct {
// RequireHTTPSInStrictMode enforces https URLs whenever runtime.StrictIdentityMode()
// is enabled (production/SGX/MarbleRun TLS).
RequireHTTPSInStrictMode bool
}
BaseURLOptions configures NormalizeBaseURL.
type BodyTooLargeError ¶
type BodyTooLargeError struct {
Limit int64
}
BodyTooLargeError is returned by ReadAllStrict when the body exceeds the limit.
func (*BodyTooLargeError) Error ¶
func (e *BodyTooLargeError) Error() string
type ClientConfig ¶
type ClientConfig struct {
// BaseURL is the base URL for the service (will be normalized)
BaseURL string
// ServiceID identifies the caller for service mesh authentication
ServiceID string
// Timeout is the request timeout. Zero means use default.
Timeout time.Duration
// HTTPClient is the base HTTP client to use (e.g., Marble mTLS client)
// If nil, a default client will be created.
HTTPClient *http.Client
// MaxBodyBytes caps response body size to prevent memory exhaustion.
// Zero means use default.
MaxBodyBytes int64
}
ClientConfig holds standard client configuration used across all service clients. This eliminates duplication of client creation logic.
type ClientDefaults ¶
type ClientDefaults struct {
Timeout time.Duration
MaxBodyBytes int64
NormalizeBaseURL bool
RequireHTTPS bool
}
ClientDefaults holds default values for client configuration.
func DefaultClientDefaults ¶
func DefaultClientDefaults() ClientDefaults
DefaultClientDefaults returns standard default values.
type ErrorResponse ¶
type ErrorResponse struct {
Code string `json:"code"`
Message string `json:"message"`
Details interface{} `json:"details,omitempty"`
TraceID string `json:"trace_id,omitempty"`
}
ErrorResponse represents a standard error response.