proxy

package
v0.0.0-...-f8a09cc Latest Latest
Warning

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

Go to latest
Published: Sep 21, 2026 License: Apache-2.0 Imports: 29 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var UserChainContextKey = userChainKey{}

UserChainContextKey is exported for use in the handler.

Functions

func BidirectionalCopy

func BidirectionalCopy(client, upstream net.Conn) error

BidirectionalCopy copies data between client and upstream in both directions concurrently. It returns when either direction encounters an error or EOF. On Linux with raw TCP sockets, it attempts splice(2) for zero-copy transfer before falling back to io.Copy.

func ClearTransportCache

func ClearTransportCache()

ClearTransportCache drops all cached transports.

func CreateProxyTransport

func CreateProxyTransport(p *models.Proxy) (*http.Transport, error)

CreateProxyTransport creates an HTTP transport configured for the given proxy This is shared between proxy handler and health checker

func GetOrCreateTransport

func GetOrCreateTransport(p *models.Proxy) (*http.Transport, error)

GetOrCreateTransport returns a cached transport for the given proxy, or creates and caches a new one.

func InvalidateTransport

func InvalidateTransport(p *models.Proxy)

InvalidateTransport removes any cached transport(s) for the given proxy, regardless of credentials/version. Callers should invoke this when a proxy is updated or deleted so the next request rebuilds with fresh settings.

Types

type AuthMiddleware

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

AuthMiddleware handles proxy authentication

func NewAuthMiddleware

func NewAuthMiddleware(settings models.AuthenticationSettings) *AuthMiddleware

NewAuthMiddleware creates a new authentication middleware

func (*AuthMiddleware) HandleConnect

func (m *AuthMiddleware) HandleConnect(req *http.Request) (*http.Request, *http.Response)

HandleConnect validates proxy authentication for HTTPS CONNECT requests

func (*AuthMiddleware) HandleRequest

func (m *AuthMiddleware) HandleRequest(req *http.Request) (*http.Request, *http.Response)

HandleRequest validates proxy authentication for HTTP requests

func (*AuthMiddleware) IsEnabled

func (m *AuthMiddleware) IsEnabled() bool

IsEnabled reports whether legacy single-user auth is currently enforcing.

func (*AuthMiddleware) UpdateSettings

func (m *AuthMiddleware) UpdateSettings(settings models.AuthenticationSettings)

UpdateSettings updates the authentication settings

type BaseSelector

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

BaseSelector contains common fields for all selectors

type HealthChecker

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

HealthChecker manages proxy health checking

func NewHealthChecker

func NewHealthChecker(
	proxyRepo *repository.ProxyRepository,
	settingsRepo *repository.SettingsRepository,
	tracker *UsageTracker,
	log *logger.Logger,
) *HealthChecker

NewHealthChecker creates a new health checker

func (*HealthChecker) CheckAllProxies

func (h *HealthChecker) CheckAllProxies(ctx context.Context) ([]models.ProxyTestResult, error)

CheckAllProxies tests all proxies concurrently with periodic (non-immediate) status accounting.

func (*HealthChecker) CheckAllProxiesWithProgress

func (h *HealthChecker) CheckAllProxiesWithProgress(
	ctx context.Context,
	onProgress func(checked, active, failed int),
	immediate bool,
) ([]models.ProxyTestResult, error)

CheckAllProxiesWithProgress tests all proxies concurrently, calling onProgress(checked, active, failed) after each proxy is checked (nil-safe). immediate=true applies each result to the proxy status right away (user-initiated); false keeps the consecutive-failure accounting.

func (*HealthChecker) CheckProxy

func (h *HealthChecker) CheckProxy(ctx context.Context, proxy *models.Proxy, immediate bool) (*models.ProxyTestResult, error)

CheckProxy tests a single proxy. When immediate is true (user-initiated manual test) the result is applied to the proxy status right away; when false (periodic/background) the consecutive-failure accounting applies.

func (*HealthChecker) StartPeriodicHealthCheck

func (h *HealthChecker) StartPeriodicHealthCheck(ctx context.Context, interval time.Duration)

StartPeriodicHealthCheck starts a background health check routine

