middleware

package
v1.19.0 Latest Latest
Warning

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

Go to latest
Published: Aug 18, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Index

Constants

View Source
const RateLimitRequestListenerPriority = 200

RateLimitRequestListenerPriority places the limiter ahead of the security chain: token resolution listens at 50 and access control at 20, so a request over budget is answered before it pays an authenticator round — and before a refusal ends the request without the middleware chain ever being built.

Variables

This section is empty.

Functions

func CompressionMiddleware

func CompressionMiddleware(config *CompressionConfig) httpcontract.Middleware

func CorsMiddleware deprecated

func CorsMiddleware(config *CorsConfig) httpcontract.Middleware

Deprecated: use github.com/precision-soft/melody/http/cors.Middleware instead.

func DefaultClientIp added in v1.11.0

func DefaultClientIp(request httpcontract.Request) string

func DefaultCompressionMiddleware

func DefaultCompressionMiddleware() httpcontract.Middleware

func DefaultCorsMiddleware deprecated

func DefaultCorsMiddleware() httpcontract.Middleware

Deprecated: use github.com/precision-soft/melody/http/cors.DefaultMiddleware instead.

func IpRateLimit

func IpRateLimit(requestsPerMinute int) httpcontract.Middleware

IpRateLimit keys on the direct peer address, so behind a reverse proxy every client shares the proxy's single budget. It builds its config internally and returns only the middleware, so the resolver cannot be set afterwards: use IpRateLimitWithResolver with NewForwardedClientIpResolver where a trusted edge sits in front.

func IpRateLimitWithResolver added in v1.19.0

func IpRateLimitWithResolver(
	requestsPerMinute int,
	clientIpResolver ClientIpResolver,
) httpcontract.Middleware

IpRateLimitWithResolver is IpRateLimit with the client address read through the given resolver — pass NewForwardedClientIpResolver with the same policy handed to Kernel.SetForwardedHeadersPolicy so a per-IP budget behind a reverse proxy is charged to the client rather than to the proxy. A nil resolver keeps the direct peer.

func RateLimitMiddleware

func RateLimitMiddleware(config *RateLimitConfig) httpcontract.Middleware

func RegisterRateLimitRequestListener added in v1.19.0

func RegisterRateLimitRequestListener(
	eventDispatcher eventcontract.EventDispatcher,
	config *RateLimitConfig,
)

RegisterRateLimitRequestListener meters every request on kernel.request, before authentication and access control. RateLimitMiddleware meters only what reaches the handler path: a request the security chain refuses is answered before the middleware chain is built, so a burst of wrong credentials consumes no budget there. This door charges that burst and answers it once the budget is gone. The default key is the client address, which exists before any token is resolved; a key extractor reading the authenticated identity falls back the same way the middleware does. Both doors share the configuration, so registering both meters a request once per door — use distinct budgets or one door.

func RestrictiveCors deprecated

func RestrictiveCors(allowedOrigins ...string) httpcontract.Middleware

Deprecated: use github.com/precision-soft/melody/http/cors.Restrictive instead.

func SimpleRateLimit

func SimpleRateLimit(requestsPerMinute int) httpcontract.Middleware

SimpleRateLimit keys on the direct peer address, so behind a reverse proxy every client shares the proxy's single budget. It builds its config internally and returns only the middleware, so the resolver cannot be set afterwards: use SimpleRateLimitWithResolver with NewForwardedClientIpResolver where a trusted edge sits in front.

func SimpleRateLimitWithResolver added in v1.19.0

func SimpleRateLimitWithResolver(
	requestsPerMinute int,
	clientIpResolver ClientIpResolver,
) httpcontract.Middleware

SimpleRateLimitWithResolver is SimpleRateLimit with the client address read through the given resolver — pass NewForwardedClientIpResolver with the same policy handed to Kernel.SetForwardedHeadersPolicy so a per-IP budget behind a reverse proxy is charged to the client rather than to the proxy. A nil resolver keeps the direct peer.

func StaticMiddleware

func StaticMiddleware(
	options *static.Options,
) httpcontract.Middleware

func UserRateLimit

func UserRateLimit(
	requestsPerMinute int,
	getUserId KeyExtractor,
) httpcontract.Middleware

UserRateLimit falls back to the direct peer address for a request that carries no user id, so behind a reverse proxy every anonymous client shares the proxy's single budget. It builds its config internally and returns only the middleware, so the resolver cannot be set afterwards: use UserRateLimitWithResolver with NewForwardedClientIpResolver where a trusted edge sits in front.

func UserRateLimitWithResolver added in v1.19.0

func UserRateLimitWithResolver(
	requestsPerMinute int,
	getUserId KeyExtractor,
	clientIpResolver ClientIpResolver,
) httpcontract.Middleware

UserRateLimitWithResolver is UserRateLimit with the anonymous fallback address read through the given resolver — pass NewForwardedClientIpResolver with the same policy handed to Kernel.SetForwardedHeadersPolicy so unauthenticated traffic behind a reverse proxy is charged per client rather than to the proxy. A nil resolver keeps the direct peer.

