core

package
v0.0.0-...-cf6c88e Latest Latest
Warning

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

Go to latest
Published: Mar 22, 2026 License: Apache-2.0 Imports: 50 Imported by: 0

Documentation

Index

Constants

View Source
const (
	MaxRecordPayload  = 16384
	MaxRecordOverhead = 256
	MaxRecordSize     = MaxRecordPayload + MaxRecordOverhead
	CacheLineSize     = 64

	H2PrefaceLen           = 24
	H2DefaultWindowSize    = 65535
	H2DefaultMaxFrameSize  = 16384
	H2MaxFrameSize         = 16777215
	H2HeaderTableSize      = 4096
	H2MaxConcurrentStream  = 256
	H2MaxHeaderListSize    = 8192
	H2ConnectionWindowSize = 4194304
	H2StreamWindowSize     = 4194304

	H2FrameData         byte = 0x0
	H2FrameHeaders      byte = 0x1
	H2FramePriority     byte = 0x2
	H2FrameRSTStream    byte = 0x3
	H2FrameSettings     byte = 0x4
	H2FramePushPromise  byte = 0x5
	H2FramePing         byte = 0x6
	H2FrameGoAway       byte = 0x7
	H2FrameWindowUpdate byte = 0x8
	H2FrameContinuation byte = 0x9

	H2FlagEndStream  byte = 0x1
	H2FlagEndHeaders byte = 0x4
	H2FlagPadded     byte = 0x8
	H2FlagPriority   byte = 0x20
	H2FlagAck        byte = 0x1

	H2ErrNoError         uint32 = 0x0
	H2ErrProtocol        uint32 = 0x1
	H2ErrInternal        uint32 = 0x2
	H2ErrFlowControl     uint32 = 0x3
	H2ErrSettingsTimeout uint32 = 0x4
	H2ErrStreamClosed    uint32 = 0x5
	H2ErrFrameSize       uint32 = 0x6
	H2ErrRefusedStream   uint32 = 0x7
	H2ErrCancel          uint32 = 0x8
	H2ErrCompression     uint32 = 0x9
	H2ErrConnect         uint32 = 0xa
	H2ErrEnhanceYourCalm uint32 = 0xb
	H2ErrInadequateSec   uint32 = 0xc
	H2ErrHTTP11Required  uint32 = 0xd

	H2SettingHeaderTableSize      uint16 = 0x1
	H2SettingEnablePush           uint16 = 0x2
	H2SettingMaxConcurrentStreams uint16 = 0x3
	H2SettingInitialWindowSize    uint16 = 0x4
	H2SettingMaxFrameSize         uint16 = 0x5
	H2SettingMaxHeaderListSize    uint16 = 0x6
)
View Source
const (
	StreamIdle       uint32 = 0
	StreamOpen       uint32 = 1
	StreamHalfClosed uint32 = 2
	StreamClosed     uint32 = 3
)

Variables

View Source
var (
	ErrRecordTooLarge         = &staticError{"record too large"}
	ErrNotClientHello         = &staticError{"not a ClientHello"}
	ErrTruncated              = &staticError{"message truncated"}
	ErrBodyTooShort           = &staticError{"body too short"}
	ErrNoSessionID            = &staticError{"no session_id"}
	ErrSessionIDTruncated     = &staticError{"session_id truncated"}
	ErrNoCipherSuites         = &staticError{"no cipher_suites"}
	ErrBadCSLen               = &staticError{"cipher_suites bad length"}
	ErrNoCompression          = &staticError{"no compression"}
	ErrNoExtensions           = &staticError{"no extensions"}
	ErrExtsTruncated          = &staticError{"extensions truncated"}
	ErrNoX25519               = &staticError{"no x25519 key_share"}
	ErrBadKeyShare            = &staticError{"bad x25519 key_share"}
	ErrAllZeroInner           = &staticError{"all-zero inner plaintext"}
	ErrSigVerifyFailed        = &staticError{"signature self-verification failed"}
	ErrH2BadPreface           = &staticError{"invalid h2 preface"}
	ErrH2FrameTooLarge        = &staticError{"h2 frame too large"}
	ErrH2StreamClosed         = &staticError{"h2 stream closed"}
	ErrH2FlowControl          = &staticError{"h2 flow control violation"}
	ErrH2BadHeader            = &staticError{"h2 bad header"}
	ErrH2GoAway               = &staticError{"h2 goaway received"}
	ErrConnectionClosed       = &staticError{"connection closed"}
	ErrBodyTooLarge           = &staticError{"body too large"}
	ErrRateLimited            = &staticError{"rate limited"}
	ErrStreamClosed           = &staticError{"stream closed"}
	ErrWindowExhausted        = &staticError{"flow control window exhausted"}
	ErrNoCertForSNI           = &staticError{"no certificate for server name"}
	ErrProxyBadResponse       = &staticError{"proxy: bad upstream response"}
	ErrProxyNoBackend         = &staticError{"proxy: no healthy backend"}
	ErrProxyTimeout           = &staticError{"proxy: upstream timeout"}
	ErrProxyConnFailed        = &staticError{"proxy: connection failed"}
	ErrIOUringRequired        = &staticError{"io_uring worker backend required"}
	ErrInternal               = &staticError{"internal error"}
	ErrRateLimit              = &staticError{"rate limit exceeded"}
	ErrTooManyHeaders         = &staticError{"too many headers"}
	ErrHpackTableSizeExceeded = &staticError{"hpack: dynamic table size exceeds protocol limit"}
	ErrWebSocketProtocol      = &staticError{"websocket protocol error"}
)
View Source
var BufReaderPool = sync.Pool{
	New: func() any { return bufio.NewReaderSize(nil, 65536) },
}
View Source
var ErrChanPool = sync.Pool{
	New: func() any { return make(chan error, 1) },
}
View Source
var FileReadBufPool = sync.Pool{
	New: func() any { b := make([]byte, 65536); return &b },
}
View Source
var H1StreamWriterPool = sync.Pool{
	New: func() any { return &H1StreamWriter{} },
}
View Source
var H2ClientPreface = [24]byte{'P', 'R', 'I', ' ', '*', ' ', 'H', 'T', 'T', 'P', '/', '2', '.', '0', '\r', '\n', '\r', '\n', 'S', 'M', '\r', '\n', '\r', '\n'}
View Source
var H2FrameBufPool = sync.Pool{
	New: func() any { b := make([]byte, 0, H2DefaultMaxFrameSize+9); return &b },
}
View Source
var H2FramePool = sync.Pool{
	New: func() any {
		return &H2Frame{
			Payload: make([]byte, 0, 1024),
		}
	},
}
View Source
var H2StreamWriterPool = sync.Pool{
	New: func() any { return &H2StreamWriter{} },
}
View Source
var HdrBufPool = sync.Pool{
	New: func() any { b := make([]byte, 5, 5); return &b },
}
View Source
var HpackStaticTable = [62][2]string{
	{},
	{":authority", ""},
	{":method", "GET"},
	{":method", "POST"},
	{":path", "/"},
	{":path", "/index.html"},
	{":scheme", "http"},
	{":scheme", "https"},
	{":status", "200"},
	{":status", "204"},
	{":status", "206"},
	{":status", "304"},
	{":status", "400"},
	{":status", "404"},
	{":status", "500"},
	{"accept-charset", ""},
	{"accept-encoding", "gzip, deflate"},
	{"accept-language", ""},
	{"accept-ranges", ""},
	{"accept", ""},
	{"access-control-allow-origin", ""},
	{"age", ""},
	{"allow", ""},
	{"authorization", ""},
	{"cache-control", ""},
	{"content-disposition", ""},
	{"content-encoding", ""},
	{"content-language", ""},
	{"content-length", ""},
	{"content-location", ""},
	{"content-range", ""},
	{"content-type", ""},
	{"cookie", ""},
	{"date", ""},
	{"etag", ""},
	{"expect", ""},
	{"expires", ""},
	{"from", ""},
	{"host", ""},
	{"if-match", ""},
	{"if-modified-since", ""},
	{"if-none-match", ""},
	{"if-range", ""},
	{"if-unmodified-since", ""},
	{"last-modified", ""},
	{"link", ""},
	{"location", ""},
	{"max-forwards", ""},
	{"proxy-authenticate", ""},
	{"proxy-authorization", ""},
	{"range", ""},
	{"referer", ""},
	{"refresh", ""},
	{"retry-after", ""},
	{"server", ""},
	{"set-cookie", ""},
	{"strict-transport-security", ""},
	{"transfer-encoding", ""},
	{"user-agent", ""},
	{"vary", ""},
	{"via", ""},
	{"www-authenticate", ""},
}
View Source
var LargeBufPool = sync.Pool{
	New: func() any { b := make([]byte, 0, 32768); return &b },
}
View Source
var MediumBufPool = sync.Pool{
	New: func() any { b := make([]byte, 0, 4096); return &b },
}
View Source
var PlainH1StreamWriterPool = sync.Pool{
	New: func() any { return &PlainH1StreamWriter{} },
}
View Source
var RecordBufPool = sync.Pool{
	New: func() any { b := make([]byte, 0, MaxRecordSize+5); return &b },
}
View Source
var RequestPool = sync.Pool{
	New: func() any {
		return &Request{
			Headers: make([][2]string, 0, 16),
			Body:    make([]byte, 0, 1024),
		}
	},
}
View Source
var ResponsePool = sync.Pool{
	New: func() any {
		return &Response{
			Headers: make([][2]string, 0, 8),
			body:    make([]byte, 0, 4096),
		}
	},
}
View Source
var SmallBufPool = sync.Pool{
	New: func() any { b := make([]byte, 0, 512); return &b },
}
View Source
var StreamPool = sync.Pool{
	New: func() any {
		return &H2Stream{
			Headers: make([][2]string, 0, 16),
			Body:    make([]byte, 0, 4096),
		}
	},
}
View Source
var SupportedSuites = [3]CipherSuiteConfig{
	{
		ID: 0x1301, KeyLen: 16, IVLen: 12,
		HashFn: sha256.New, HashLen: 32,
		MakeAEAD: func(key []byte) (cipher.AEAD, error) {
			b, err := aes.NewCipher(key)
			if err != nil {
				return nil, err
			}
			return cipher.NewGCM(b)
		},
	},
	{
		ID: 0x1302, KeyLen: 32, IVLen: 12,
		HashFn: sha512.New384, HashLen: 48,
		MakeAEAD: func(key []byte) (cipher.AEAD, error) {
			b, err := aes.NewCipher(key)
			if err != nil {
				return nil, err
			}
			return cipher.NewGCM(b)
		},
	},
	{
		ID: 0x1303, KeyLen: 32, IVLen: 12,
		HashFn: sha256.New, HashLen: 32,
		MakeAEAD: func(key []byte) (cipher.AEAD, error) {
			return chacha20poly1305.New(key)
		},
	},
}
View Source
var WriteBufPool = sync.Pool{
	New: func() any { b := make([]byte, 0, MaxRecordSize); return &b },
}

