Skip to content

KarthikSunkari/GoRateGuard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GoRateGuard

A lightweight, high-performance HTTP reverse proxy in Go with pluggable rate limiting strategies. Zero external dependencies beyond gopkg.in/yaml.v3.


Features

  • Reverse proxy powered by net/http/httputil.ReverseProxy
  • Three rate limiting strategies behind a common RateLimiter interface:
    • Token Bucket — configurable rate + burst, lazy refill
    • Sliding Window — sub-window based counter with automatic eviction
    • Fixed Window — simple counter with periodic reset
  • Per-endpoint + per-client limiting — key = client IP + request path
  • YAML configuration — define per-endpoint rules and defaults
  • HTTP 429 with Retry-After header and JSON error body
  • pprof endpoints at /debug/pprof/ for production profiling
  • Stdlib only — no external dependencies except YAML parsing

Architecture

                    ┌───────────────────────────────────────────────────┐
                    │                   GoRateGuard                     │
                    │                                                   │
  HTTP Request ──►  │  ┌─────────────┐    ┌──────────────┐    ┌──────┐ │
                    │  │  Middleware  │───►│ Rate Limiter │    │Proxy │ │
                    │  │  (IP + Path)│    │              │    │      │ │
                    │  └──────┬──────┘    │ ┌──────────┐ │    │  ──► │──► Backend
                    │         │           │ │  Token   │ │    │      │ │
                    │    429 ◄┤           │ │  Bucket  │ │    └──────┘ │
                    │  Retry- │           │ ├──────────┤ │             │
                    │  After  │           │ │ Sliding  │ │             │
                    │         │           │ │ Window   │ │             │
                    │         │           │ ├──────────┤ │             │
                    │         │           │ │  Fixed   │ │             │
                    │         │           │ │ Window   │ │             │
                    │         │           │ └──────────┘ │             │
                    │         │           └──────────────┘             │
                    └─────────┼───────────────────────────────────────┘
                              │
                    HTTP 429 + JSON body
cmd/gorateguard/main.go          → entry point, loads config, starts server
internal/
├── config/config.go             → YAML config parsing & validation
├── proxy/proxy.go               → reverse proxy handler
├── limiter/
│   ├── limiter.go               → RateLimiter interface
│   ├── token_bucket.go          → token bucket implementation
│   ├── sliding_window.go        → sliding window counter
│   ├── fixed_window.go          → fixed window counter
│   ├── limiter_test.go          → unit tests
│   └── limiter_bench_test.go    → benchmarks
└── middleware/middleware.go      → rate limit middleware
config.yaml                      → sample configuration

Quick Start

Build

go build -o gorateguard ./cmd/gorateguard

Run

# Start a backend server (e.g., a simple echo server on port 9090)
# Then start GoRateGuard:
./gorateguard -config config.yaml

GoRateGuard will listen on :8080 (default) and proxy requests to http://localhost:9090.

Test a rate-limited endpoint

# Send requests to a rate-limited endpoint
for i in $(seq 1 25); do
  curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/api/submit
done

When rate limited, you'll receive:

HTTP/1.1 429 Too Many Requests
Retry-After: 1
Content-Type: application/json

{"error": "rate limit exceeded", "retry_after": 0.8}

Configuration

server:
  port: 8080
  target: "http://localhost:9090"

default_strategy: "token_bucket"

endpoints:
  - path: "/api/search"
    strategy: "sliding_window"
    requests: 100
    window: "60s"
  - path: "/api/submit"
    strategy: "token_bucket"
    rate: 10
    burst: 20
  - path: "/api/status"
    strategy: "fixed_window"
    requests: 1000
    window: "60s"

default_limit:
  requests: 50
  window: "60s"
  rate: 5
  burst: 10
Field Description
server.port Port to listen on
server.target Backend URL to proxy to
default_strategy Fallback strategy for unmatched paths
endpoints[].path URL path prefix to match
endpoints[].strategy token_bucket, sliding_window, or fixed_window
endpoints[].requests Max requests per window (sliding/fixed)
endpoints[].window Window duration, e.g. 60s, 5m
endpoints[].rate Tokens per second (token bucket)
endpoints[].burst Max burst capacity (token bucket)

Rate Limiting Strategies

Token Bucket

Allows bursts up to the configured capacity, then throttles to a steady rate. Tokens refill lazily on each request — no background goroutines.

Best for: APIs where occasional bursts are acceptable.

Sliding Window

Divides the window into 10 sub-windows and evicts expired ones on each request. Provides smoother rate limiting than fixed windows.

Best for: APIs requiring consistent throughput distribution.

Fixed Window

Simple counter that resets at the start of each window. Lowest overhead, but allows bursts at window boundaries.

Best for: High-throughput endpoints where simplicity matters.


Profiling

pprof endpoints are available at /debug/pprof/:

# CPU profile (30 seconds)
go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30

# Heap profile
go tool pprof http://localhost:8080/debug/pprof/heap

# Goroutine dump
curl http://localhost:8080/debug/pprof/goroutine?debug=2

Tests

# Run all unit tests
go test -v ./...

# Run with race detector
go test -race ./...

Benchmarks

Run on Apple M2, Go 1.26, macOS (arm64):

goos: darwin
goarch: arm64
cpu: Apple M2
BenchmarkTokenBucket-8              14,650,060      72.35 ns/op     0 B/op    0 allocs/op
BenchmarkTokenBucket_Parallel-8      6,178,888     202.9  ns/op     8 B/op    1 allocs/op
BenchmarkSlidingWindow-8            18,244,938     100.5  ns/op     0 B/op    0 allocs/op
BenchmarkSlidingWindow_Parallel-8    4,444,251     230.6  ns/op     8 B/op    1 allocs/op
BenchmarkFixedWindow-8              24,105,178      50.44 ns/op     0 B/op    0 allocs/op
BenchmarkFixedWindow_Parallel-8      6,568,222     184.7  ns/op     8 B/op    1 allocs/op

All strategies sustain millions of ops/sec with zero allocations in serial mode.

# Reproduce
go test -bench=. -benchmem ./internal/limiter/

Go Module

module github.com/karthikSunkari/GoRateGuard
go 1.21

Dependencies: gopkg.in/yaml.v3 (YAML parsing only)


License

MIT

About

A lightweight, high-performance HTTP reverse proxy in Go

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages