smartproxy/readme.hints.md

6.5 KiB
Raw Blame History

SmartProxy Project Hints

Project Overview

  • Package: @push.rocks/smartproxy high-performance proxy supporting HTTP(S), TCP, WebSocket, and ACME integration.
  • Written in TypeScript, compiled output in dist_ts/, uses ESM with NodeNext resolution.

Important: ACME Configuration in v19.0.0

  • Breaking Change: ACME configuration must be placed within individual route TLS settings, not at the top level
  • Route-level ACME config is the ONLY way to enable SmartAcme initialization
  • SmartCertManager requires email in route config for certificate acquisition
  • Top-level ACME configuration is ignored in v19.0.0

Repository Structure

  • ts/ TypeScript source files:
    • index.ts exports main modules.
    • plugins.ts centralizes native and third-party imports.
    • Subdirectories: networkproxy/, nftablesproxy/, port80handler/, redirect/, smartproxy/.
    • Key classes: ProxyRouter (classes.router.ts), SmartProxy (classes.smartproxy.ts), plus handlers/managers.
  • dist_ts/ transpiled .js and .d.ts files mirroring ts/ structure.
  • test/ test suites in TypeScript:
    • test.router.ts routing logic (hostname matching, wildcards, path parameters, config management).
    • test.smartproxy.ts proxy behavior tests (TCP forwarding, SNI handling, concurrency, chaining, timeouts).
    • test/helpers/ utilities (e.g., certificates).
  • assets/certs/ placeholder certificates for ACME and TLS.

Development Setup

  • Requires pnpm (v10+).
  • Install dependencies: pnpm install.
  • Build: pnpm build (runs tsbuild --web --allowimplicitany).
  • Test: pnpm test (runs tstest test/).
  • Format: pnpm format (runs gitzone format).

Testing Framework

  • Uses @push.rocks/tapbundle (tap, expect, expactAsync).
  • Test files: must start with test. and use .ts extension.
  • Run specific tests via tsx, e.g., tsx test/test.router.ts.

Coding Conventions

  • Import modules via plugins.ts:
    import * as plugins from './plugins.ts';
    const server = new plugins.http.Server();
    
  • Reference plugins with full path: plugins.acme, plugins.smartdelay, plugins.minimatch, etc.
  • Path patterns support globs (*) and parameters (:param) in ProxyRouter.
  • Wildcard hostname matching leverages minimatch patterns.

Key Components

  • ProxyRouter
    • Methods: routeReq, routeReqWithDetails.
    • Hostname matching: case-insensitive, strips port, supports exact, wildcard, TLD, complex patterns.
    • Path routing: exact, wildcard, parameter extraction (pathParams), returns pathMatch and pathRemainder.
    • Config API: setNewProxyConfigs, addProxyConfig, removeProxyConfig, getHostnames, getProxyConfigs.
  • SmartProxy
    • Manages one or more net.Server instances to forward TCP streams.
    • Options: preserveSourceIP, defaultAllowedIPs, globalPortRanges, sniEnabled.
    • DomainConfigManager: round-robin selection for multiple target IPs.
    • Graceful shutdown in stop(), ensures no lingering servers or sockets.

Notable Points

  • TSConfig: module: NodeNext, verbatimModuleSyntax, allows .js extension imports in TS.
  • Mermaid diagrams and architecture flows in readme.md illustrate component interactions and protocol flows.
  • CLI entrypoint (cli.js) supports command-line usage (ACME, proxy controls).
  • ACME and certificate handling via Port80Handler and helpers.certificates.ts.

ACME/Certificate Configuration Example (v19.0.0)

const proxy = new SmartProxy({
  routes: [{
    name: 'example.com',
    match: { domains: 'example.com', ports: 443 },
    action: {
      type: 'forward',
      target: { host: 'localhost', port: 8080 },
      tls: {
        mode: 'terminate',
        certificate: 'auto',
        acme: {  // ACME config MUST be here, not at top level
          email: 'ssl@example.com',
          useProduction: false,
          challengePort: 80
        }
      }
    }
  }]
});

TODOs / Considerations

  • Ensure import extensions in source match build outputs (.ts vs .js).
  • Update plugins.ts when adding new dependencies.
  • Maintain test coverage for new routing or proxy features.
  • Keep ts/ and dist_ts/ in sync after refactors.
  • Consider implementing top-level ACME config support for backward compatibility

HTTP-01 ACME Challenge Fix (v19.3.8)

Issue

Non-TLS connections on ports configured in useHttpProxy were not being forwarded to HttpProxy. This caused ACME HTTP-01 challenges to fail when the ACME port (usually 80) was included in useHttpProxy.

Root Cause

In the RouteConnectionHandler.handleForwardAction method, only connections with TLS settings (mode: 'terminate' or 'terminate-and-reencrypt') were being forwarded to HttpProxy. Non-TLS connections were always handled as direct connections, even when the port was configured for HttpProxy.

Solution

Added a check for non-TLS connections on ports listed in useHttpProxy:

// No TLS settings - check if this port should use HttpProxy
const isHttpProxyPort = this.settings.useHttpProxy?.includes(record.localPort);

if (isHttpProxyPort && this.httpProxyBridge.getHttpProxy()) {
  // Forward non-TLS connections to HttpProxy if configured
  this.httpProxyBridge.forwardToHttpProxy(/*...*/);
  return;
}

Test Coverage

  • test/test.http-fix-unit.ts - Unit tests verifying the fix
  • Tests confirm that non-TLS connections on HttpProxy ports are properly forwarded
  • Tests verify that non-HttpProxy ports still use direct connections

Configuration Example

const proxy = new SmartProxy({
  useHttpProxy: [80], // Enable HttpProxy for port 80
  httpProxyPort: 8443,
  acme: {
    email: 'ssl@example.com',
    port: 80
  },
  routes: [
    // Your routes here
  ]
});

ACME Certificate Provisioning Timing Fix (v19.3.9)

Issue

Certificate provisioning would start before ports were listening, causing ACME HTTP-01 challenges to fail with connection refused errors.

Root Cause

SmartProxy initialization sequence:

  1. Certificate manager initialized → immediately starts provisioning
  2. Ports start listening (too late for ACME challenges)

Solution

Deferred certificate provisioning until after ports are ready:

// SmartCertManager.initialize() now skips automatic provisioning
// SmartProxy.start() calls provisionAllCertificates() directly after ports are listening

Test Coverage

  • test/test.acme-timing-simple.ts - Verifies proper timing sequence

Migration

Update to v19.3.9+, no configuration changes needed.