Functions

func AppendJSONString

func AppendJSONString(dst []byte, s string) []byte

func AppendRecord

func AppendRecord(dst []byte, ct byte, payload []byte) []byte

func BuildALPNExtension

func BuildALPNExtension(proto string) []byte

func BuildCertificate

func BuildCertificate(chain [][]byte) []byte

func BuildCertificateVerify

func BuildCertificateVerify(sig []byte) []byte

func BuildEncryptedExtensions

func BuildEncryptedExtensions(alpn string) []byte

func BuildFinished

func BuildFinished(verifyData []byte) []byte

func BuildH1Response

func BuildH1Response(resp *Response) ([]byte, *[]byte)

func BuildServerHello

func BuildServerHello(random, sessionID []byte, suiteID uint16, pubKey []byte) []byte

func CoarseNanotime

func CoarseNanotime() int64

func ComputeFinished

func ComputeFinished(h func() hash.Hash, hashLen int, baseSecret, transcriptHash []byte) []byte

func Dbg

func Dbg(format string, v ...interface{})

func DbgHex

func DbgHex(label string, data []byte)

func DialTCP4

func DialTCP4(addr string, timeout time.Duration) (net.Conn, error)

func EmptyTranscriptHash

func EmptyTranscriptHash(h func() hash.Hash) []byte

func EqualFoldASCII

func EqualFoldASCII(a, b string) bool

func Filename

func Filename(path string) string

Filename extracts the filename component from a file path, stripping any directory prefix.

core.Filename("/var/data/report.csv")  // "report.csv"
core.Filename("report.csv")            // "report.csv"

func GenerateCert

func GenerateCert() ([]byte, *ecdsa.PrivateKey, error)

func GenerateSelfSignedForDomain

func GenerateSelfSignedForDomain(domain string) ([]byte, *ecdsa.PrivateKey, error)

func H2WriteContinuation

func H2WriteContinuation(dst []byte, flags byte, streamID uint32, headerBlock []byte) []byte

func H2WriteFrame

func H2WriteFrame(dst []byte, typ, flags byte, streamID uint32, payload []byte) []byte

func H2WriteGoAway

func H2WriteGoAway(dst []byte, lastStream, errCode uint32) []byte

func H2WritePing

func H2WritePing(dst []byte, ack bool, data [8]byte) []byte

func H2WriteRSTStream

func H2WriteRSTStream(dst []byte, streamID, errCode uint32) []byte

func H2WriteSettings

func H2WriteSettings(dst []byte, settings [][2]uint32) []byte

func H2WriteSettingsAck

func H2WriteSettingsAck(dst []byte) []byte

func H2WriteWindowUpdate

func H2WriteWindowUpdate(dst []byte, streamID, increment uint32) []byte

func HSWrap

func HSWrap(msgType byte, body []byte) []byte

func HexDigit

func HexDigit(b byte) byte

func HpackDecodeInt

func HpackDecodeInt(data []byte, prefixBits uint8) (uint64, int)

func HpackDecodeString

func HpackDecodeString(data []byte) (string, int)

func IOUringStartupProbeReport

func IOUringStartupProbeReport() string

func LoadOrGenerateCert

func LoadOrGenerateCert(certFile, keyFile string) ([]byte, *ecdsa.PrivateKey, error)

func MonotonicNanotime

func MonotonicNanotime() int64

MonotonicNanotime returns the runtime's monotonic nanosecond clock. It is appropriate for timeout and elapsed-time bookkeeping in hot paths.

func NegotiateALPN

func NegotiateALPN(clientProtos []string, allowH2 bool) string

NegotiateALPN picks the best supported application protocol from the client's ALPN list. When allowH2 is false, "h2" is skipped and only HTTP/1.1 may be selected.

func ParseClientHello

func ParseClientHello(data []byte, result *ParsedClientHello) error

func ParseH1Request

func ParseH1Request(data []byte, req *Request) int

func ParseH1RequestHead

func ParseH1RequestHead(data []byte, req *Request) (headerEnd int, contentLength int, hasContentLength bool, closeConn bool, badTransferEncoding bool, ok bool)

func ReadRecord

func ReadRecord(conn net.Conn, hdrScratch []byte) (byte, []byte, *[]byte, error)

func ReadRecordSkipCCS

func ReadRecordSkipCCS(conn net.Conn, hdr []byte) (byte, []byte, *[]byte, error)

func ReleaseRecordBuf

func ReleaseRecordBuf(bp *[]byte)

func SendCCS

func SendCCS(conn net.Conn) error

func SendCloseNotify

func SendCloseNotify(conn net.Conn, writer *TrafficAEAD)

func SetDebugFlag

func SetDebugFlag(on bool)

func SignCertificateVerify

func SignCertificateVerify(priv *ecdsa.PrivateKey, transcriptHash []byte) ([]byte, error)

func StatusText

func StatusText(code int) string

func StringHash

func StringHash(s string) uint64

func StripInnerPlaintext

func StripInnerPlaintext(data []byte) (content []byte, contentType byte, err error)

func TLSDeriveSecret

func TLSDeriveSecret(h func() hash.Hash, hashLen int, secret []byte, label string, transcriptHash []byte) []byte

func TLSExpandLabel

func TLSExpandLabel(h func() hash.Hash, secret []byte, label string, ctx []byte, length int) []byte

func TLSExtract

func TLSExtract(h func() hash.Hash, salt, ikm []byte) []byte

func TLSExtractTo

func TLSExtractTo(h func() hash.Hash, salt, ikm, dst []byte) []byte

func ToLowerASCII

func ToLowerASCII(s string) string

func Uint32Hash

func Uint32Hash(v uint32) uint64

func Uint32Hash64

func Uint32Hash64(v uint64) uint64

func UnsafeBytes

func UnsafeBytes(s string) []byte

func UnsafeString

func UnsafeString(b []byte) string

func ValidateHost

func ValidateHost(host string) bool

func WriteAppData

func WriteAppData(conn net.Conn, writer *TrafficAEAD, data []byte) error

func WriteH1Response

func WriteH1Response(conn net.Conn, writer *TrafficAEAD, resp *Response) error

func WriteRecord

func WriteRecord(conn net.Conn, ct byte, payload []byte) error

Types

type ACMEConfig

type ACMEConfig struct {
	Email    string
	CacheDir string
	Domains  []string
	ACMENode string
}

ACMEConfig configures automatic TLS certificate provisioning through Let's Encrypt (or any ACME-compatible CA). Pass this in the server Config to enable automatic certificate management.

cfg := core.Config{
    ACME: &core.ACMEConfig{
        Email:    "[email protected]",
        CacheDir: "/var/certs",
        Domains:  []string{"example.com", "www.example.com"},
    },
}

type BackendConfig

type BackendConfig struct {
	Addr          string
	Weight        int
	TLS           bool
	TLSSkipVerify bool
}

BackendConfig defines a single upstream backend server that the reverse proxy will forward traffic to. Addr accepts bare host:port, or full URLs with http:// or https:// schemes. When an https:// URL is given, TLS is enabled automatically and defaults to port 443 if no port is specified. Weight controls the proportion of traffic this backend receives when using LBWeightedRR. If you need to connect to backends with self-signed certs (e.g. in development), set TLSSkipVerify to true.

// Plain HTTP backend on port 8080
core.BackendConfig{Addr: "10.0.0.10:8080", Weight: 1}

// HTTPS backend with auto-detected TLS
core.BackendConfig{Addr: "https://api-internal.example.com"}

// Dev backend with self-signed cert
core.BackendConfig{Addr: "https://localhost:9090", TLSSkipVerify: true}

type BandwidthConfig

type BandwidthConfig struct {
	MaxUploadRate   int64
	MaxDownloadRate int64
	BurstSize       int64
}

type BasicAuthConfig

type BasicAuthConfig struct {
	Users map[string]string
	Realm string
}

BasicAuthConfig holds username/password pairs and the HTTP Basic Auth realm string.

cfg := core.BasicAuthConfig{
	Users: map[string]string{"admin": "secret"},
	Realm: "Admin Panel",
}

type BufConn

type BufConn struct {
	net.Conn
	// contains filtered or unexported fields
}

func NewBufConn

func NewBufConn(c net.Conn) *BufConn

func (*BufConn) Read

func (bc *BufConn) Read(p []byte) (int, error)

func (*BufConn) Release

func (bc *BufConn) Release()

type CORSConfig

type CORSConfig struct {
	AllowOrigins     []string
	AllowMethods     []string
	AllowHeaders     []string
	ExposeHeaders    []string
	AllowCredentials bool
	MaxAge           int
}

CORSConfig controls Cross-Origin Resource Sharing headers.

cfg := core.CORSConfig{
	AllowOrigins:     []string{"https://example.com", "https://app.example.com"},
	AllowMethods:     []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
	AllowHeaders:     []string{"Content-Type", "Authorization"},
	ExposeHeaders:    []string{"X-Request-ID"},
	AllowCredentials: true,
	MaxAge:           86400,
}

Set AllowOrigins to []string{"*"} to allow all origins. When AllowCredentials is true and the origin is "*", the actual Origin header value is reflected back (per the CORS spec).

type CORSEngine

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

CORSEngine holds a snapshot of the CORS config and can be updated at runtime without restarting the server. Access to the snapshot is lock-free via atomic.Pointer.

func NewCORSEngine

func NewCORSEngine(cfg CORSConfig) *CORSEngine

NewCORSEngine creates a CORSEngine with the given config.

func (*CORSEngine) Config

func (ce *CORSEngine) Config() CORSConfig

func (*CORSEngine) Middleware

func (ce *CORSEngine) Middleware() MiddlewareFunc

func (*CORSEngine) Update

func (ce *CORSEngine) Update(cfg CORSConfig)

type CacheOption

type CacheOption func(*cacheOpts)

func WithCompressMinLen

func WithCompressMinLen(n int) CacheOption

WithCompressMinLen sets the minimum response body size in bytes before pre-compression kicks in. Responses smaller than this threshold are stored uncompressed even when WithPreCompress is true, since gzip overhead can make small payloads larger. Defaults to the value from ProxyCacheConfig.CompressMinLen (512 bytes with DefaultProxyCacheConfig).

pr.CacheThis(5*time.Minute, 0, core.WithCompressMinLen(1024))

func WithPreCompress

func WithPreCompress(on bool) CacheOption

