proxy

package
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Apr 19, 2026 License: MIT Imports: 34 Imported by: 0

Documentation

Overview

Package proxy implements the forward and reverse proxy data plane for Resin.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrAuthRequired = &ProxyError{
		HTTPCode:   http.StatusProxyAuthRequired,
		ResinError: "AUTH_REQUIRED",
		Message:    "Proxy authentication required",
	}
	ErrAuthFailed = &ProxyError{
		HTTPCode:   http.StatusForbidden,
		ResinError: "AUTH_FAILED",
		Message:    "Proxy authentication failed",
	}
	ErrURLParseError = &ProxyError{
		HTTPCode:   http.StatusBadRequest,
		ResinError: "URL_PARSE_ERROR",
		Message:    "Failed to parse request URL",
	}
	ErrInvalidProtocol = &ProxyError{
		HTTPCode:   http.StatusBadRequest,
		ResinError: "INVALID_PROTOCOL",
		Message:    "Protocol must be http or https",
	}
	ErrInvalidHost = &ProxyError{
		HTTPCode:   http.StatusBadRequest,
		ResinError: "INVALID_HOST",
		Message:    "Invalid or empty host",
	}
	ErrPlatformNotFound = &ProxyError{
		HTTPCode:   http.StatusNotFound,
		ResinError: "PLATFORM_NOT_FOUND",
		Message:    "Platform not found",
	}
	ErrAccountRejected = &ProxyError{
		HTTPCode:   http.StatusForbidden,
		ResinError: "ACCOUNT_REJECTED",
		Message:    "Account extraction failed and platform rejects unmatched requests",
	}
	ErrNoAvailableNodes = &ProxyError{
		HTTPCode:   http.StatusServiceUnavailable,
		ResinError: "NO_AVAILABLE_NODES",
		Message:    "No available nodes for routing",
	}
	ErrUpstreamConnectFailed = &ProxyError{
		HTTPCode:   http.StatusBadGateway,
		ResinError: "UPSTREAM_CONNECT_FAILED",
		Message:    "Failed to connect to upstream",
	}
	ErrUpstreamTimeout = &ProxyError{
		HTTPCode:   http.StatusGatewayTimeout,
		ResinError: "UPSTREAM_TIMEOUT",
		Message:    "Upstream connection or response timed out",
	}
	ErrUpstreamRequestFailed = &ProxyError{
		HTTPCode:   http.StatusBadGateway,
		ResinError: "UPSTREAM_REQUEST_FAILED",
		Message:    "Upstream request failed",
	}
	ErrInternalError = &ProxyError{
		HTTPCode:   http.StatusInternalServerError,
		ResinError: "INTERNAL_ERROR",
		Message:    "Internal proxy error",
	}
)

Predefined proxy errors aligned with DESIGN.md error specification.

Functions

func NewCountingListener

func NewCountingListener(ln net.Listener, sink MetricsEventSink) net.Listener

NewCountingListener wraps a listener with connection lifecycle tracking.

func NormalizeRulePrefix

func NormalizeRulePrefix(prefix string) (string, error)

NormalizeRulePrefix canonicalizes an account-header rule prefix.

Rules:

  • Trim surrounding whitespace.
  • Reject empty values and values containing '?'.
  • Keep "*" as wildcard.
  • Lowercase only the host part before the first '/'.
  • Keep path part (if any) as-is.

Types

type AccountMatcher

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

AccountMatcher performs longest-prefix matching on (host, path) to find the set of header names from which to extract an account identity.

Rules are stored in a segment-based trie keyed by domain (lowercase) then path segments. The wildcard key "*" serves as a catch-all fallback.

func BuildAccountMatcher

func BuildAccountMatcher(rules []model.AccountHeaderRule) *AccountMatcher

BuildAccountMatcher constructs a matcher from persisted rules.

func (*AccountMatcher) Match

func (m *AccountMatcher) Match(host, path string) []string

Match returns the header names for the longest-prefix rule matching the given host and path. Returns nil if no rule matches.

func (*AccountMatcher) MatchWithPrefix

func (m *AccountMatcher) MatchWithPrefix(host, path string) (string, []string)

MatchWithPrefix returns the matched url_prefix and its headers for the longest-prefix rule matching the given host/path. If no rule matches, it returns ("", nil). Wildcard fallback returns ("*", wildcardHeaders).

type AccountMatcherRuntime

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

AccountMatcherRuntime stores the current account matcher in an atomic pointer. Readers stay lock-free; updates swap in a fully built immutable matcher.

func NewAccountMatcherRuntime

func NewAccountMatcherRuntime(initial *AccountMatcher) *AccountMatcherRuntime

NewAccountMatcherRuntime creates a runtime matcher store with an initial matcher. If initial is nil, it uses an empty matcher.

func (*AccountMatcherRuntime) Current

func (r *AccountMatcherRuntime) Current() *AccountMatcher

Current returns the currently published matcher pointer.

func (*AccountMatcherRuntime) Match

func (r *AccountMatcherRuntime) Match(host, path string) []string

Match resolves header rules using the current matcher snapshot.

func (*AccountMatcherRuntime) MatchWithPrefix

func (r *AccountMatcherRuntime) MatchWithPrefix(host, path string) (string, []string)

MatchWithPrefix resolves rules using the current matcher snapshot and returns both the matched url_prefix and header list.

func (*AccountMatcherRuntime) ReplaceRules

func (r *AccountMatcherRuntime) ReplaceRules(rules []model.AccountHeaderRule)

ReplaceRules rebuilds a matcher from persisted rules and atomically replaces it.

func (*AccountMatcherRuntime) Swap

func (r *AccountMatcherRuntime) Swap(next *AccountMatcher)

Swap atomically replaces the current matcher. Passing nil resets to an empty matcher.

type AccountRuleMatcher

type AccountRuleMatcher interface {
	Match(host, path string) []string
}

AccountRuleMatcher provides longest-prefix rule matching for account headers. ReverseProxy depends on this interface to allow runtime matcher swapping.

type ConfigAwareEventEmitter

type ConfigAwareEventEmitter struct {
	Base              EventEmitter
	RequestLogEnabled func() bool

	// Reverse proxy request-log detail controls (hot-reload friendly).
	ReverseProxyLogDetailEnabled       func() bool
	ReverseProxyLogReqHeadersMaxBytes  func() int
	ReverseProxyLogReqBodyMaxBytes     func() int
	ReverseProxyLogRespHeadersMaxBytes func() int
	ReverseProxyLogRespBodyMaxBytes    func() int
}

ConfigAwareEventEmitter wraps another EventEmitter and gates request-log emission by a runtime flag provider (hot-reload friendly).

func (ConfigAwareEventEmitter) EmitRequestFinished

func (e ConfigAwareEventEmitter) EmitRequestFinished(ev RequestFinishedEvent)

func (ConfigAwareEventEmitter) EmitRequestLog

func (e ConfigAwareEventEmitter) EmitRequestLog(ev RequestLogEntry)

type ConnectionDirection

type ConnectionDirection int

ConnectionDirection indicates inbound vs outbound connection flow.

const (
	ConnectionInbound ConnectionDirection = iota
	ConnectionOutbound
)

type ConnectionLifecycleEvent

type ConnectionLifecycleEvent struct {
	Direction ConnectionDirection
	Op        ConnectionOp
}

ConnectionLifecycleEvent tracks connection open/close.

type ConnectionOp

type ConnectionOp int

ConnectionOp is the operation type for a connection lifecycle event.

const (
	ConnectionOpen ConnectionOp = iota
	ConnectionClose
)

type EventEmitter

type EventEmitter interface {
	EmitRequestFinished(RequestFinishedEvent)
	EmitRequestLog(RequestLogEntry)
}

EventEmitter defines the interface for proxy-layer event emission. Covers both metrics and requestlog event paths (STAGES.md Task 8).

type ForwardProxy

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

ForwardProxy implements an HTTP forward proxy with Proxy-Authorization authentication, HTTP request forwarding, and CONNECT tunneling.

func NewForwardProxy

func NewForwardProxy(cfg ForwardProxyConfig) *ForwardProxy

NewForwardProxy creates a new forward proxy handler.

func (*ForwardProxy) ServeHTTP

func (p *ForwardProxy) ServeHTTP(w http.ResponseWriter, r *http.Request)

type ForwardProxyConfig

type ForwardProxyConfig struct {
	ProxyToken        string
	AuthVersion       string
	Router            *routing.Router
	Pool              outbound.PoolAccessor
	Health            HealthRecorder
	Events            EventEmitter
	MetricsSink       MetricsEventSink
	OutboundTransport OutboundTransportConfig
	TransportPool     *OutboundTransportPool
}

ForwardProxyConfig holds dependencies for the forward proxy.

type HealthRecorder

type HealthRecorder interface {
	RecordResult(hash node.Hash, success bool)
	RecordLatency(hash node.Hash, rawTarget string, latency *time.Duration)
}

HealthRecorder abstracts passive health feedback reporting. topology.GlobalNodePool satisfies this interface.

type MetricsEventSink

type MetricsEventSink interface {
	// OnTrafficDelta reports a global traffic byte count delta.
	OnTrafficDelta(ingressBytes, egressBytes int64)
	// OnConnectionLifecycle reports a connection open/close event.
	OnConnectionLifecycle(direction ConnectionDirection, op ConnectionOp)
}

MetricsEventSink receives traffic and connection lifecycle events from the proxy layer. Implemented by metrics.Manager (wired in main.go). This interface is defined here (in the proxy package) to avoid an import cycle between proxy and metrics.

type NoOpEventEmitter

type NoOpEventEmitter struct{}

NoOpEventEmitter is a no-op implementation used until Phase 7/8.

func (NoOpEventEmitter) EmitRequestFinished

func (NoOpEventEmitter) EmitRequestFinished(RequestFinishedEvent)

func (NoOpEventEmitter) EmitRequestLog

func (NoOpEventEmitter) EmitRequestLog(RequestLogEntry)

type OutboundTransportConfig

type OutboundTransportConfig struct {
	MaxIdleConns        int
	MaxIdleConnsPerHost int
	IdleConnTimeout     time.Duration
}

type OutboundTransportPool

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

OutboundTransportPool manages reusable outbound HTTP transports keyed by node hash. A single instance should be shared by forward/reverse proxies so keep-alive pools are reused and can be evicted on node removal.

func NewOutboundTransportPool

func NewOutboundTransportPool(cfg OutboundTransportConfig) *OutboundTransportPool

NewOutboundTransportPool creates a transport pool with normalized settings.

func (*OutboundTransportPool) CloseAll

func (p *OutboundTransportPool) CloseAll()

CloseAll closes idle connections and clears all pooled transports.

func (*OutboundTransportPool) Evict

func (p *OutboundTransportPool) Evict(hash node.Hash)

Evict closes idle connections for one node transport and removes it from pool.

func (*OutboundTransportPool) Get

Get returns a reusable transport for the given node hash.

type PlatformLookup

type PlatformLookup interface {
	GetPlatform(id string) (*platform.Platform, bool)
	GetPlatformByName(name string) (*platform.Platform, bool)
}

PlatformLookup provides read-only access to platforms.

type ProxyError

type ProxyError struct {
	HTTPCode   int
	ResinError string // X-Resin-Error header value
	Message    string // plain-text body
}

ProxyError represents a structured proxy error response.

type ProxyType

type ProxyType int
const (
	ProxyTypeForward       ProxyType = 1
	ProxyTypeReverse       ProxyType = 2
	ProxyTypeSocks5Forward ProxyType = 3
)

type RequestFinishedEvent

type RequestFinishedEvent struct {
	PlatformID string
	ProxyType  ProxyType // 1=http forward, 2=reverse, 3=socks5 forward
	IsConnect  bool
	NetOK      bool
	DurationNs int64
}

RequestFinishedEvent is emitted when a proxy request completes. Used by the metrics subsystem (Phase 8).

type RequestLogEntry

type RequestLogEntry struct {
	ID              string    // optional stable ID; repo generates one when empty
	StartedAtNs     int64     // request start time (Unix nano), used as ts_ns in DB
	ProxyType       ProxyType // 1=http forward, 2=reverse, 3=socks5 forward
	ClientIP        string
	PlatformID      string
	PlatformName    string
	Account         string
	TargetHost      string
	TargetURL       string
	NodeHash        string
	NodeTag         string // display tag: "<Subscription>/<Tag>" (DESIGN.md §601)
	EgressIP        string
	DurationNs      int64
	NetOK           bool
	HTTPMethod      string
	HTTPStatus      int
	ResinError      string // logical proxy error code, e.g. UPSTREAM_TIMEOUT
	UpstreamStage   string // where upstream/network failure happened
	UpstreamErrKind string // normalized error family
	UpstreamErrno   string // normalized errno, when available
	UpstreamErrMsg  string // sanitized upstream error message
	IngressBytes    int64  // bytes from upstream to client (header + body)
	EgressBytes     int64  // bytes from client to upstream (header + body)

	// Optional detail payload (mainly for reverse proxy request logging).
	ReqHeaders           []byte
	ReqHeadersLen        int
	ReqHeadersTruncated  bool
	ReqBody              []byte
	ReqBodyLen           int
	ReqBodyTruncated     bool
	RespHeaders          []byte
	RespHeadersLen       int
	RespHeadersTruncated bool
	RespBody             []byte
	RespBodyLen          int
	RespBodyTruncated    bool
}

RequestLogEntry captures per-request details for the structured request log. Used by the requestlog subsystem (Phase 8).

type ReverseProxy

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

ReverseProxy implements an HTTP reverse proxy. Identity segment format depends on auth version: V1: /PROXY_TOKEN/Platform.Account/protocol/host/path?query LEGACY_V0: /PROXY_TOKEN/Platform:Account/protocol/host/path?query

func NewReverseProxy

func NewReverseProxy(cfg ReverseProxyConfig) *ReverseProxy

NewReverseProxy creates a new reverse proxy handler.

func (*ReverseProxy) ServeHTTP

func (p *ReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request)

type ReverseProxyConfig

type ReverseProxyConfig struct {
	ProxyToken        string
	AuthVersion       string
	Router            *routing.Router
	Pool              outbound.PoolAccessor
	PlatformLookup    PlatformLookup
	Health            HealthRecorder
	Matcher           AccountRuleMatcher
	Events            EventEmitter
	MetricsSink       MetricsEventSink
	OutboundTransport OutboundTransportConfig
	TransportPool     *OutboundTransportPool
}

ReverseProxyConfig holds dependencies for the reverse proxy.

type Socks5Inbound added in v1.1.0

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

Socks5Inbound implements SOCKS5 CONNECT over a raw TCP connection.

func NewSocks5Inbound added in v1.1.0

func NewSocks5Inbound(cfg Socks5InboundConfig) *Socks5Inbound

NewSocks5Inbound creates a new SOCKS5 inbound handler.

func (*Socks5Inbound) ServeConn added in v1.1.0

func (s *Socks5Inbound) ServeConn(conn net.Conn)

ServeConn handles a SOCKS5 session on an already-accepted TCP connection.

func (*Socks5Inbound) ServeConnContext added in v1.1.0

func (s *Socks5Inbound) ServeConnContext(baseCtx context.Context, conn net.Conn)

ServeConnContext handles a SOCKS5 session with a caller-provided base context.

type Socks5InboundConfig added in v1.1.0

type Socks5InboundConfig struct {
	ProxyToken  string
	AuthVersion string
	Router      *routing.Router
	Pool        outbound.PoolAccessor
	Health      HealthRecorder
	Events      EventEmitter
	MetricsSink MetricsEventSink
}

Socks5InboundConfig holds dependencies for the SOCKS5 inbound handler.

Jump to

Keyboard shortcuts

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