Types

type ClientIpResolver added in v1.11.0

type ClientIpResolver = func(httpcontract.Request) string

func NewForwardedClientIpResolver added in v1.16.0

func NewForwardedClientIpResolver(policy httpcontract.ForwardedHeadersPolicy) ClientIpResolver

NewForwardedClientIpResolver returns a ClientIpResolver that walks X-Forwarded-For right-to-left, skipping addresses that match the trusted proxy list, and returns the first untrusted address — the real client as attested by the trusted edge. It reuses the same ForwardedHeadersPolicy handed to Kernel.SetForwardedHeadersPolicy, so there is a single trusted-proxy list to maintain. It falls back to DefaultClientIp — the direct peer — whenever the forwarded chain cannot be trusted: forwarded headers are not trusted by policy, the trusted list is empty, the direct peer is not a trusted proxy (the header is then attacker-controlled), the chain has no parseable untrusted address, or every entry is a trusted proxy. Plug it into a rate-limit config with SetClientIpResolver so per-IP limits behind a reverse proxy key on the client instead of the proxy.

type CompressionConfig

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

func DefaultCompressionConfig

func DefaultCompressionConfig() *CompressionConfig

func NewCompressionConfig

func NewCompressionConfig(
	level int,
	minSize int,
	excludedContentTypes []string,
	excludedPaths []string,
) *CompressionConfig

func (*CompressionConfig) ExcludedContentTypes

func (instance *CompressionConfig) ExcludedContentTypes() []string

func (*CompressionConfig) ExcludedPaths

func (instance *CompressionConfig) ExcludedPaths() []string

func (*CompressionConfig) Level

func (instance *CompressionConfig) Level() int

func (*CompressionConfig) MinSize

func (instance *CompressionConfig) MinSize() int

func (*CompressionConfig) SetExcludedContentTypes

func (instance *CompressionConfig) SetExcludedContentTypes(excludedContentTypes []string)

func (*CompressionConfig) SetExcludedPaths

func (instance *CompressionConfig) SetExcludedPaths(excludedPaths []string)

func (*CompressionConfig) SetLevel

func (instance *CompressionConfig) SetLevel(level int)

func (*CompressionConfig) SetMinSize

func (instance *CompressionConfig) SetMinSize(minSize int)

type CorsConfig deprecated

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

Deprecated: use github.com/precision-soft/melody/http/cors.Service instead.

func DefaultCorsConfig deprecated

func DefaultCorsConfig() *CorsConfig

Deprecated: use github.com/precision-soft/melody/http/cors.DefaultService instead.

func NewCorsConfig deprecated

func NewCorsConfig(
	allowOrigins []string,
	allowMethods []string,
	allowHeaders []string,
	exposeHeaders []string,
	allowCredentials bool,
	maxAge int,
	allowOriginFunc func(origin string) bool,
) *CorsConfig

Deprecated: use github.com/precision-soft/melody/http/cors.NewService instead.

func RestrictiveCorsConfig deprecated

func RestrictiveCorsConfig(allowedOrigins []string) *CorsConfig

Deprecated: use github.com/precision-soft/melody/http/cors.RestrictiveService instead.

func (*CorsConfig) AllowCredentials

func (instance *CorsConfig) AllowCredentials() bool

func (*CorsConfig) AllowHeaders

func (instance *CorsConfig) AllowHeaders() []string

func (*CorsConfig) AllowMethods

func (instance *CorsConfig) AllowMethods() []string

func (*CorsConfig) AllowOriginFunc

func (instance *CorsConfig) AllowOriginFunc() func(origin string) bool

func (*CorsConfig) AllowOrigins

func (instance *CorsConfig) AllowOrigins() []string

func (*CorsConfig) ExposeHeaders

func (instance *CorsConfig) ExposeHeaders() []string

func (*CorsConfig) MaxAge

func (instance *CorsConfig) MaxAge() int

func (*CorsConfig) SetAllowHeaders

func (instance *CorsConfig) SetAllowHeaders(allowHeaders []string)

func (*CorsConfig) SetAllowMethods

func (instance *CorsConfig) SetAllowMethods(allowMethods []string)

func (*CorsConfig) SetAllowOrigins

func (instance *CorsConfig) SetAllowOrigins(allowOrigins []string)

type FixedWindowLimiter added in v1.19.0

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

func NewFixedWindowLimiter added in v1.19.0

func NewFixedWindowLimiter(rate int, window time.Duration) *FixedWindowLimiter

NewFixedWindowLimiter builds a limiter whose counters live in THIS process and nowhere else, which is the one thing to weigh before it guards anything that matters. The map is built at construction and dies with the process, so every restart hands each caller a full budget back — under a supervisor that restarts quickly, a limit of five per hour becomes five per restart — and it is not shared across replicas, so a limit of five is five per instance and the deployment enforces five times the number of them. That is the right trade for shaping ordinary traffic and the wrong one for login, one-time-password or password-reset routes, where the limit is a security control: those want a store the whole deployment sees, which is what integrations/rueidis.RateLimiter is — the distributed drop-in for these limiters. Melody says the same thing at boot about the two other defaults that live in the process, its cache backend and its session storage; it cannot say it about a limiter, because a limiter is wired by the application rather than by the framework.