WithPreCompress controls whether a pre-compressed gzip copy of the response body is stored in the cache alongside the original. When enabled, clients that send Accept-Encoding: gzip receive the pre-compressed copy without any runtime compression overhead. Defaults to true.

pr.CacheThis(5*time.Minute, 0, core.WithPreCompress(false))

type CacheRule

type CacheRule struct {
	PathPrefix string
	MaxAge     time.Duration
	Methods    []string
	MaxBytes   int64
	StatusOnly []int
}

CacheRule configures caching for a specific path prefix. If PathPrefix is empty it acts as a default rule. Methods defaults to ["GET"] if empty.

core.CacheRule{
    PathPrefix: "/api/",
    MaxAge:     10 * time.Minute,
    Methods:    []string{"GET"},
    MaxBytes:   1 << 20,         // 1 MB max per entry
    StatusOnly: []int{200, 301}, // only cache these status codes
}

type CertConfig

type CertConfig struct {
	Domain   string
	CertFile string
	KeyFile  string
	Source   CertSource
}

CertConfig specifies a TLS certificate to load for a domain. Used with manual certificate management instead of ACME.

cfg := core.Config{
    Certs: []core.CertConfig{
        {
            Domain:   "example.com",
            CertFile: "/etc/ssl/example.com.pem",
            KeyFile:  "/etc/ssl/example.com-key.pem",
            Source:   core.CertManual,
        },
    },
}

type CertEntry

type CertEntry struct {
	Domain   string
	ChainDER [][]byte
	PrivKey  *ecdsa.PrivateKey
	TLSCert  tls.Certificate
	Source   CertSource
	// contains filtered or unexported fields
}

func NewCertEntryFromDER

func NewCertEntryFromDER(domain string, certDER []byte, privKey *ecdsa.PrivateKey, source CertSource) *CertEntry

func NewCertEntryFromPEM

func NewCertEntryFromPEM(domain string, certPEM, keyPEM []byte, source CertSource) (*CertEntry, error)

func (*CertEntry) CachedEE

func (entry *CertEntry) CachedEE(alpn string) []byte

func (*CertEntry) CachedEECert

func (entry *CertEntry) CachedEECert(alpn string) []byte

type CertInfo

type CertInfo struct {
	Domain string
	Source CertSource
}

type CertSource

type CertSource uint8
const (
	CertSelfSigned CertSource = iota
	CertManual
	CertACME
)

type CertStore

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

func NewCertStore

func NewCertStore() *CertStore

func (*CertStore) AddCert

func (cs *CertStore) AddCert(entry *CertEntry)

func (*CertStore) ListCerts

func (cs *CertStore) ListCerts() []CertInfo

func (*CertStore) Lookup

func (cs *CertStore) Lookup(serverName string) *CertEntry

func (*CertStore) RemoveCert

func (cs *CertStore) RemoveCert(domain string)

func (*CertStore) SetDefault

func (cs *CertStore) SetDefault(domain string)

type CipherSuiteConfig

type CipherSuiteConfig struct {
	ID       uint16
	KeyLen   int
	IVLen    int
	HashFn   func() hash.Hash
	HashLen  int
	MakeAEAD func(key []byte) (cipher.AEAD, error)
	// contains filtered or unexported fields
}

func NegotiateSuite

func NegotiateSuite(clientIDs []uint16) *CipherSuiteConfig

func (*CipherSuiteConfig) ComputeFinishedTo

func (cs *CipherSuiteConfig) ComputeFinishedTo(baseSecret, transcriptHash, dst []byte) []byte

func (*CipherSuiteConfig) DeriveSecretTo

func (cs *CipherSuiteConfig) DeriveSecretTo(tmpl *hkdfLabelTemplate, secret, transcriptHash, dst []byte) []byte

type CompressConfig

type CompressConfig struct {
	Level   int
	MinSize int
}

CompressConfig controls response compression.

cfg := core.CompressConfig{
	Level:   6,     // gzip/deflate level (1-9)
	MinSize: 512,   // skip compression below this body size
}

type Config

type Config struct {
	Addr             string
	HTTPAddr         string
	ReadTimeout      time.Duration
	WriteTimeout     time.Duration
	IdleTimeout      time.Duration
	HandshakeTimeout time.Duration

	MaxBodySize       int64
	MaxReadSize       int64
	MaxWriteSize      int64
	MaxHeaderSize     int
	MaxRequestsPerIP  int64
	MaxConcurrentReqs int64

	TLSCertFile   string
	TLSKeyFile    string
	Certs         []CertConfig
	DefaultDomain string
	ACME          *ACMEConfig
	WorkerCount   int

	ConnBandwidth   BandwidthConfig
	GlobalBandwidth BandwidthConfig
	Listeners       int

	ServerName      string
	TrustedProxies  []string
	EnableCompress  bool
	CompressLevel   int
	CompressMinSize int

	Debug           bool
	LogRequests     bool
	ShutdownTimeout time.Duration
	PlainHTTP       bool
	DisableHTTP2    bool
}

Config controls every aspect of the ALOS server. Pass it to New to create a Server. Zero values use sane defaults (see DefaultConfig).

s := core.New(core.Config{
	Addr:        ":443",
	IdleTimeout: 120 * time.Second,
	Listeners:   4,
	PlainHTTP:   false,
	ServerName:  "ALOS",
	ACME: &core.ACMEConfig{
		Email:   "[email protected]",
		Domains: []string{"example.com"},
	},
})

Fields:

  • Addr: listen address (":443", ":8443", "0.0.0.0:443", etc.)
  • HTTPAddr: listen address for the HTTP-to-HTTPS redirect listener. Defaults to ":80" when Addr uses port 443, otherwise disabled.
  • PlainHTTP: when true the server speaks plain HTTP/1.1 on Addr, skipping TLS entirely.
  • DisableHTTP2: when true the server never negotiates HTTP/2 and only serves HTTP/1.1 over TLS.
  • IdleTimeout: how long an idle keep-alive connection stays open.
  • HandshakeTimeout: deadline for the TLS handshake.
  • MaxBodySize: reject request bodies larger than this (0 = unlimited).
  • MaxReadSize / MaxWriteSize: per-connection I/O caps.
  • MaxHeaderSize: maximum header block in bytes.
  • MaxConcurrentReqs: server-wide concurrency cap (0 = unlimited).
  • Listeners: number of SO_REUSEPORT listeners (Linux only, 1 elsewhere).
  • Certs: per-domain certificate configs (manual, self-signed, or ACME).
  • ACME: automatic Let's Encrypt certificates.
  • ConnBandwidth / GlobalBandwidth: per-connection and global rate limits.
  • Debug: enable verbose internal logging.
  • LogRequests: log every accepted and closed connection.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns a Config with sensible defaults. It listens on :8443 with a 120s idle timeout, 30s handshake timeout, 8 KiB header limit, gzip level 6, and request logging enabled.

type ConnectionLimiter

type ConnectionLimiter struct {
	Upload   *TokenBucket
	Download *TokenBucket
}

func NewConnectionLimiter

func NewConnectionLimiter(config BandwidthConfig) *ConnectionLimiter

func (*ConnectionLimiter) ThrottleDownload

func (cl *ConnectionLimiter) ThrottleDownload(n int64)

func (*ConnectionLimiter) ThrottleUpload

func (cl *ConnectionLimiter) ThrottleUpload(n int64)

type DomainConfig

type DomainConfig struct {
	Domain         string
	Backends       []BackendConfig
	LoadBalancer   LoadBalancerType
	ConnectTimeout time.Duration
	ReadTimeout    time.Duration
	WriteTimeout   time.Duration
	MaxRetries     int
	MaxIdleConns   int
	IdleTimeout    time.Duration
	HealthCheck    HealthCheckConfig
	HostHeader     string
	PreserveHost   bool
}

DomainConfig holds the full configuration for a proxied domain, including its backends, load balancer strategy, timeouts, connection pooling, and health checks. Pass this to Server.AddProxyDomain to register a domain.

Timeouts and pool sizes default to sensible values when left at zero:

  • ConnectTimeout: 10s

  • ReadTimeout: 30s

  • WriteTimeout: 10s

  • MaxRetries: 2

  • MaxIdleConns: 32

  • IdleTimeout: 90s

  • Backend weights: 1

    srv.AddProxyDomain(core.DomainConfig{ Domain: "api.example.com", Backends: []core.BackendConfig{ {Addr: "10.0.0.10:8080", Weight: 3}, {Addr: "10.0.0.11:8080", Weight: 1}, }, LoadBalancer: core.LBWeightedRR, ConnectTimeout: 5 * time.Second, ReadTimeout: 15 * time.Second, MaxRetries: 3, MaxIdleConns: 64, HealthCheck: core.HealthCheckConfig{ Enabled: true, Path: "/healthz", Interval: 5 * time.Second, FailThreshold: 3, }, })

type FineTuneOptions

type FineTuneOptions struct {
	WorkerCandidates   []int
	ListenerCandidates []int
	Scenarios          []FineTuneScenario
	AutoInstallWrk     bool
	StartupTimeout     time.Duration
	RequestTimeout     time.Duration
	BindHost           string
}

func DefaultFineTuneOptions

func DefaultFineTuneOptions() FineTuneOptions

type FineTuneReport

type FineTuneReport struct {
	WrkPath            string
	AutoInstalledWrk   bool
	WorkerCandidates   []int
	ListenerCandidates []int
	BestWorkerCount    int
	BestListenerCount  int
	BestAcceptShards   int
	BestByScenario     []FineTuneResult
	Results            []FineTuneResult
}

func (*FineTuneReport) String

func (report *FineTuneReport) String() string

type FineTuneResult

type FineTuneResult struct {
	Scenario       FineTuneScenario
	Workers        int
	Listeners      int
	AcceptShards   int
	RequestsPerSec float64
	Latency        string
	TransferPerSec string
	RawOutput      string
}

type FineTuneScenario

type FineTuneScenario struct {
	Name        string
	Connections int
	Threads     int
	Duration    time.Duration
}

type GlobalLimiter

type GlobalLimiter struct {
	Upload   *TokenBucket
	Download *TokenBucket
}

func NewGlobalLimiter

func NewGlobalLimiter(config BandwidthConfig) *GlobalLimiter

type H1StreamWriter

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

func (*H1StreamWriter) Close

func (w *H1StreamWriter) Close() error

func (*H1StreamWriter) Flush

func (w *H1StreamWriter) Flush() error

func (*H1StreamWriter) WriteChunk

func (w *H1StreamWriter) WriteChunk(data []byte) error

func (*H1StreamWriter) WriteHeader

func (w *H1StreamWriter) WriteHeader(statusCode int, headers [][2]string, contentType string) error

type H2Conn

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

type H2Frame

