httputils

package
v1.5.0 Latest Latest
Warning

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

Go to latest
Published: Aug 24, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package httputils provides reusable HTTP utility components.

Index

Constants

View Source
const MaxRequestBodyBytes int64 = 16 * 1024 * 1024

MaxRequestBodyBytes caps every body read through ReadBody. AWS API request payloads top out at 6 MiB (Lambda synchronous invoke) or 5 GiB streamed (S3 PutObject, which uses its own streaming path and does not call ReadBody). 16 MiB leaves headroom for unusually large API requests while preventing unbounded memory growth from attacker-controlled bodies.

View Source
const TagsPathPrefix = "/tags/"

TagsPathPrefix is the shared "/tags/{resourceArn}" prefix multiple services expose for TagResource/UntagResource/ListTagsForResource.

Variables

This section is empty.

Functions

func DrainBody

func DrainBody(r *http.Request)

DrainBody reads and discards the request body. This is important for HTTP keep-alive, as the server needs to know the request body has been fully consumed before reusing the connection.

func EchoError

func EchoError(ctx context.Context, c *echo.Context, code int, message string, err error) error

EchoError is a helper for Echo handlers to write errors with proper logging.

func ExtractAccessKeyFromRequest added in v1.3.1

func ExtractAccessKeyFromRequest(r *http.Request) string

ExtractAccessKeyFromRequest extracts the AWS access key ID from an HTTP request. It checks the SigV4 Authorization header credential scope first, then the X-Amz-Credential query parameter, and returns an empty string if none is found.

func ExtractRegionFromRequest

func ExtractRegionFromRequest(r *http.Request, defaultRegion string) string

ExtractRegionFromRequest extracts the AWS region from an HTTP request. It checks the SigV4 Authorization header credential scope first, then query credential, then the X-Amz-Region header, then falls back to defaultRegion.

func ExtractServiceFromRequest

func ExtractServiceFromRequest(r *http.Request) string

ExtractServiceFromRequest extracts the AWS service name from the SigV4 Authorization header credential scope or X-Amz-Credential query parameter. Returns an empty string if the service name cannot be determined.

func GetBuffer added in v1.3.1

func GetBuffer() *bytes.Buffer

GetBuffer acquires a clean *bytes.Buffer from the pool.

func GetCRC32 added in v1.3.1

func GetCRC32() hash.Hash32

GetCRC32 retrieves a pooled CRC32 IEEE hasher with state reset.

func GetCRC32C added in v1.3.1

func GetCRC32C() hash.Hash32

GetCRC32C retrieves a pooled CRC32C (Castagnoli) hasher with state reset.

func GetMD5 added in v1.3.1

func GetMD5() hash.Hash

GetMD5 retrieves a pooled MD5 hasher with state reset.

func GetOperation

func GetOperation(ctx context.Context) string

GetOperation retrieves the operation name from context, or "Unknown" if not set.

func GetResource

func GetResource(ctx context.Context) string

GetResource retrieves the resource identifier from context, or "" if not set.

func GetSHA256 added in v1.3.1

func GetSHA256() hash.Hash

GetSHA256 retrieves a pooled SHA256 hasher with state reset.

func MatchesTaggedResourceARN added in v1.3.1

func MatchesTaggedResourceARN(path, serviceName string) bool

MatchesTaggedResourceARN reports whether path is a "/tags/{resourceArn}" request whose ARN names serviceName (arn:{partition}:{serviceName}:...). Several services share the "/tags/" prefix; only the ARN's own service segment reliably disambiguates the true owner -- a bare prefix match steals every other service's tag requests too (see gopherstack-sokq).

func PutBuffer added in v1.3.1

func PutBuffer(buf *bytes.Buffer)

PutBuffer returns a *bytes.Buffer to the pool. Buffers larger than maxPooledBufferSize are discarded to bound memory retention.

func PutCRC32 added in v1.3.1

func PutCRC32(h hash.Hash32)

PutCRC32 returns a CRC32 IEEE hasher to the pool.

func PutCRC32C added in v1.3.1

func PutCRC32C(h hash.Hash32)

PutCRC32C returns a CRC32C hasher to the pool.

func PutMD5 added in v1.3.1

func PutMD5(h hash.Hash)

PutMD5 returns an MD5 hasher to the pool.

func PutSHA256 added in v1.3.1

func PutSHA256(h hash.Hash)

PutSHA256 returns a SHA256 hasher to the pool.

func ReadBody

func ReadBody(r *http.Request) ([]byte, error)

ReadBody reads the request body and returns it as a byte slice. It handles cases where r.Body might be nil (e.g. in some test environments). It re-seeds the request body so it can be read multiple times and ensures the original request body is closed. It uses a custom ReadCloser to avoid redundant reads and allocations if called multiple times. The body is capped at MaxRequestBodyBytes; reads that exceed the cap return an `*http.MaxBytesError` (use errors.As to detect and translate to a 413 response). Because this helper has no access to the originating ResponseWriter, it cannot auto-write the 413 itself — callers must handle the size-cap error and respond appropriately.

func RequestIDMiddleware

func RequestIDMiddleware() echo.MiddlewareFunc

RequestIDMiddleware returns an Echo middleware that injects an x-amz-request-id header (a new UUID) into every HTTP response.

func SanitizeHeaderString

func SanitizeHeaderString(s string) string

SanitizeHeaderString removes all characters except alphanumeric, hyphens, underscores, and periods. This breaks the taint for static analysis tools like CodeQL which flag raw header values in logs.

