10 KiB
SmartProxy Port Handling
This document covers all the port handling capabilities in SmartProxy, including port range specification, dynamic port mapping, and runtime port management.
Port Range Syntax
SmartProxy offers flexible port range specification through the TPortRange
type, which can be defined in three different ways:
1. Single Port
// Match a single port
{
match: {
ports: 443
}
}
2. Array of Specific Ports
// Match multiple specific ports
{
match: {
ports: [80, 443, 8080]
}
}
3. Port Range
// Match a range of ports
{
match: {
ports: [{ from: 8000, to: 8100 }]
}
}
4. Mixed Port Specifications
You can combine different port specification methods in a single rule:
// Match both specific ports and port ranges
{
match: {
ports: [80, 443, { from: 8000, to: 8100 }]
}
}
Port Forwarding Options
SmartProxy offers several ways to handle port forwarding from source to target:
1. Static Port Forwarding
Forward to a fixed target port:
{
action: {
type: 'forward',
target: {
host: 'backend.example.com',
port: 8080
}
}
}
2. Preserve Source Port
Forward to the same port on the target:
{
action: {
type: 'forward',
target: {
host: 'backend.example.com',
port: 'preserve'
}
}
}
3. Dynamic Port Mapping
Use a function to determine the target port based on connection context:
{
action: {
type: 'forward',
target: {
host: 'backend.example.com',
port: (context) => {
// Calculate port based on request details
return 8000 + (context.port % 100);
}
}
}
}
Port Selection Context
When using dynamic port mapping functions, you have access to a rich context object that provides details about the connection:
interface IRouteContext {
// Connection information
port: number; // The matched incoming port
domain?: string; // The domain from SNI or Host header
clientIp: string; // The client's IP address
serverIp: string; // The server's IP address
path?: string; // URL path (for HTTP connections)
query?: string; // Query string (for HTTP connections)
headers?: Record<string, string>; // HTTP headers (for HTTP connections)
// TLS information
isTls: boolean; // Whether the connection is TLS
tlsVersion?: string; // TLS version if applicable
// Route information
routeName?: string; // The name of the matched route
routeId?: string; // The ID of the matched route
// Additional properties
timestamp: number; // The request timestamp
connectionId: string; // Unique connection identifier
}
Common Port Mapping Patterns
1. Port Offset Mapping
Forward traffic to target ports with a fixed offset:
{
action: {
type: 'forward',
target: {
host: 'backend.example.com',
port: (context) => context.port + 1000
}
}
}
2. Domain-Based Port Mapping
Forward to different backend ports based on the domain:
{
action: {
type: 'forward',
target: {
host: 'backend.example.com',
port: (context) => {
switch (context.domain) {
case 'api.example.com': return 8001;
case 'admin.example.com': return 8002;
case 'staging.example.com': return 8003;
default: return 8000;
}
}
}
}
}
3. Load Balancing with Hash-Based Distribution
Distribute connections across a port range using a deterministic hash function:
{
action: {
type: 'forward',
target: {
host: 'backend.example.com',
port: (context) => {
// Simple hash function to ensure consistent mapping
const hostname = context.domain || '';
const hash = hostname.split('').reduce((a, b) => a + b.charCodeAt(0), 0);
return 8000 + (hash % 10); // Map to ports 8000-8009
}
}
}
}
IPv6-Mapped IPv4 Compatibility
SmartProxy automatically handles IPv6-mapped IPv4 addresses for optimal compatibility. When a connection from an IPv4 address (e.g., 192.168.1.1
) arrives as an IPv6-mapped address (::ffff:192.168.1.1
), the system normalizes these addresses for consistent matching.
This is particularly important when:
- Matching client IP restrictions in route configurations
- Preserving source IP for outgoing connections
- Tracking connections and rate limits
No special configuration is needed - the system handles this normalization automatically.
Dynamic Port Management
SmartProxy allows for runtime port configuration changes without requiring a restart.
Adding and Removing Ports
// Get the SmartProxy instance
const proxy = new SmartProxy({ /* config */ });
// Add a new listening port
await proxy.addListeningPort(8081);
// Remove a listening port
await proxy.removeListeningPort(8082);
Runtime Route Updates
// Get current routes
const currentRoutes = proxy.getRoutes();
// Add new route for the new port
const newRoute = {
name: 'New Dynamic Route',
match: {
ports: 8081,
domains: ['dynamic.example.com']
},
action: {
type: 'forward',
target: {
host: 'backend.example.com',
port: 9000
}
}
};
// Update the route configuration
await proxy.updateRoutes([...currentRoutes, newRoute]);
// Remove routes for a specific port
const routesWithout8082 = currentRoutes.filter(route => {
const ports = proxy.routeManager.expandPortRange(route.match.ports);
return !ports.includes(8082);
});
await proxy.updateRoutes(routesWithout8082);
Performance Considerations
Port Range Expansion
When using large port ranges, SmartProxy uses internal caching to optimize performance. For example, a range like { from: 1000, to: 2000 }
is expanded only once and then cached for future use.
Port Range Validation
The system automatically validates port ranges to ensure:
- Port numbers are within the valid range (1-65535)
- The "from" value is not greater than the "to" value in range specifications
- Port ranges do not contain duplicate entries
Invalid port ranges will be logged as warnings and skipped during configuration.
Configuration Recipes
Global Port Range
Listen on a large range of ports and forward to the same ports on a backend:
{
name: 'Global port range forwarding',
match: {
ports: [{ from: 8000, to: 9000 }]
},
action: {
type: 'forward',
target: {
host: 'backend.example.com',
port: 'preserve'
}
}
}
Domain-Specific Port Ranges
Different port ranges for different domain groups:
[
{
name: 'API port range',
match: {
ports: [{ from: 8000, to: 8099 }]
},
action: {
type: 'forward',
target: {
host: 'api.backend.example.com',
port: 'preserve'
}
}
},
{
name: 'Admin port range',
match: {
ports: [{ from: 9000, to: 9099 }]
},
action: {
type: 'forward',
target: {
host: 'admin.backend.example.com',
port: 'preserve'
}
}
}
]
Mixed Internal/External Port Forwarding
Forward specific high-numbered ports to standard ports on internal servers:
[
{
name: 'Web server forwarding',
match: {
ports: [8080, 8443]
},
action: {
type: 'forward',
target: {
host: 'web.internal',
port: (context) => context.port === 8080 ? 80 : 443
}
}
},
{
name: 'Database forwarding',
match: {
ports: [15432]
},
action: {
type: 'forward',
target: {
host: 'db.internal',
port: 5432
}
}
}
]
Debugging Port Configurations
When troubleshooting port forwarding issues, enable detailed logging:
const proxy = new SmartProxy({
routes: [ /* your routes */ ],
enableDetailedLogging: true
});
This will log:
- Port configuration during startup
- Port matching decisions during routing
- Dynamic port function results
- Connection details including source and target ports
Port Security Considerations
Restricting Ports
For security, you may want to restrict which ports can be accessed by specific clients:
{
name: 'Restricted port range',
match: {
ports: [{ from: 8000, to: 9000 }],
clientIp: ['10.0.0.0/8'] // Only internal network can access these ports
},
action: {
type: 'forward',
target: {
host: 'internal.example.com',
port: 'preserve'
}
}
}
Rate Limiting by Port
Apply different rate limits for different port ranges:
{
name: 'API ports with rate limiting',
match: {
ports: [{ from: 8000, to: 8100 }]
},
action: {
type: 'forward',
target: {
host: 'api.example.com',
port: 'preserve'
},
security: {
rateLimit: {
enabled: true,
maxRequests: 100,
window: 60 // 60 seconds
}
}
}
}
Best Practices
-
Use Specific Port Ranges: Instead of large ranges (e.g., 1-65535), use specific ranges for specific purposes
-
Prioritize Routes: When multiple routes could match, use the
priority
field to ensure the most specific route is matched first -
Name Your Routes: Use descriptive names to make debugging easier, especially when using port ranges
-
Use Preserve Port Where Possible: Using
port: 'preserve'
is more efficient and easier to maintain than creating multiple specific mappings -
Limit Dynamic Port Functions: While powerful, complex port functions can be harder to debug; prefer simple map or math-based functions
-
Use Port Variables: For complex setups, define your port ranges as variables for easier maintenance:
const API_PORTS = [{ from: 8000, to: 8099 }];
const ADMIN_PORTS = [{ from: 9000, to: 9099 }];
const routes = [
{
name: 'API Routes',
match: { ports: API_PORTS, /* ... */ },
// ...
},
{
name: 'Admin Routes',
match: { ports: ADMIN_PORTS, /* ... */ },
// ...
}
];