type H2Frame struct {
	Length   uint32
	Type     byte
	Flags    byte
	StreamID uint32
	Payload  []byte
}

func H2ReadFrame

func H2ReadFrame(reader func() ([]byte, error)) (*H2Frame, error)

type H2Stream

type H2Stream struct {
	ID      uint32
	State   atomic.Uint32
	Window  atomic.Int64
	Method  string
	Path    string
	Scheme  string
	Auth    string
	Headers [][2]string
	Body    []byte
	// contains filtered or unexported fields
}

func (*H2Stream) Reset

func (s *H2Stream) Reset()

type H2StreamWriter

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

func (*H2StreamWriter) Close

func (w *H2StreamWriter) Close() error

func (*H2StreamWriter) Flush

func (w *H2StreamWriter) Flush() error

func (*H2StreamWriter) WriteChunk

func (w *H2StreamWriter) WriteChunk(data []byte) error

func (*H2StreamWriter) WriteHeader

func (w *H2StreamWriter) WriteHeader(statusCode int, headers [][2]string, contentType string) error

type HTTPRoute

type HTTPRoute struct {
	PathPrefix string
	Backend    string
	HostHeader string
}

HTTPRoute defines an HTTP (non-TLS) routing rule for the server. When the server receives a plain HTTP request, it matches the path against PathPrefix and forwards to Backend. This is used with Server.SetHTTPRoutes.

srv.SetHTTPRoutes([]core.HTTPRoute{
    {PathPrefix: "/.well-known/acme-challenge/", Backend: "127.0.0.1:8080"},
    {PathPrefix: "/", Backend: "https://example.com"},
})

type HTTPRouter

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

func (*HTTPRouter) AddRoute

func (hr *HTTPRouter) AddRoute(route HTTPRoute)

func (*HTTPRouter) RemoveRoute

func (hr *HTTPRouter) RemoveRoute(pathPrefix string)

func (*HTTPRouter) SetRoutes

func (hr *HTTPRouter) SetRoutes(routes []HTTPRoute)

type HandlerFunc

type HandlerFunc func(req *Request, resp *Response)

func Chain

func Chain(handler HandlerFunc, middleware ...MiddlewareFunc) HandlerFunc

Chain wraps a handler with the given middleware, applying them in order so the first middleware in the list is the outermost wrapper.

protected := core.Chain(myHandler, authMiddleware, logMiddleware)
r.GET("/secret", protected)

type HealthCheckConfig

type HealthCheckConfig struct {
	Enabled          bool
	Path             string
	Interval         time.Duration
	Timeout          time.Duration
	FailThreshold    int
	SuccessThreshold int
}

HealthCheckConfig configures active health checking for backends in a domain. When Enabled is true, the proxy periodically sends HTTP GET requests to Path on each backend and marks them healthy or unhealthy based on the response. Backends that fail FailThreshold consecutive checks are taken out of rotation. They are re-added after SuccessThreshold consecutive passing checks.

All fields have sensible defaults when left at zero values:

  • Interval defaults to 10s

  • Timeout defaults to 5s

  • FailThreshold defaults to 3

  • SuccessThreshold defaults to 2

    core.HealthCheckConfig{ Enabled: true, Path: "/healthz", Interval: 5 * time.Second, Timeout: 2 * time.Second, FailThreshold: 3, SuccessThreshold: 2, }

type HpackDecoder

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

func NewHpackDecoder

func NewHpackDecoder() *HpackDecoder

func (*HpackDecoder) Decode

func (d *HpackDecoder) Decode(data []byte) ([][2]string, error)

func (*HpackDecoder) DecodeInto

func (d *HpackDecoder) DecodeInto(headers [][2]string, data []byte) ([][2]string, error)

func (*HpackDecoder) DecodeIntoRequest

func (d *HpackDecoder) DecodeIntoRequest(headers [][2]string, data []byte) ([][2]string, hpackRequestMeta, error)

func (*HpackDecoder) DecodeRequestMeta

func (d *HpackDecoder) DecodeRequestMeta(data []byte) (hpackRequestMeta, error)

type HpackEncoder

type HpackEncoder struct {
	Buf []byte
}

func (*HpackEncoder) EncodeHeader

func (e *HpackEncoder) EncodeHeader(name, value string)

func (*HpackEncoder) EncodeIndexed

func (e *HpackEncoder) EncodeIndexed(idx uint64)

func (*HpackEncoder) EncodeInt

func (e *HpackEncoder) EncodeInt(prefix byte, prefixBits uint8, val uint64)

func (*HpackEncoder) EncodeStatus

func (e *HpackEncoder) EncodeStatus(code int)

func (*HpackEncoder) EncodeString

func (e *HpackEncoder) EncodeString(s string)

func (*HpackEncoder) Reset

func (e *HpackEncoder) Reset(dst []byte)

type JSON

type JSON struct {
	Buf []byte
}

func AcquireJSON

func AcquireJSON() *JSON

func (*JSON) Marshal

func (j *JSON) Marshal(v any) []byte

func (*JSON) Release

func (j *JSON) Release()

type LoadBalancerType

type LoadBalancerType uint8
const (
	LBRoundRobin LoadBalancerType = iota
	LBWeightedRR
	LBLeastConn
	LBIPHash
	LBRandom
)

type MiddlewareFunc

type MiddlewareFunc func(HandlerFunc) HandlerFunc

func AllowMethods

func AllowMethods(methods ...string) MiddlewareFunc

AllowMethods returns middleware that rejects requests whose method is not in the given list with 405 Method Not Allowed.

s.Router.Use(core.AllowMethods("GET", "POST"))

func BasicAuth

func BasicAuth(cfg BasicAuthConfig) MiddlewareFunc

BasicAuth returns middleware that requires HTTP Basic credentials. Passwords are compared in constant time. If the realm is empty it defaults to "Restricted".

s.Router.Use(core.BasicAuth(core.BasicAuthConfig{
	Users: map[string]string{"admin": "secret"},
	Realm: "Admin Panel",
}))

func BodyLimit

func BodyLimit(maxBytes int64) MiddlewareFunc

BodyLimit returns middleware that rejects bodies larger than maxBytes with 413 Payload Too Large. It checks Content-Length first, then the actual body length.

s.Router.Use(core.BodyLimit(10 << 20)) // 10 MiB

func CORS

func CORS(config CORSConfig) MiddlewareFunc

CORS returns a one-shot middleware from a static CORSConfig. For runtime-updatable CORS use NewCORSEngine + CORSEngine.Middleware.

s.Router.Use(core.CORS(core.CORSConfig{
	AllowOrigins: []string{"*"},
	AllowMethods: []string{"GET", "POST"},
}))

func Compress

func Compress(cfg CompressConfig) MiddlewareFunc

Compress returns middleware that gzip- or deflate-compresses response bodies based on the client's Accept-Encoding header. Responses below MinSize bytes or already streamed are left untouched.

s.Router.Use(core.Compress(core.CompressConfig{Level: 6, MinSize: 512}))
func Header(name, value string) MiddlewareFunc

Header returns middleware that sets a fixed response header on every request.

s.Router.Use(core.Header("X-Powered-By", "ALOS"))

func If

func If(predicate func(*Request) bool, inner MiddlewareFunc) MiddlewareFunc

If returns middleware that conditionally applies inner only when predicate returns true for the current request.

s.Router.Use(core.If(
	func(req *core.Request) bool { return req.Path != "/health" },
	core.Logger(),
))

func Logger

func Logger() MiddlewareFunc

Logger returns middleware that logs every request with method, path, status code, and elapsed time to the standard logger.

s.Router.Use(core.Logger())

func RealIP

func RealIP(trustedProxies ...string) MiddlewareFunc

RealIP returns middleware that overwrites req.RemoteAddr with the value from X-Forwarded-For or X-Real-IP headers. When trustedProxies is non-empty, the header is only trusted if the direct client IP matches one of the listed CIDRs or IPs. When trustedProxies is empty, the header is always trusted — only use this behind a known proxy.

s.Router.Use(core.RealIP("10.0.0.0/8", "172.16.0.0/12"))

func Recovery

func Recovery() MiddlewareFunc

Recovery returns middleware that catches panics in downstream handlers and responds with 500 Internal Server Error instead of crashing the process. Always register it first in the middleware chain.

s.Router.Use(core.Recovery())

func RequestID

func RequestID() MiddlewareFunc

RequestID returns middleware that assigns a monotonically increasing integer ID to each request via the X-Request-ID response header.

s.Router.Use(core.RequestID())

func SecurityHeaders

func SecurityHeaders(cfg SecurityHeadersConfig) MiddlewareFunc

SecurityHeaders returns middleware that injects the configured security headers into every response.

s.Router.Use(core.SecurityHeaders(core.DefaultSecurityHeaders()))

func Timeout

func Timeout(d time.Duration) MiddlewareFunc

Timeout returns middleware that aborts the handler if it takes longer than d. The client receives a 504 Gateway Timeout.

s.Router.Use(core.Timeout(10 * time.Second))

type Param

type Param struct {
	Key   string
	Value string
}

Param stores one route parameter extracted during routing.

r.GET("/users/:id", func(req *core.Request, resp *core.Response) {
    id := req.Params[0].Value
    resp.String(id)
})

r.GET("/teams/:team/users/:user", func(req *core.Request, resp *core.Response) {
    team := req.Params[0].Value
    user := req.Params[1].Value
    resp.String(team + ":" + user)
})

type ParsedClientHello

type ParsedClientHello struct {
	SessionID         []byte
	CipherSuites      []uint16
	X25519PubKey      []byte
	ALPNProtos        []string
	ServerName        string
	SupportedVersions []uint16
	// contains filtered or unexported fields
}

func (*ParsedClientHello) Reset

func (ch *ParsedClientHello) Reset()

func (*ParsedClientHello) SupportsTLS13

func (ch *ParsedClientHello) SupportsTLS13() bool

type PlainH1StreamWriter

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

func (*PlainH1StreamWriter) Close

func (w *PlainH1StreamWriter) Close() error

func (*PlainH1StreamWriter) Flush

func (w *PlainH1StreamWriter) Flush() error

func (*PlainH1StreamWriter) WriteChunk

func (w *PlainH1StreamWriter) WriteChunk(data []byte) error

func (*PlainH1StreamWriter) WriteHeader

func (w *PlainH1StreamWriter) WriteHeader(statusCode int, headers [][2]string, contentType string) error

type ProxyCache

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

func NewProxyCache

func NewProxyCache(cfg ProxyCacheConfig) *ProxyCache

func (*ProxyCache) Get

func (pc *ProxyCache) Get(method, host, path, acceptEncoding string) (*cacheEntry, bool)

func (*ProxyCache) Purge