func ScopedPrefixMatch added in v1.3.1

func ScopedPrefixMatch(r *http.Request, path, prefix, serviceName string) bool

ScopedPrefixMatch reports whether path has the given prefix AND the request's SigV4 signing scope, if present, permits serviceName to claim it: an unsigned request (no Authorization header, or none carrying a recognizable scope) still matches, but a request signed for a different, known service does not. Use this in a RouteMatcher instead of a bare strings.HasPrefix whenever the path shape is one another service's real wire API could also produce -- a bare prefix match steals that service's requests (see gopherstack-vpoh: iotdataplane's own "/connections/{id}" swallowed Outposts' GetConnection).

func SetOperation

func SetOperation(ctx context.Context, operation string) context.Context

SetOperation returns a new context with the operation name updated. This follows the idiomatic context pattern - immutable values.

func SetOperationAndResource

func SetOperationAndResource(ctx context.Context, operation, resource string) context.Context

SetOperationAndResource returns a new context with both operation and resource set. This is a convenience function to set both at once without intermediate contexts.

func SetResource

func SetResource(ctx context.Context, resource string) context.Context

SetResource returns a new context with the resource identifier updated. This follows the idiomatic context pattern - immutable values.

func WriteDynamoDBResponse

func WriteDynamoDBResponse(ctx context.Context, w http.ResponseWriter, code int, payload any)

WriteDynamoDBResponse writes a DynamoDB-style JSON response with CRC32 checksum. Sets Content-Type to "application/x-amz-json-1.0" and X-Amz-Crc32.

func WriteError

func WriteError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error, code int)

WriteError writes an error response with structured logging. Uses the logger from ctx to record the error with context. Drains the request body to ensure connection reuse.

func WriteJSON

func WriteJSON(ctx context.Context, w http.ResponseWriter, code int, payload any)

WriteJSON marshals the payload to JSON, sets standard headers, and writes the response. Sets Content-Type to "application/json" and Content-Length.

func WriteS3ErrorResponse

func WriteS3ErrorResponse(ctx context.Context, w http.ResponseWriter, r *http.Request, s3Err any, code int)

WriteS3ErrorResponse writes an S3-compatible XML error response. Drains the request body and writes the error as XML.

func WriteXML

func WriteXML(ctx context.Context, w http.ResponseWriter, code int, payload any)

WriteXML writes an XML response with the given status code. The full body is buffered before writing it to the response.

Types

type OperationKey

type OperationKey struct{}

OperationKey is a type-safe context key for storing operation metadata.

type ResponseWriter

type ResponseWriter struct {
	http.ResponseWriter
	// contains filtered or unexported fields
}

ResponseWriter wraps http.ResponseWriter and tracks the HTTP status code. Use this when you need to inspect the status after WriteHeader is called.

func NewResponseWriter

func NewResponseWriter(w http.ResponseWriter) *ResponseWriter

NewResponseWriter creates a ResponseWriter that wraps the given http.ResponseWriter.

func (*ResponseWriter) StatusCode

func (w *ResponseWriter) StatusCode() int

StatusCode returns the HTTP status code that was written.

func (*ResponseWriter) Write

func (w *ResponseWriter) Write(b []byte) (int, error)

Write sets status to http.StatusOK if not already set, then delegates to wrapped ResponseWriter.

func (*ResponseWriter) WriteHeader

func (w *ResponseWriter) WriteHeader(code int)

WriteHeader writes the status code and delegates to the wrapped ResponseWriter.

type SigV4Error

type SigV4Error struct {
	Code    string
	Message string
	Status  int
}

SigV4Error is the AWS error returned when validation fails. The Code field drives the X-Amzn-Errortype header / error code clients expect.

type SigV4Validator

type SigV4Validator struct {
	// contains filtered or unexported fields
}

SigV4Validator cryptographically verifies AWS Signature Version 4 on incoming requests. It is OFF by default; the caller opts in via NewSigV4Validator and the EchoMiddleware. When enabled, requests whose recomputed signature does not match the Authorization header are rejected with the AWS-accurate error.

Verification re-derives the signing key from a single configured secret key (the access-key-id in the request is informational only — gopherstack is a single-tenant simulator). This mirrors how AWS validates: only the secret is secret; everything else is reconstructed from the request.

func NewSigV4Validator

func NewSigV4Validator(secretKey string) *SigV4Validator

NewSigV4Validator builds a validator that checks signatures against secretKey. A blank secretKey is treated as "test" — the common AWS dummy credential — so the default localstack-style client (AWS_SECRET_ACCESS_KEY=test) validates.

func (*SigV4Validator) EchoMiddleware

func (v *SigV4Validator) EchoMiddleware() echo.MiddlewareFunc

EchoMiddleware returns Echo middleware that validates SigV4 on every request. Requests without an Authorization header are passed through unchanged (many gopherstack internal/health/dashboard calls are unsigned); only requests that present a SigV4 Authorization header are verified. This keeps anonymous and presigned-URL flows working while still rejecting tampered signed requests.

func (*SigV4Validator) Verify

func (v *SigV4Validator) Verify(r *http.Request) *SigV4Error

Verify recomputes the SigV4 signature for r and compares it to the signature in the Authorization header. It returns nil on a match, or a *SigV4Error describing the AWS-accurate rejection otherwise. Verify reads and restores the request body so downstream handlers still see it.

Jump to

Keyboard shortcuts

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