func NewFixedWindowLimiterWithClock added in v1.19.0

func NewFixedWindowLimiterWithClock(clockInstance clockcontract.Clock, rate int, window time.Duration) *FixedWindowLimiter

func NewTokenBucketLimiter deprecated

func NewTokenBucketLimiter(rate int, window time.Duration) *FixedWindowLimiter

Deprecated: use NewFixedWindowLimiter.

func NewTokenBucketLimiterWithClock deprecated

func NewTokenBucketLimiterWithClock(clockInstance clockcontract.Clock, rate int, window time.Duration) *FixedWindowLimiter

Deprecated: use NewFixedWindowLimiterWithClock.

func (*FixedWindowLimiter) Allow added in v1.19.0

func (instance *FixedWindowLimiter) Allow(key string) bool

func (*FixedWindowLimiter) Close added in v1.19.0

func (instance *FixedWindowLimiter) Close() error

func (*FixedWindowLimiter) Reset added in v1.19.0

func (instance *FixedWindowLimiter) Reset(key string)

func (*FixedWindowLimiter) SetMaxKeys added in v1.19.0

func (instance *FixedWindowLimiter) SetMaxKeys(maxKeys int)

SetMaxKeys bounds how many distinct keys the limiter tracks. When the map is full and an idle-entry prune frees nothing, a request under an unseen key is denied rather than minting a bucket, so an attacker varying the key cannot grow the map without bound. A non-positive value is ignored.

type KeyExtractor

type KeyExtractor = func(httpcontract.Request) string

type OnLimitExceeded

type OnLimitExceeded = func(httpcontract.Request) (httpcontract.Response, error)

type RateLimitConfig

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

func NewRateLimitConfig

func NewRateLimitConfig(
	limiter httpcontract.RateLimiter,
	keyExtractor KeyExtractor,
	onLimitExceeded OnLimitExceeded,
) *RateLimitConfig

func (*RateLimitConfig) ClientIpResolver added in v1.11.0

func (instance *RateLimitConfig) ClientIpResolver() ClientIpResolver

func (*RateLimitConfig) KeyExtractor

func (instance *RateLimitConfig) KeyExtractor() KeyExtractor

func (*RateLimitConfig) Limiter

func (instance *RateLimitConfig) Limiter() httpcontract.RateLimiter

func (*RateLimitConfig) OnLimitExceeded

func (instance *RateLimitConfig) OnLimitExceeded() OnLimitExceeded

func (*RateLimitConfig) SetClientIpResolver added in v1.11.0

func (instance *RateLimitConfig) SetClientIpResolver(resolver ClientIpResolver)

func (*RateLimitConfig) SetKeyExtractor

func (instance *RateLimitConfig) SetKeyExtractor(keyExtractor KeyExtractor)

func (*RateLimitConfig) SetOnLimitExceeded

func (instance *RateLimitConfig) SetOnLimitExceeded(onLimitExceeded OnLimitExceeded)

type SlidingWindowLimiter

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

func NewSlidingWindowLimiter

func NewSlidingWindowLimiter(limit int, window time.Duration) *SlidingWindowLimiter

NewSlidingWindowLimiter holds its timestamps in THIS process, exactly as NewFixedWindowLimiter holds its counters: a restart returns every caller's full budget and each replica enforces the limit on its own, so the deployment allows the limit times the number of instances. Where the limit is a security control rather than traffic shaping, use the distributed drop-in in integrations/rueidis.

func NewSlidingWindowLimiterWithClock

func NewSlidingWindowLimiterWithClock(clockInstance clockcontract.Clock, limit int, window time.Duration) *SlidingWindowLimiter

func (*SlidingWindowLimiter) Allow

func (instance *SlidingWindowLimiter) Allow(key string) bool

func (*SlidingWindowLimiter) Close

func (instance *SlidingWindowLimiter) Close() error

func (*SlidingWindowLimiter) Reset

func (instance *SlidingWindowLimiter) Reset(key string)

func (*SlidingWindowLimiter) SetMaxKeys added in v1.19.0

func (instance *SlidingWindowLimiter) SetMaxKeys(maxKeys int)

SetMaxKeys bounds how many distinct keys the limiter tracks. When the map is full and an idle-entry prune frees nothing, a request under an unseen key is denied rather than minting a window, so an attacker varying the key cannot grow the map without bound. A non-positive value is ignored.

type TokenBucketLimiter deprecated

type TokenBucketLimiter = FixedWindowLimiter

Deprecated: use FixedWindowLimiter. The limiter refills to full capacity at the window edge rather than proportionally to elapsed time, so it is a fixed-window counter and admits up to twice the rate across an instant straddling that edge; use SlidingWindowLimiter where the rate must hold over every trailing window.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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