func (pc *ProxyCache) Purge(method, host, path string) bool

func (*ProxyCache) PurgeAll

func (pc *ProxyCache) PurgeAll()

func (*ProxyCache) PurgeDomain

func (pc *ProxyCache) PurgeDomain(domain string) int64

func (*ProxyCache) Put

func (pc *ProxyCache) Put(method, host, path string, statusCode int, headers [][2]string, contentType string, body []byte)

func (*ProxyCache) PutManual

func (pc *ProxyCache) PutManual(method, host, path string, statusCode int, headers [][2]string, contentType string, body []byte, ttl time.Duration, maxHits uint64, preCompress bool, compressMinLen int)

func (*ProxyCache) ServeCached

func (pc *ProxyCache) ServeCached(entry *cacheEntry, req *Request, resp *Response)

func (*ProxyCache) Stats

func (pc *ProxyCache) Stats() (entries int64, totalBytes int64, hits uint64, misses uint64)

func (*ProxyCache) Stop

func (pc *ProxyCache) Stop()

func (*ProxyCache) UpdateConfig

func (pc *ProxyCache) UpdateConfig(cfg ProxyCacheConfig)

type ProxyCacheConfig

type ProxyCacheConfig struct {
	Rules          []CacheRule
	MaxEntrySize   int64
	DefaultMaxAge  time.Duration
	MaxTotalBytes  int64
	MaxEntries     int64
	StaleWhileRev  time.Duration
	PreCompress    bool
	CompressLevel  int
	CompressMinLen int
}

ProxyCacheConfig controls how the reverse proxy caches backend responses.

cfg := core.DefaultProxyCacheConfig()
cfg.MaxTotalBytes = 512 << 20           // 512 MB total cache
cfg.MaxEntrySize = 8 << 20              // 8 MB max per entry
cfg.DefaultMaxAge = 10 * time.Minute
cfg.PreCompress = true                   // pre-gzip cached entries
cfg.Rules = []core.CacheRule{
    {PathPrefix: "/static/", MaxAge: time.Hour},
    {PathPrefix: "/api/", MaxAge: 30 * time.Second, StatusOnly: []int{200}},
}
srv.SetProxyCache(cfg)

func DefaultProxyCacheConfig

func DefaultProxyCacheConfig() ProxyCacheConfig

DefaultProxyCacheConfig returns a ProxyCacheConfig with sensible defaults: 4 MB max entry, 5 minute TTL, 256 MB total, 10k entries, gzip compression at level 6 for responses >= 512 bytes.

type ProxyEngine

type ProxyEngine struct {
	OnError    ProxyErrorFunc
	OnRequest  ProxyInterceptFunc
	OnResponse ProxyResponseFunc
	Cache      *ProxyCache
	// contains filtered or unexported fields
}

ProxyEngine is the core reverse-proxy component that routes incoming TLS connections to upstream backends based on the SNI domain. It manages connection pools, load balancing, health checking, and optional response caching across all registered domains.

Hook into the proxy lifecycle with:

  • OnError: called when a backend request fails (connection error, timeout)
  • OnRequest: called before forwarding; modify headers/path or reject
  • OnResponse: called after the backend replies; modify headers/status or cache

ProxyEngine is created internally by the Server. Use Server.OnProxyError, Server.OnProxyRequest, and Server.OnProxyResponse to register callbacks.

func NewProxyEngine

func NewProxyEngine() *ProxyEngine

NewProxyEngine creates a ProxyEngine with an empty domain table. This is called internally by Server.New — you typically don't need to call it directly.

func (*ProxyEngine) AddDomain

func (pe *ProxyEngine) AddDomain(cfg DomainConfig)

AddDomain registers or replaces a domain configuration. If a domain with the same name already exists, its health checkers and connection pools are shut down before the new config takes effect. Safe to call at runtime while the server is handling traffic — the swap is atomic.

pe.AddDomain(core.DomainConfig{
    Domain:   "api.example.com",
    Backends: []core.BackendConfig{{Addr: "10.0.0.10:8080"}},
})

func (*ProxyEngine) Handle

func (pe *ProxyEngine) Handle(req *Request, resp *Response) bool

func (*ProxyEngine) ListDomains

func (pe *ProxyEngine) ListDomains() []string

ListDomains returns the names of all currently registered proxy domains. The order is non-deterministic.

func (*ProxyEngine) Lookup

func (pe *ProxyEngine) Lookup(host string) *domainState

func (*ProxyEngine) RemoveDomain

func (pe *ProxyEngine) RemoveDomain(domain string)

RemoveDomain removes a domain by name and gracefully shuts down its health checkers and connection pools. No-op if the domain doesn't exist. Safe to call at runtime.

func (*ProxyEngine) Stop

func (pe *ProxyEngine) Stop()

Stop shuts down all health checkers and closes all idle backend connections across every registered domain. Called automatically by Server.Shutdown.

type ProxyError

type ProxyError struct {
	Domain     string
	Backend    string
	ClientAddr string
	Method     string
	Path       string
	Attempt    int
	Err        error
}

ProxyError holds context about a failed backend request. It is passed to the OnError callback registered with Server.OnProxyError. Domain and Backend identify which upstream was tried, Attempt indicates which retry this was (starting at 1), and Err contains the underlying network or timeout error.

srv.OnProxyError(func(pe core.ProxyError) {
    log.Printf("proxy error domain=%s backend=%s attempt=%d err=%v",
        pe.Domain, pe.Backend, pe.Attempt, pe.Err)
})

type ProxyErrorFunc

type ProxyErrorFunc func(ProxyError)

type ProxyInterceptFunc

type ProxyInterceptFunc func(pr *ProxyRequest) bool

type ProxyRequest

type ProxyRequest struct {
	Domain     string
	Backend    string
	ClientAddr string
	Method     string
	Path       string
	Headers    [][2]string
	Host       string
}

ProxyRequest is passed to the OnRequest callback before the request is forwarded to the backend. You can inspect or modify Headers, Host, and Path to rewrite the outgoing request. Returning false from the callback rejects the request with a 502 Bad Gateway.