type LeastConnectionsSelector

type LeastConnectionsSelector struct {
	*BaseSelector
	// contains filtered or unexported fields
}

LeastConnectionsSelector selects the proxy with the lowest usage count

func NewLeastConnectionsSelector

func NewLeastConnectionsSelector(repo *repository.ProxyRepository, settings *models.RotationSettings) *LeastConnectionsSelector

NewLeastConnectionsSelector creates a new least connections selector

func (*LeastConnectionsSelector) Refresh

Refresh reloads the proxy list from database

func (*LeastConnectionsSelector) Select

Select returns the proxy with the lowest live in-memory usage count

type PoolChain

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

PoolChain holds an ordered list of pool selectors for a user: index 0 = main pool, index 1..N = fallback pools. It refreshes pool selectors periodically and provides the high-level SendWithRetry / ConnectWithRetry methods used by the proxy handler.

func NewPoolChain

func NewPoolChain(db *database.DB, pools []models.ProxyPool, maxRetry int, log *logger.Logger) *PoolChain

NewPoolChain builds a PoolChain from an ordered list of ProxyPool objects.

func (*PoolChain) ConnectWithRetry

func (c *PoolChain) ConnectWithRetry(
	host string,
	ctx context.Context,
	rotationSettings *models.RotationSettings,
	log *logger.Logger,
) (net.Conn, int, error)

ConnectWithRetry establishes a TCP tunnel (HTTPS CONNECT) through the chain.

func (*PoolChain) Refresh

func (c *PoolChain) Refresh(ctx context.Context)

Refresh reloads all pool selectors (non-blocking goroutine).

func (*PoolChain) SendWithRetry

func (c *PoolChain) SendWithRetry(
	req *http.Request,
	ctx context.Context,
	rotationSettings *models.RotationSettings,
	log *logger.Logger,
) (*http.Response, int, error)

SendWithRetry attempts to forward an HTTP request through the chain. On each attempt it picks the next fresh proxy. If a pool has no active proxies it moves to the next pool automatically.

type PoolSelector

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

PoolSelector selects a proxy from a specific pool using the pool's rotation strategy. It keeps in-memory state (round-robin index, stick counters) per pool instance.

func NewPoolSelector

func NewPoolSelector(db *database.DB, pool models.ProxyPool) *PoolSelector

NewPoolSelector creates a PoolSelector for the given pool.

func (*PoolSelector) HasActive

func (ps *PoolSelector) HasActive() bool

HasActive returns true if the pool currently has at least one active/idle proxy.

func (*PoolSelector) Refresh

func (ps *PoolSelector) Refresh(ctx context.Context) error

Refresh reloads only active/idle proxies that belong to this pool.

func (*PoolSelector) RemoveProxy

func (ps *PoolSelector) RemoveProxy(proxyID int)

RemoveProxy removes a specific proxy from the in-memory list (called after failure).

func (*PoolSelector) Select

func (ps *PoolSelector) Select(_ context.Context) (*models.Proxy, error)

Select picks the next proxy according to the pool's rotation method.

type ProxySelector

type ProxySelector interface {
	Select(ctx context.Context) (*models.Proxy, error)
	Refresh(ctx context.Context) error
}

ProxySelector defines the interface for proxy selection strategies

func NewProxySelector

func NewProxySelector(repo *repository.ProxyRepository, settings *models.RotationSettings) (ProxySelector, error)

NewProxySelector creates a proxy selector based on settings

type RandomSelector

type RandomSelector struct {
	*BaseSelector
}

RandomSelector selects a random proxy

func NewRandomSelector

func NewRandomSelector(repo *repository.ProxyRepository, settings *models.RotationSettings) *RandomSelector

NewRandomSelector creates a new random selector

func (*RandomSelector) Refresh

func (s *RandomSelector) Refresh(ctx context.Context) error

Refresh reloads the proxy list from database

func (*RandomSelector) Select

func (s *RandomSelector) Select(ctx context.Context) (*models.Proxy, error)

Select returns a random proxy from the available pool

type RateLimitMiddleware

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

RateLimitMiddleware handles per-IP rate limiting

func NewRateLimitMiddleware

func NewRateLimitMiddleware(settings models.RateLimitSettings) *RateLimitMiddleware

NewRateLimitMiddleware creates a new rate limiting middleware

func (*RateLimitMiddleware) CleanupLimiters

func (m *RateLimitMiddleware) CleanupLimiters()

CleanupLimiters removes limiters for IPs that haven't been seen recently Should be called periodically to prevent memory leaks

func (*RateLimitMiddleware) HandleConnect

func (m *RateLimitMiddleware) HandleConnect(req *http.Request) (*http.Request, *http.Response)

HandleConnect validates rate limits for HTTPS CONNECT requests

func (*RateLimitMiddleware) HandleRequest

func (m *RateLimitMiddleware) HandleRequest(req *http.Request) (*http.Request, *http.Response)

HandleRequest validates rate limits for HTTP requests

func (*RateLimitMiddleware) UpdateSettings

func (m *RateLimitMiddleware) UpdateSettings(settings models.RateLimitSettings)

UpdateSettings updates the rate limit settings

type RequestRecord

type RequestRecord struct {
	ProxyID      int
	ProxyAddress string
	RequestedURL string
	Method       string
	Success      bool
	ResponseTime int // milliseconds
	StatusCode   int
	ErrorMessage string
	Timestamp    time.Time
}

RequestRecord represents a single proxy request

type RoundRobinSelector

type RoundRobinSelector struct {
	*BaseSelector
	// contains filtered or unexported fields
}

RoundRobinSelector selects proxies in sequential order

func NewRoundRobinSelector

func NewRoundRobinSelector(repo *repository.ProxyRepository, settings *models.RotationSettings) *RoundRobinSelector

NewRoundRobinSelector creates a new round-robin selector

func (*RoundRobinSelector) Refresh

func (s *RoundRobinSelector) Refresh(ctx context.Context) error

Refresh reloads the proxy list from database

func (*RoundRobinSelector) Select

func (s *RoundRobinSelector) Select(ctx context.Context) (*models.Proxy, error)

Select returns the next proxy in round-robin fashion

type Server

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

Server represents the proxy server

func New

func New(
	port int,
	log *logger.Logger,
	db *database.DB,
	proxyRepo *repository.ProxyRepository,
	poolRepo *repository.PoolRepository,
	userRepo *repository.UserRepository,
	settingsRepo *repository.SettingsRepository,
) (*Server, error)

New creates a new proxy server instance

func (*Server) ReloadSettings

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

ReloadSettings reloads settings from database and updates components

func (*Server) Shutdown

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

Shutdown gracefully shuts down the proxy server

func (*Server) Start

func (s *Server) Start() error

Start starts the proxy server

type TimeBasedSelector

type TimeBasedSelector struct {
	*BaseSelector
	// contains filtered or unexported fields
}

TimeBasedSelector selects proxy based on time intervals

func NewTimeBasedSelector

func NewTimeBasedSelector(repo *repository.ProxyRepository, settings *models.RotationSettings, intervalSeconds int) *TimeBasedSelector

NewTimeBasedSelector creates a new time-based selector

func (*TimeBasedSelector) Refresh

func (s *TimeBasedSelector) Refresh(ctx context.Context) error

Refresh reloads the proxy list from database

func (*TimeBasedSelector) Select

func (s *TimeBasedSelector) Select(ctx context.Context) (*models.Proxy, error)

Select returns a proxy based on current time interval

type UpstreamProxyHandler

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

UpstreamProxyHandler handles requests with upstream proxy rotation

func NewUpstreamProxyHandler

func NewUpstreamProxyHandler(
	selector ProxySelector,
	tracker *UsageTracker,
	settings *models.RotationSettings,
	log *logger.Logger,
) *UpstreamProxyHandler

NewUpstreamProxyHandler creates a new upstream proxy handler

func (*UpstreamProxyHandler) HandleConnectRequest

func (h *UpstreamProxyHandler) HandleConnectRequest(w http.ResponseWriter, r *http.Request)

HandleConnectRequest handles HTTPS CONNECT requests. It hijacks the client connection, establishes an upstream tunnel, and copies data bidirectionally using splice(2) on Linux.

func (*UpstreamProxyHandler) HandleHTTPRequest

func (h *UpstreamProxyHandler) HandleHTTPRequest(w http.ResponseWriter, r *http.Request)

HandleHTTPRequest handles HTTP requests (non-CONNECT) with upstream proxy rotation. It writes the proxied response directly to w.

type UsageTracker

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

UsageTracker tracks proxy usage and updates statistics.

On the request hot path, per-request synchronous DB writes (one INSERT + one UPDATE each) are the dominant scaling cost. When a batch writer is started (StartBatchWriter), RecordRequest instead enqueues onto a buffered channel and a single worker coalesces records: bulk-inserting proxy_requests via CopyFrom and folding per-proxy stat deltas into one UPDATE per proxy per flush window. This turns O(requests) round-trips into O(distinct proxies / second).

func NewUsageTracker

func NewUsageTracker(repo *repository.ProxyRepository) *UsageTracker

NewUsageTracker creates a new usage tracker. It writes synchronously until StartBatchWriter is called.

func (*UsageTracker) GetRecentRequests

func (t *UsageTracker) GetRecentRequests(ctx context.Context, proxyID int, limit int) ([]RequestRecord, error)

GetRecentRequests retrieves recent requests for a proxy

func (*UsageTracker) RecordHealthCheck

func (t *UsageTracker) RecordHealthCheck(ctx context.Context, proxyID int, success bool, responseTime int, errorMsg string) error

RecordHealthCheck records a health check result

func (*UsageTracker) RecordManualTestResult

func (t *UsageTracker) RecordManualTestResult(ctx context.Context, proxyID int, success bool, errorMsg string) error

RecordManualTestResult persists the result of an explicit manual proxy test. Status is applied immediately: success -> 'active', failure -> 'failed'. The consecutive-failure counter is kept consistent: reset on success, incremented on failure.

func (*UsageTracker) RecordRequest

func (t *UsageTracker) RecordRequest(ctx context.Context, record RequestRecord) error

RecordRequest records a proxy request. With the batch writer running it is a non-blocking enqueue (dropping on overflow); otherwise it writes synchronously.

func (*UsageTracker) StartBatchWriter

func (t *UsageTracker) StartBatchWriter(log *logger.Logger)

StartBatchWriter enables asynchronous batched stat writes. Idempotent.

func (*UsageTracker) Stop

func (t *UsageTracker) Stop()

Stop drains and flushes any buffered records and stops the batch writer.

func (*UsageTracker) UpdateProxyStatus

func (t *UsageTracker) UpdateProxyStatus(ctx context.Context, proxyID int, status string) error

UpdateProxyStatus updates only the status of a proxy

type UserAuthMiddleware

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

UserAuthMiddleware resolves Proxy-Authorization credentials against proxy_users. When a matching enabled user is found it attaches a *PoolChain to the request context. If user-based auth is not configured (no proxy_users) and the legacy single-user auth is enabled, it falls through to the original AuthMiddleware behaviour.

func NewUserAuthMiddleware

func NewUserAuthMiddleware(
	userRepo *repository.UserRepository,
	poolRepo *repository.PoolRepository,
	db *database.DB,
	legacy *AuthMiddleware,
	rotSettings *models.RotationSettings,
	log *logger.Logger,
) *UserAuthMiddleware

NewUserAuthMiddleware creates the middleware.

func (*UserAuthMiddleware) HandleConnect

func (m *UserAuthMiddleware) HandleConnect(req *http.Request) (*http.Request, *http.Response)

HandleConnect is the same but for HTTPS CONNECT.

func (*UserAuthMiddleware) HandleRequest

func (m *UserAuthMiddleware) HandleRequest(req *http.Request) (*http.Request, *http.Response)

HandleRequest is called for every HTTP proxy request. It reads Proxy-Authorization, looks up the user, builds a PoolChain and stores it in the request context so the handler can use it.

func (*UserAuthMiddleware) InvalidateUser

func (m *UserAuthMiddleware) InvalidateUser(username string)

InvalidateUser removes a user's cached chain (call after user is updated/deleted).

Jump to

Keyboard shortcuts

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