Common use cases:

  • Inject authentication headers for internal backends

  • Rewrite paths (e.g. strip a /api prefix)

  • Block requests based on client IP or headers

    srv.OnProxyRequest(func(pr *core.ProxyRequest) bool { // Add internal auth header pr.Headers = append(pr.Headers, [2]string{"X-Internal-Auth", "secret"})

    // Strip /api prefix if len(pr.Path) > 4 && pr.Path[:4] == "/api" { pr.Path = pr.Path[4:] } return true })

type ProxyResponse

type ProxyResponse struct {
	Domain     string
	Backend    string
	ClientAddr string
	Method     string
	Path       string
	StatusCode int
	Headers    [][2]string
	// contains filtered or unexported fields
}

ProxyResponse is passed to the OnResponse callback after the backend replies but before the response is sent to the client. You can modify Headers and StatusCode to transform what the client receives. Use CacheThis to opt this response into the proxy cache with a specific TTL and hit limit, or DontCache to explicitly skip caching even if a cache rule would match.

srv.OnProxyResponse(func(pr *core.ProxyResponse) {
    // Inject a custom header
    pr.Headers = append(pr.Headers, [2]string{"X-Proxy", "alos"})

    // Cache successful API responses for 5 minutes
    if pr.StatusCode == 200 && len(pr.Path) > 4 && pr.Path[:4] == "/api" {
        pr.CacheThis(5*time.Minute, 0)
    }

    // Never cache error responses
    if pr.StatusCode >= 400 {
        pr.DontCache()
    }
})

func (*ProxyResponse) CacheThis

func (pr *ProxyResponse) CacheThis(ttl time.Duration, maxHits uint64, opts ...CacheOption)

CacheThis marks this response for caching with the given TTL. The entry is evicted after ttl elapses or after maxHits cache hits, whichever comes first. Pass 0 for maxHits to keep the entry alive until TTL expiration only.

Optional CacheOption values control gzip pre-compression of the cached body. By default, a gzip copy is stored for responses >= 512 bytes.

// Cache for 10 minutes, unlimited hits
pr.CacheThis(10*time.Minute, 0)

// Cache for 1 hour, evict after 1000 hits
pr.CacheThis(time.Hour, 1000)

// Cache without pre-compression
pr.CacheThis(5*time.Minute, 0, core.WithPreCompress(false))

func (*ProxyResponse) DontCache

func (pr *ProxyResponse) DontCache()

DontCache prevents this specific response from being cached, overriding any cache rules that would otherwise match. Call this in OnResponse for responses that should never be stored (e.g. user-specific data, error pages).

if pr.StatusCode >= 400 {
    pr.DontCache()
}

type ProxyResponseFunc

type ProxyResponseFunc func(pr *ProxyResponse)

type RateLimitEngine

type RateLimitEngine struct {
	OnLimit RateLimitFunc
	// contains filtered or unexported fields
}

RateLimitEngine tracks per-client request counts against configured rules. It uses lock-free atomics for counters and a sharded map for client state. The engine runs a background goroutine that cleans up stale entries every 30 seconds.

func NewRateLimitEngine

func NewRateLimitEngine() *RateLimitEngine

NewRateLimitEngine creates a new rate limiter with an empty rule set and starts the background cleanup goroutine. Call Stop when the engine is no longer needed.

func (*RateLimitEngine) AddRule

func (rle *RateLimitEngine) AddRule(rule RateLimitRule)

func (*RateLimitEngine) Check

func (rle *RateLimitEngine) Check(ip string, path string) (bool, *compiledRule, time.Duration)

func (*RateLimitEngine) RemoveRule

func (rle *RateLimitEngine) RemoveRule(path string)

func (*RateLimitEngine) SetRules

func (rle *RateLimitEngine) SetRules(rules []RateLimitRule)

func (*RateLimitEngine) Stop

func (rle *RateLimitEngine) Stop()

type RateLimitEvent

type RateLimitEvent struct {
	IP         string
	Path       string
	Rule       RateLimitRule
	RetryAfter time.Duration
}

RateLimitEvent is passed to the OnLimit callback when a request is rate-limited. It contains the client IP, request path, the matched rule, and how long the client should wait before retrying.

type RateLimitFunc

type RateLimitFunc func(event RateLimitEvent, req *Request, resp *Response) bool

type RateLimitRule

type RateLimitRule struct {
	Path     string
	MaxReqs  int64
	Window   time.Duration
	BlockFor time.Duration
	OnLimit  RateLimitFunc
}

RateLimitRule defines a rate limit for a specific path pattern. Path can be an exact path, a prefix ending in * (wildcard), or a regex prefixed with ~.

// Exact path: 100 requests per minute, block for 5 minutes on exceed
core.RateLimitRule{
    Path:     "/api/login",
    MaxReqs:  100,
    Window:   time.Minute,
    BlockFor: 5 * time.Minute,
}

// Prefix wildcard: anything under /api/
core.RateLimitRule{
    Path:    "/api/*",
    MaxReqs: 1000,
    Window:  time.Minute,
}

// Regex: match /users/{digits}
core.RateLimitRule{
    Path:    "~^/users/[0-9]+$",
    MaxReqs: 50,
    Window:  time.Minute,
}

type Request

type Request struct {
	Method       string
	Path         string
	Proto        string
	Host         string
	RemoteAddr   string
	Headers      [][2]string
	Body         []byte
	StreamID     uint32
	IsH2         bool
	Params       [8]Param
	ParamCount   int
	StreamWriter StreamWriter
	// contains filtered or unexported fields
}

Request is the handler-facing view of an incoming request.

srv.Router.GET("/users/:id", func(req *core.Request, resp *core.Response) {
    resp.String(req.Method + " " + req.Path + " for " + req.ParamValue("id"))
})

srv.Router.POST("/echo", func(req *core.Request, resp *core.Response) {
    ct := req.Header("Content-Type")
    resp.SetHeader("X-Seen-Content-Type", ct)
    resp.Bytes(req.Body)
})

srv.Router.GET("/conn", func(req *core.Request, resp *core.Response) {
    resp.String(req.RemoteAddr)
})

func (*Request) Header

func (r *Request) Header(name string) string

Header returns the first matching request header using ASCII case folding.

auth := req.Header("Authorization")
contentType := req.Header("content-type")
origin := req.Header("Origin")

func (*Request) HijackConn

func (r *Request) HijackConn() net.Conn

HijackConn hands the connection to the handler for protocols that need raw I/O.

srv.Router.GET("/raw", func(req *core.Request, resp *core.Response) {
    conn := req.HijackConn()
    if conn == nil {
        resp.Status(500).String("hijack failed")
        return
    }
    defer conn.Close()
    _, _ = conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nraw"))
})

func (*Request) ParamValue

func (r *Request) ParamValue(name string) string

ParamValue returns a named route parameter.

r.GET("/users/:id", func(req *core.Request, resp *core.Response) {
    resp.String(req.ParamValue("id"))
})

r.GET("/users/:id/posts/:postID", func(req *core.Request, resp *core.Response) {
    resp.String(req.ParamValue("id") + "/" + req.ParamValue("postID"))
})

func (*Request) Reset

func (r *Request) Reset()

func (*Request) SetParam

func (r *Request) SetParam(key, value string)

SetParam appends a synthetic route parameter.

r.Use(func(next core.HandlerFunc) core.HandlerFunc {
    return func(req *core.Request, resp *core.Response) {
        req.SetParam("tenant", "default")
        next(req, resp)
    }
})

type Response

type Response struct {
	StatusCode int
	Headers    [][2]string

	ContentType string
	// contains filtered or unexported fields
}

Response is the mutable response object passed to every handler.

srv.Router.GET("/text", func(req *core.Request, resp *core.Response) {
    resp.Status(200).String("hello")
})

srv.Router.GET("/json", func(req *core.Request, resp *core.Response) {
    resp.Status(200).
        SetHeader("Cache-Control", "no-store").
        JSONString(`{"ok":true}`)
})

srv.Router.GET("/binary", func(req *core.Request, resp *core.Response) {
    resp.Status(200).Bytes([]byte{0x41, 0x4c, 0x4f, 0x53})
})

func (*Response) BodyLen

func (r *Response) BodyLen() int

func (*Response) Bytes

func (r *Response) Bytes(b []byte) *Response

Bytes writes raw bytes as application/octet-stream.

resp.Status(200).Bytes(fileData)

func (*Response) GetBody

func (r *Response) GetBody() []byte

GetBody returns the current in-memory response body.

body := resp.GetBody()

func (*Response) HTML

func (r *Response) HTML(s string) *Response

HTML writes an HTML response body.

resp.Status(200).HTML("<h1>Welcome</h1>")

func (*Response) IsStreamed

func (r *Response) IsStreamed() bool

func (*Response) JSON

func (r *Response) JSON(jsonBytes []byte) *Response

JSON writes already-encoded JSON bytes.

resp.Status(200).JSON([]byte(`{"users":[]}`))
resp.JSON(payload)

func (*Response) JSONMarshal

func (r *Response) JSONMarshal(v any) error

JSONMarshal marshals a value with sonic and writes the result as JSON.

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

_ = resp.Status(200).JSONMarshal(User{ID: 1, Name: "Alice"})
_ = resp.JSONMarshal(map[string]any{"ok": true})

func (*Response) JSONString

func (r *Response) JSONString(s string) *Response

JSONString writes JSON from a string literal or prebuilt string.

resp.Status(200).JSONString(`{"ok":true}`)

func (*Response) Reset

func (r *Response) Reset()

func (*Response) SendFile

func (r *Response) SendFile(path string, opts ...SendFileOption) error

SendFile streams a file to the client with automatic MIME type detection, optional download attachment headers, and optional bandwidth rate limiting. The path is sanitized to prevent directory traversal.

// Serve a file inline:
resp.SendFile("static/image.png")

// Force download with a custom filename:
resp.SendFile("data/report.csv",
    core.WithAttachment("report-2024.csv"))

// Rate-limit to 10 Mbps:
resp.SendFile("videos/demo.mp4",
    core.WithRateLimit(10),
    core.WithAttachment("demo.mp4"))

func (*Response) SetBody

func (r *Response) SetBody(b []byte)

SetBody replaces the body without changing the current content type.

resp.ContentType = "application/xml"
resp.SetBody([]byte("<ok/>"))

func (*Response) SetHeader

func (r *Response) SetHeader(name, value string) *Response

SetHeader appends a sanitized response header.

resp.SetHeader("Cache-Control", "no-store")
resp.SetHeader("X-Frame-Options", "DENY")
resp.SetHeader("X-Request-ID", "req-42")

func (*Response) SetHeaderUnsafe

func (r *Response) SetHeaderUnsafe(name, value string) *Response

SetHeaderUnsafe appends a trusted response header without sanitization.

resp.SetHeaderUnsafe("Server", "ALOS")

func (*Response) SetSW

func (r *Response) SetSW(sw StreamWriter)

func (*Response) SetStreamer

func (r *Response) SetStreamer(sw StreamWriter)

func (*Response) Status

func (r *Response) Status(code int) *Response

Status sets the response status code and returns the same Response.

resp.Status(201).String("created")
resp.Status(404).JSONString(`{"error":"not found"}`)

func (*Response) Streamer

func (r *Response) Streamer() StreamWriter

func (*Response) String

func (r *Response) String(s string) *Response

String writes a UTF-8 plain-text response body.

resp.Status(200).String("hello world")
resp.String("pong")

type RouteGroup

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

RouteGroup is a collection of routes that share a common path prefix and middleware stack. Create one with Router.Group.

api := r.Group("/api/v1", authMiddleware)
api.GET("/users", listUsers)
api.POST("/users", createUser)

admin := api.Group("/admin", adminOnly)
admin.DELETE("/users/:id", deleteUser)

func (*RouteGroup) ANY

func (g *RouteGroup) ANY(path string, handler HandlerFunc)

ANY registers a handler for all HTTP methods on the group.

func (*RouteGroup) DELETE

func (g *RouteGroup) DELETE(path string, handler HandlerFunc)

DELETE registers a DELETE handler on the group.

func (*RouteGroup) GET

func (g *RouteGroup) GET(path string, handler HandlerFunc)

GET registers a GET handler on the group.

func (*RouteGroup) Group

func (g *RouteGroup) Group(prefix string, middleware ...MiddlewareFunc) *RouteGroup

Group creates a nested sub-group inheriting the parent's prefix and middleware.

v1 := r.Group("/v1")
admin := v1.Group("/admin", adminAuth)
admin.GET("/stats", statsHandler)  // matches /v1/admin/stats

func (*RouteGroup) HEAD

func (g *RouteGroup) HEAD(path string, handler HandlerFunc)

HEAD registers a HEAD handler on the group.

func (*RouteGroup) OPTIONS

func (g *RouteGroup) OPTIONS(path string, handler HandlerFunc)

OPTIONS registers an OPTIONS handler on the group.

func (*RouteGroup) PATCH

func (g *RouteGroup) PATCH(path string, handler HandlerFunc)

PATCH registers a PATCH handler on the group.

func (*RouteGroup) POST

func (g *RouteGroup) POST(path string, handler HandlerFunc)

POST registers a POST handler on the group.

func (*RouteGroup) PUT

func (g *RouteGroup) PUT(path string, handler HandlerFunc)

PUT registers a PUT handler on the group.

func (*RouteGroup) Use

func (g *RouteGroup) Use(middleware ...MiddlewareFunc)

Use adds middleware to the group. These run after any middleware passed to Group and before the route handler.

type Router

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

func NewRouter

func NewRouter() *Router

NewRouter creates a new Router with default 404 and 405 handlers. Register routes with the HTTP method helpers (GET, POST, PUT, DELETE, etc.) then call Build before passing the router to the server.

r := core.NewRouter()
r.GET("/", indexHandler)
r.Build()

func (*Router) ANY

func (r *Router) ANY(path string, handler HandlerFunc)

ANY registers a handler that matches all HTTP methods (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, CONNECT, TRACE) at the given path.

r.ANY("/health", func(req *core.Request, resp *core.Response) {
    resp.Status(200).String("ok")
})

func (*Router) Build

func (r *Router) Build()

Build pre-compiles the routing table by wrapping all handlers with global middleware and building index lookup tables. Call this once after all routes and middleware are registered, before the server starts handling requests.

r.GET("/", indexHandler)
r.Use(core.Logger)
r.Build()

func (*Router) DELETE

func (r *Router) DELETE(path string, handler HandlerFunc)

DELETE registers a handler for DELETE requests at the given path.

func (*Router) GET

func (r *Router) GET(path string, handler HandlerFunc)

GET registers a handler for GET requests at the given path.

func (*Router) Group

func (r *Router) Group(prefix string, middleware ...MiddlewareFunc) *RouteGroup

Group creates a new RouteGroup with the given path prefix and optional middleware. All routes registered on the group inherit the prefix and middleware chain.

api := r.Group("/api/v1")
api.Use(rateLimiter)
api.GET("/items", listItems)

func (*Router) HEAD

func (r *Router) HEAD(path string, handler HandlerFunc)

HEAD registers a handler for HEAD requests at the given path.

func (*Router) Handle

func (r *Router) Handle(method, path string, handler HandlerFunc)

Handle registers a handler for the given HTTP method and path pattern. Path patterns can include named parameters (:param) and catch-all segments (*name).

r.Handle("GET", "/users/:id", getUser)
r.Handle("GET", "/files/*filepath", serveFiles)

func (*Router) Lookup

func (r *Router) Lookup(method, path string, req *Request) HandlerFunc

Lookup finds the handler for the given method and path, populating any path parameters on the Request. Returns the not-found or method-not-allowed handler when no exact match exists. This is called internally by the server for each incoming request.

func (*Router) Match

func (r *Router) Match(method, path string) bool

Match reports whether a route exists for the given method and path without executing the handler. Useful for testing route registration.

if r.Match("GET", "/api/users") {
    fmt.Println("route exists")
}

func (*Router) MethodNotAllowed

func (r *Router) MethodNotAllowed(handler HandlerFunc)

MethodNotAllowed sets a custom handler that runs when a route exists but not for the requested HTTP method.

func (*Router) NotFound

func (r *Router) NotFound(handler HandlerFunc)

NotFound sets a custom handler that runs when no route matches the request path.

r.NotFound(func(req *core.Request, resp *core.Response) {
    resp.Status(404).JSON([]byte(`{"error":"not found"}`))
})

func (*Router) OPTIONS

func (r *Router) OPTIONS(path string, handler HandlerFunc)

OPTIONS registers a handler for OPTIONS requests at the given path.

func (*Router) PATCH

func (r *Router) PATCH(path string, handler HandlerFunc)

PATCH registers a handler for PATCH requests at the given path.

func (*Router) POST

func (r *Router) POST(path string, handler HandlerFunc)

POST registers a handler for POST requests at the given path.

func (*Router) PUT

func (r *Router) PUT(path string, handler HandlerFunc)

PUT registers a handler for PUT requests at the given path.

func (*Router) Use

func (r *Router) Use(middleware ...MiddlewareFunc)

Use registers one or more global middleware functions that run on every request in the order they are added.

r.Use(core.Logger, core.Recovery)
r.Use(core.CORS(core.NewCORSEngine(core.CORSConfig{
    AllowOrigins: []string{"*"},
})))

type SecurityHeadersConfig

type SecurityHeadersConfig struct {
	ContentTypeNosniff bool
	XFrameOptions      string
	XSSProtection      bool
	HSTSMaxAge         int
	HSTSSubdomains     bool
	HSTSPreload        bool
	ReferrerPolicy     string
}

SecurityHeadersConfig controls which security-related headers are injected into every response.

cfg := core.SecurityHeadersConfig{
	ContentTypeNosniff: true,
	XFrameOptions:      "DENY",
	XSSProtection:      true,
	HSTSMaxAge:         63072000,
	HSTSSubdomains:     true,
	HSTSPreload:        true,
	ReferrerPolicy:     "strict-origin-when-cross-origin",
}

func DefaultSecurityHeaders

func DefaultSecurityHeaders() SecurityHeadersConfig

DefaultSecurityHeaders returns a SecurityHeadersConfig with all protections enabled: nosniff, DENY framing, XSS filter, 2-year HSTS with includeSubDomains and preload, and strict-origin-when-cross-origin referrer policy.

type SendFileOption

type SendFileOption func(*sendFileConfig)

func WithAttachment

func WithAttachment(filename string) SendFileOption

WithAttachment sets the Content-Disposition header to "attachment" with the given filename, triggering a file download in the browser.

resp.SendFile("data/export.csv", core.WithAttachment("export-2024.csv"))

func WithContentType

func WithContentType(ct string) SendFileOption

WithContentType overrides the auto-detected MIME type for the file.

resp.SendFile("data.bin", core.WithContentType("application/pdf"))

func WithRateLimit

func WithRateLimit(mbps int64) SendFileOption

WithRateLimit caps the file transfer speed to the given rate in megabits per second and sets the burst size equal to the rate. Valid range is 1-250000 Mbps.

resp.SendFile("video.mp4", core.WithRateLimit(50))  // 50 Mbps

func WithRateLimitBurst

func WithRateLimitBurst(mbps, burstMbps int64) SendFileOption

WithRateLimitBurst is like WithRateLimit but also sets a separate burst size in Mbps, allowing short bursts above the steady-state rate.

resp.SendFile("file.zip", core.WithRateLimitBurst(10, 50))  // 10 Mbps steady, 50 Mbps burst

type Server

type Server struct {
	Router    *Router
	CORS      *CORSEngine
	RateLimit *RateLimitEngine
	// contains filtered or unexported fields
}

Server is the main entry point. Create one with New, configure routes on its Router field, then call ListenAndServeTLS or ListenAndServe.

s := core.New(core.Config{Addr: ":443"})
s.Router.GET("/", handler)
log.Fatal(s.ListenAndServeTLS())

The Server manages its own TLS 1.3 implementation, optional HTTP/2 multiplexing, connection pooling, reverse proxy, CORS, rate limiting, and ACME. All public methods are safe for concurrent use.

func New

func New(configs ...Config) *Server

New creates a Server with the given Config. When called without arguments it uses DefaultConfig. The returned server is ready for route registration; call ListenAndServeTLS or ListenAndServe to start accepting connections.

s := core.New(core.Config{
	Addr:      ":443",
	PlainHTTP: false,
	Listeners: 4,
})
s.Router.GET("/hello", func(req *core.Request, resp *core.Response) {
	resp.Status(200).String("hello")
})
log.Fatal(s.ListenAndServeTLS())

func NewServer

func NewServer(addr string) *Server

func (*Server) AddACMEDomain

func (s *Server) AddACMEDomain(domain string, email ...string)

func (*Server) AddCert

func (s *Server) AddCert(domain string, certPEM, keyPEM []byte) error

func (*Server) AddCertFromFiles

func (s *Server) AddCertFromFiles(domain, certFile, keyFile string) error

func (*Server) AddCerts

func (s *Server) AddCerts(domain string, certPEM, keyPEM []byte) error

func (*Server) AddDomainCert

func (s *Server) AddDomainCert(domain string, certPEM, keyPEM []byte) error

func (*Server) AddHTTPRoute

func (s *Server) AddHTTPRoute(route HTTPRoute)

func (*Server) AddProxyDomain

func (s *Server) AddProxyDomain(cfg DomainConfig)

AddProxyDomain registers a reverse-proxy domain. If the ProxyEngine does not exist yet it is created automatically. Safe to call at runtime.

s.AddProxyDomain(core.DomainConfig{
	Domain: "api.example.com",
	Backends: []core.BackendConfig{
		{Addr: "10.0.0.1:8080", Weight: 3},
		{Addr: "10.0.0.2:8080", Weight: 1},
	},
	Balancer:   core.LBWeightedRR,
	MaxRetries: 2,
	HealthCheck: core.HealthCheckConfig{
		Path:     "/health",
		Interval: 10 * time.Second,
		Timeout:  2 * time.Second,
	},
})

func (*Server) AddRateLimitRule

func (s *Server) AddRateLimitRule(rule RateLimitRule)

AddRateLimitRule appends a single rule without replacing existing ones.

func (*Server) AddSelfSignedCert

func (s *Server) AddSelfSignedCert(domain string) error

func (*Server) EnableACME

func (s *Server) EnableACME(cfg ACMEConfig)

func (*Server) FineTune

func (s *Server) FineTune() error

func (*Server) FineTuneWithOptions

func (s *Server) FineTuneWithOptions(options FineTuneOptions) (*FineTuneReport, error)

func (*Server) IsDebug

func (s *Server) IsDebug() bool

func (*Server) ListCerts

func (s *Server) ListCerts() []CertInfo

func (*Server) ListenAndServe

func (s *Server) ListenAndServe() error

ListenAndServe starts a plain HTTP/1.1 server (no TLS). Use this when Config.PlainHTTP is true or when TLS termination is handled upstream.

s := core.New(core.Config{Addr: ":80", PlainHTTP: true})
s.Router.GET("/", handler)
log.Fatal(s.ListenAndServe())

func (*Server) ListenAndServeTLS

func (s *Server) ListenAndServeTLS() error

ListenAndServeTLS starts the HTTPS server with ALOS's built-in TLS 1.3 stack. It loads certificates (self-signed, manual PEM, or ACME), opens the configured number of SO_REUSEPORT listeners, optionally advertises HTTP/2 depending on Config.DisableHTTP2, starts the HTTP-to-HTTPS redirect listener, and blocks until Shutdown is called or an unrecoverable error occurs.

log.Fatal(s.ListenAndServeTLS())

func (*Server) LocalIPs

func (s *Server) LocalIPs() []net.IP

func (*Server) NewH1StreamWriter

func (s *Server) NewH1StreamWriter(conn net.Conn, writer *TrafficAEAD) *H1StreamWriter

func (*Server) NewH2StreamWriter

func (s *Server) NewH2StreamWriter(streamID uint32, hc *H2Conn, streamWindow *atomic.Int64) *H2StreamWriter

func (*Server) NewPlainH1StreamWriter

func (s *Server) NewPlainH1StreamWriter(conn net.Conn) *PlainH1StreamWriter

func (*Server) OnProxyError

func (s *Server) OnProxyError(fn ProxyErrorFunc)

OnProxyError registers a callback invoked whenever a backend request fails. Use it for alerting or structured logging.

s.OnProxyError(func(pe core.ProxyError) {
	log.Printf("proxy error: domain=%s backend=%s err=%v",
		pe.Domain, pe.Backend, pe.Err)
})

func (*Server) OnProxyRequest

func (s *Server) OnProxyRequest(fn ProxyInterceptFunc)

OnProxyRequest registers a callback invoked before the request is forwarded to a backend. Modify pr.Headers or pr.Host to alter the outgoing request. Return false to reject with 502.

s.OnProxyRequest(func(pr *core.ProxyRequest) bool {
	pr.Headers = append(pr.Headers, [2]string{"X-Forwarded-For", pr.ClientAddr})
	return true
})

func (*Server) OnProxyResponse

func (s *Server) OnProxyResponse(fn ProxyResponseFunc)

OnProxyResponse registers a callback invoked after a backend responds (or a cache hit is served). Modify pr.Headers or pr.StatusCode before they reach the client. Call pr.CacheThis() to store the response.

s.OnProxyResponse(func(pr *core.ProxyResponse) {
	pr.Headers = append(pr.Headers, [2]string{"X-Proxy-By", "ALOS"})
})

func (*Server) OnRateLimit

func (s *Server) OnRateLimit(fn RateLimitFunc)

OnRateLimit registers a global callback invoked when a request is rate-limited. Return true to indicate the response has been handled, or false to fall through to the default 429 response.

s.OnRateLimit(func(event core.RateLimitEvent, req *core.Request, resp *core.Response) bool {
	resp.Status(429).JSON([]byte(`{"error":"too many requests"}`))
	return true
})

func (*Server) Proxy

func (s *Server) Proxy() *ProxyEngine

func (*Server) ProxyCacheStats

func (s *Server) ProxyCacheStats() (entries int64, totalBytes int64, hits uint64, misses uint64)

ProxyCacheStats returns current cache metrics: number of entries, total bytes used, cumulative hits, and cumulative misses.

func (*Server) PurgeAllProxyCache

func (s *Server) PurgeAllProxyCache()

PurgeAllProxyCache drops every entry in the response cache.

func (*Server) PurgeDomainCache

func (s *Server) PurgeDomainCache(domain string) int64

PurgeDomainCache removes all cached responses for the given domain. Returns the number of entries purged.

func (*Server) PurgeProxyCache

func (s *Server) PurgeProxyCache(method, host, path string) bool

PurgeProxyCache removes a single cached response by method, host, and path. Returns true if an entry was found and removed.

func (*Server) ReloadCert

func (s *Server) ReloadCert(domain, certFile, keyFile string) error

func (*Server) RemoveCert

func (s *Server) RemoveCert(domain string)

func (*Server) RemoveHTTPRoute

func (s *Server) RemoveHTTPRoute(pathPrefix string)

func (*Server) RemoveProxyDomain

func (s *Server) RemoveProxyDomain(domain string)

RemoveProxyDomain removes a domain and cleans up health checkers and connection pools for all its backends.

func (*Server) RemoveRateLimitRule

func (s *Server) RemoveRateLimitRule(path string)

RemoveRateLimitRule removes the rule matching the given path pattern.

func (*Server) ServeH1

func (s *Server) ServeH1(conn net.Conn, reader, writer *TrafficAEAD, hdrBuf []byte)

func (*Server) ServeH1Plain

func (s *Server) ServeH1Plain(conn net.Conn)

func (*Server) ServeH2

func (s *Server) ServeH2(conn net.Conn, reader, writer *TrafficAEAD, hdrBuf []byte)

func (*Server) ServeH2Plain

func (s *Server) ServeH2Plain(conn net.Conn)

func (*Server) SetCORS

func (s *Server) SetCORS(cfg CORSConfig)

SetCORS configures CORS policy for the server.

s.SetCORS(core.CORSConfig{
	AllowOrigins:     []string{"https://example.com"},
	AllowMethods:     []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
	AllowHeaders:     []string{"Content-Type", "Authorization"},
	ExposeHeaders:    []string{"X-Request-ID"},
	AllowCredentials: true,
	MaxAge:           86400,
})

func (*Server) SetDebug

func (s *Server) SetDebug(on bool)

func (*Server) SetDefaultCert

func (s *Server) SetDefaultCert(domain string)

func (*Server) SetHTTPRoutes

func (s *Server) SetHTTPRoutes(routes []HTTPRoute)

SetHTTPRoutes configures port-80 HTTP routes that proxy plain HTTP traffic to backend servers (instead of 301-redirecting to HTTPS).

s.SetHTTPRoutes([]core.HTTPRoute{
	{PathPrefix: "/api/", Backend: "10.0.0.5:3000"},
	{PathPrefix: "/",     Backend: "10.0.0.6:8080", HostHeader: "backend.local"},
})

func (*Server) SetLogRequests

func (s *Server) SetLogRequests(on bool)

func (*Server) SetProxy

func (s *Server) SetProxy(pe *ProxyEngine)

func (*Server) SetProxyCache

func (s *Server) SetProxyCache(cfg ProxyCacheConfig)

SetProxyCache enables proxy response caching. Cached responses are matched by method + host + path (query string stripped). Configure per-path rules, total budget, entry size limits, and pre-compression.

s.SetProxyCache(core.ProxyCacheConfig{
	MaxEntrySize:   4 << 20,
	MaxTotalBytes:  256 << 20,
	DefaultMaxAge:  5 * time.Minute,
	PreCompress:    true,
	CompressLevel:  6,
	CompressMinLen: 512,
	Rules: []core.CacheRule{
		{PathPrefix: "/api/", MaxAge: 30 * time.Second},
		{PathPrefix: "/static/", MaxAge: 24 * time.Hour},
	},
})

func (*Server) SetRateLimitRules

func (s *Server) SetRateLimitRules(rules []RateLimitRule)

SetRateLimitRules replaces all rate-limit rules. Each rule matches a path pattern and enforces a request budget per client IP.

s.SetRateLimitRules([]core.RateLimitRule{
	{Path: "/api/*",  MaxReqs: 100, Window: 60 * time.Second, BlockFor: 5 * time.Minute},
	{Path: "/*",      MaxReqs: 300, Window: 60 * time.Second, BlockFor: 2 * time.Minute},
})

func (*Server) Shutdown

func (s *Server) Shutdown(ctx context.Context) error

Shutdown gracefully drains in-flight connections and stops ACME renewal, rate-limit cleanup, and listener goroutines. Pass a context with a deadline to bound how long the drain may take.

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := s.Shutdown(ctx); err != nil {
	log.Printf("shutdown: %v", err)
}

func (*Server) UpdateCORS

func (s *Server) UpdateCORS(cfg CORSConfig)

func (*Server) UpdateCert

func (s *Server) UpdateCert(domain string, certPEM, keyPEM []byte) error

type ServerStats

type ServerStats struct {
	ActiveConns atomic.Int64

	TotalConns atomic.Uint64

	H2Conns atomic.Uint64

	H1Conns atomic.Uint64

	TotalReqs atomic.Uint64

	BytesIn atomic.Uint64

	BytesOut atomic.Uint64
	// contains filtered or unexported fields
}
var Stats ServerStats

type ShardedMap

type ShardedMap[K comparable, V any] struct {
	// contains filtered or unexported fields
}

func NewShardedMap

func NewShardedMap[K comparable, V any](hasher func(K) uint64) *ShardedMap[K, V]

func (*ShardedMap[K, V]) Delete

func (s *ShardedMap[K, V]) Delete(key K)

func (*ShardedMap[K, V]) Load

func (s *ShardedMap[K, V]) Load(key K) (V, bool)

func (*ShardedMap[K, V]) LoadOrStore

func (s *ShardedMap[K, V]) LoadOrStore(key K, val V) (V, bool)

func (*ShardedMap[K, V]) Range

func (s *ShardedMap[K, V]) Range(fn func(K, V) bool)

func (*ShardedMap[K, V]) Store

func (s *ShardedMap[K, V]) Store(key K, val V)

type StreamWriter

type StreamWriter interface {
	WriteHeader(statusCode int, headers [][2]string, contentType string) error
	WriteChunk(data []byte) error
	Flush() error
	Close() error
}

type TLSConn

type TLSConn struct {
	Raw     net.Conn
	ReadBuf []byte
}

func NewTLSConn

func NewTLSConn(c net.Conn) *TLSConn

func (*TLSConn) Release

func (c *TLSConn) Release()

type TokenBucket

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

func NewTokenBucket

func NewTokenBucket(ratePerSec, burst int64) *TokenBucket

func (*TokenBucket) Allow

func (tb *TokenBucket) Allow(tokens int64) bool

func (*TokenBucket) Wait

func (tb *TokenBucket) Wait(tokens int64)

func (*TokenBucket) WaitWithDeadline

func (tb *TokenBucket) WaitWithDeadline(tokens int64, deadline time.Time) bool

type TrafficAEAD

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

func NewTrafficAEAD

func NewTrafficAEAD(_ func() hash.Hash, secret []byte, cs *CipherSuiteConfig) (*TrafficAEAD, error)

func (*TrafficAEAD) Decrypt

func (t *TrafficAEAD) Decrypt(ciphertext []byte) ([]byte, error)

func (*TrafficAEAD) Encrypt

func (t *TrafficAEAD) Encrypt(plaintext []byte) []byte

func (*TrafficAEAD) EncryptAppend

func (t *TrafficAEAD) EncryptAppend(dst, plaintext []byte) []byte

func (*TrafficAEAD) Overhead

func (t *TrafficAEAD) Overhead() int

type WSConn

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

WSConn is a server-side WebSocket connection (RFC 6455). Create one by calling UpgradeWebSocket inside a route handler, then use ReadMessage and WriteMessage (or WriteText / WriteBinary) to exchange data.

ws := core.UpgradeWebSocket(req, resp)
if ws == nil {
    return
}
defer ws.Close()
for {
    op, msg, err := ws.ReadMessage()
    if err != nil {
        break
    }
    ws.WriteText("echo: " + string(msg))
}

func UpgradeWebSocket

func UpgradeWebSocket(req *Request, resp *Response) *WSConn

UpgradeWebSocket performs the WebSocket handshake on the current request. It validates the required headers (Upgrade, Connection, Sec-WebSocket-Key, Sec-WebSocket-Version) and hijacks the underlying connection.

Returns a *WSConn on success, or nil if the handshake fails (in which case an error response is already written).

r.GET("/ws", func(req *core.Request, resp *core.Response) {
    ws := core.UpgradeWebSocket(req, resp)
    if ws == nil {
        return
    }
    defer ws.Close()
    for {
        _, msg, err := ws.ReadMessage()
        if err != nil {
            break
        }
        ws.WriteText(string(msg))
    }
})

func (*WSConn) Close

func (ws *WSConn) Close() error

Close sends a close frame and closes the underlying connection. Safe to call multiple times.

func (*WSConn) Ping

func (ws *WSConn) Ping() error

Ping sends a WebSocket ping frame to the client. The client should respond with a pong automatically.

func (*WSConn) ReadMessage

func (ws *WSConn) ReadMessage() (byte, []byte, error)

ReadMessage reads the next WebSocket frame. It returns the opcode (0x1 for text, 0x2 for binary), the payload, and any error. Close frames are handled automatically — a close reply is sent and io.EOF is returned. Ping frames trigger an automatic pong response.

func (*WSConn) SetDeadline

func (ws *WSConn) SetDeadline(t time.Time) error

SetDeadline sets the read and write deadline on the underlying connection.

func (*WSConn) WriteBinary

func (ws *WSConn) WriteBinary(data []byte) error

WriteBinary sends a binary WebSocket frame.

func (*WSConn) WriteMessage

func (ws *WSConn) WriteMessage(opcode byte, data []byte) error

WriteMessage writes a WebSocket frame with the given opcode and payload. Typically use WriteText or WriteBinary instead of calling this directly.

func (*WSConn) WriteText

func (ws *WSConn) WriteText(msg string) error

WriteText sends a text WebSocket frame.

ws.WriteText("hello client")

type WriteRequest

type WriteRequest struct {
	Data []byte
	Done chan error
	// contains filtered or unexported fields
}

Jump to

Keyboard shortcuts

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