Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
e69c55de3b | |||
9a9bcd2df0 | |||
b27cb8988c | |||
0de7531e17 | |||
c0002fee38 | |||
27f9b1eac1 |
19
changelog.md
19
changelog.md
@ -1,5 +1,24 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-04-05 - 7.0.1 - fix(package.json)
|
||||||
|
Update packageManager field in package.json to specify the pnpm version for improved reproducibility.
|
||||||
|
|
||||||
|
- Added the packageManager field to clearly specify the pnpm version and its checksum.
|
||||||
|
|
||||||
|
## 2025-04-04 - 7.0.0 - BREAKING CHANGE(redirect)
|
||||||
|
Remove deprecated SSL redirect implementation and update exports to use the new redirect module
|
||||||
|
|
||||||
|
- Deleted ts/classes.sslredirect.ts which contained the old SSL redirect logic
|
||||||
|
- Updated ts/index.ts to export 'redirect/classes.redirect.js' instead of the removed SSL redirect module
|
||||||
|
- Adopted a new redirect implementation that provides enhanced features and a more consistent API
|
||||||
|
|
||||||
|
## 2025-03-25 - 6.0.1 - fix(readme)
|
||||||
|
Update README documentation: replace all outdated 'PortProxy' references with 'SmartProxy', adjust architecture diagrams, code examples, and configuration details (including correcting IPTables to NfTables) to reflect the new naming.
|
||||||
|
|
||||||
|
- Renamed 'PortProxy' to 'SmartProxy' in diagrams, flow sequences, and descriptive text
|
||||||
|
- Updated code examples and installation instructions accordingly
|
||||||
|
- Corrected references from IPTables to NfTables for modern system support
|
||||||
|
|
||||||
## 2025-03-18 - 5.1.0 - feat(docs)
|
## 2025-03-18 - 5.1.0 - feat(docs)
|
||||||
docs: replace IPTablesProxy references with NfTablesProxy in README and examples, updating configuration options and diagrams for advanced nftables features
|
docs: replace IPTablesProxy references with NfTablesProxy in README and examples, updating configuration options and diagrams for advanced nftables features
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@push.rocks/smartproxy",
|
"name": "@push.rocks/smartproxy",
|
||||||
"version": "6.0.0",
|
"version": "7.0.1",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.",
|
"description": "A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.",
|
||||||
"main": "dist_ts/index.js",
|
"main": "dist_ts/index.js",
|
||||||
@ -83,5 +83,6 @@
|
|||||||
"mongodb-memory-server",
|
"mongodb-memory-server",
|
||||||
"puppeteer"
|
"puppeteer"
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"packageManager": "pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6"
|
||||||
}
|
}
|
||||||
|
132
readme.md
132
readme.md
@ -15,7 +15,7 @@ flowchart TB
|
|||||||
direction TB
|
direction TB
|
||||||
HTTP80[HTTP Port 80\nSslRedirect]
|
HTTP80[HTTP Port 80\nSslRedirect]
|
||||||
HTTPS443[HTTPS Port 443\nNetworkProxy]
|
HTTPS443[HTTPS Port 443\nNetworkProxy]
|
||||||
PortProxy[TCP Port Proxy\nwith SNI routing]
|
SmartProxy[SmartProxy\nwith SNI routing]
|
||||||
NfTables[NfTablesProxy]
|
NfTables[NfTablesProxy]
|
||||||
Router[ProxyRouter]
|
Router[ProxyRouter]
|
||||||
ACME[Port80Handler\nACME/Let's Encrypt]
|
ACME[Port80Handler\nACME/Let's Encrypt]
|
||||||
@ -31,16 +31,16 @@ flowchart TB
|
|||||||
Client -->|HTTP Request| HTTP80
|
Client -->|HTTP Request| HTTP80
|
||||||
HTTP80 -->|Redirect| Client
|
HTTP80 -->|Redirect| Client
|
||||||
Client -->|HTTPS Request| HTTPS443
|
Client -->|HTTPS Request| HTTPS443
|
||||||
Client -->|TLS/TCP| PortProxy
|
Client -->|TLS/TCP| SmartProxy
|
||||||
|
|
||||||
HTTPS443 -->|Route Request| Router
|
HTTPS443 -->|Route Request| Router
|
||||||
Router -->|Proxy Request| Service1
|
Router -->|Proxy Request| Service1
|
||||||
Router -->|Proxy Request| Service2
|
Router -->|Proxy Request| Service2
|
||||||
|
|
||||||
PortProxy -->|Direct TCP| Service2
|
SmartProxy -->|Direct TCP| Service2
|
||||||
PortProxy -->|Direct TCP| Service3
|
SmartProxy -->|Direct TCP| Service3
|
||||||
|
|
||||||
NfTables -.->|Low-level forwarding| PortProxy
|
NfTables -.->|Low-level forwarding| SmartProxy
|
||||||
|
|
||||||
HTTP80 -.->|Challenge Response| ACME
|
HTTP80 -.->|Challenge Response| ACME
|
||||||
ACME -.->|Generate/Manage| Certs
|
ACME -.->|Generate/Manage| Certs
|
||||||
@ -51,7 +51,7 @@ flowchart TB
|
|||||||
classDef client fill:#dfd,stroke:#333,stroke-width:2px;
|
classDef client fill:#dfd,stroke:#333,stroke-width:2px;
|
||||||
|
|
||||||
class Client client;
|
class Client client;
|
||||||
class HTTP80,HTTPS443,PortProxy,IPTables,Router,ACME component;
|
class HTTP80,HTTPS443,SmartProxy,NfTables,Router,ACME component;
|
||||||
class Service1,Service2,Service3 backend;
|
class Service1,Service2,Service3 backend;
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -98,49 +98,49 @@ sequenceDiagram
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
### Port Proxy with SNI-based Routing
|
### SNI-based Connection Handling
|
||||||
This diagram illustrates how TCP connections with SNI (Server Name Indication) are processed and forwarded:
|
This diagram illustrates how TCP connections with SNI (Server Name Indication) are processed and forwarded:
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
participant Client
|
participant Client
|
||||||
participant PortProxy
|
participant SmartProxy
|
||||||
participant Backend
|
participant Backend
|
||||||
|
|
||||||
Client->>PortProxy: TLS Connection
|
Client->>SmartProxy: TLS Connection
|
||||||
|
|
||||||
alt SNI Enabled
|
alt SNI Enabled
|
||||||
PortProxy->>Client: Accept Connection
|
SmartProxy->>Client: Accept Connection
|
||||||
Client->>PortProxy: TLS ClientHello with SNI
|
Client->>SmartProxy: TLS ClientHello with SNI
|
||||||
PortProxy->>PortProxy: Extract SNI Hostname
|
SmartProxy->>SmartProxy: Extract SNI Hostname
|
||||||
PortProxy->>PortProxy: Match Domain Config
|
SmartProxy->>SmartProxy: Match Domain Config
|
||||||
PortProxy->>PortProxy: Validate Client IP
|
SmartProxy->>SmartProxy: Validate Client IP
|
||||||
|
|
||||||
alt IP Allowed
|
alt IP Allowed
|
||||||
PortProxy->>Backend: Forward Connection
|
SmartProxy->>Backend: Forward Connection
|
||||||
Note over PortProxy,Backend: Bidirectional Data Flow
|
Note over SmartProxy,Backend: Bidirectional Data Flow
|
||||||
else IP Rejected
|
else IP Rejected
|
||||||
PortProxy->>Client: Close Connection
|
SmartProxy->>Client: Close Connection
|
||||||
end
|
end
|
||||||
else Port-based Routing
|
else Port-based Routing
|
||||||
PortProxy->>PortProxy: Match Port Range
|
SmartProxy->>SmartProxy: Match Port Range
|
||||||
PortProxy->>PortProxy: Find Domain Config
|
SmartProxy->>SmartProxy: Find Domain Config
|
||||||
PortProxy->>PortProxy: Validate Client IP
|
SmartProxy->>SmartProxy: Validate Client IP
|
||||||
|
|
||||||
alt IP Allowed
|
alt IP Allowed
|
||||||
PortProxy->>Backend: Forward Connection
|
SmartProxy->>Backend: Forward Connection
|
||||||
Note over PortProxy,Backend: Bidirectional Data Flow
|
Note over SmartProxy,Backend: Bidirectional Data Flow
|
||||||
else IP Rejected
|
else IP Rejected
|
||||||
PortProxy->>Client: Close Connection
|
SmartProxy->>Client: Close Connection
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
loop Connection Active
|
loop Connection Active
|
||||||
PortProxy-->>PortProxy: Monitor Activity
|
SmartProxy-->>SmartProxy: Monitor Activity
|
||||||
PortProxy-->>PortProxy: Check Max Lifetime
|
SmartProxy-->>SmartProxy: Check Max Lifetime
|
||||||
alt Inactivity or Max Lifetime Exceeded
|
alt Inactivity or Max Lifetime Exceeded
|
||||||
PortProxy->>Client: Close Connection
|
SmartProxy->>Client: Close Connection
|
||||||
PortProxy->>Backend: Close Connection
|
SmartProxy->>Backend: Close Connection
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
@ -192,7 +192,7 @@ sequenceDiagram
|
|||||||
|
|
||||||
- **HTTPS Reverse Proxy** - Route traffic to backend services based on hostname with TLS termination
|
- **HTTPS Reverse Proxy** - Route traffic to backend services based on hostname with TLS termination
|
||||||
- **WebSocket Support** - Full WebSocket proxying with heartbeat monitoring
|
- **WebSocket Support** - Full WebSocket proxying with heartbeat monitoring
|
||||||
- **TCP Port Forwarding** - Advanced port forwarding with SNI inspection and domain-based routing
|
- **TCP Connection Handling** - Advanced connection handling with SNI inspection and domain-based routing
|
||||||
- **Enhanced TLS Handling** - Robust TLS handshake processing with improved certificate error handling
|
- **Enhanced TLS Handling** - Robust TLS handshake processing with improved certificate error handling
|
||||||
- **HTTP to HTTPS Redirection** - Automatically redirect HTTP requests to HTTPS
|
- **HTTP to HTTPS Redirection** - Automatically redirect HTTP requests to HTTPS
|
||||||
- **Let's Encrypt Integration** - Automatic certificate management using ACME protocol
|
- **Let's Encrypt Integration** - Automatic certificate management using ACME protocol
|
||||||
@ -224,15 +224,16 @@ const proxy = new NetworkProxy({
|
|||||||
const proxyConfigs = [
|
const proxyConfigs = [
|
||||||
{
|
{
|
||||||
hostName: 'example.com',
|
hostName: 'example.com',
|
||||||
destinationIp: '127.0.0.1',
|
destinationIps: ['127.0.0.1'],
|
||||||
destinationPort: 3000,
|
destinationPorts: [3000],
|
||||||
publicKey: 'your-cert-content',
|
publicKey: 'your-cert-content',
|
||||||
privateKey: 'your-key-content'
|
privateKey: 'your-key-content',
|
||||||
|
rewriteHostHeader: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
hostName: 'api.example.com',
|
hostName: 'api.example.com',
|
||||||
destinationIp: '127.0.0.1',
|
destinationIps: ['127.0.0.1'],
|
||||||
destinationPort: 4000,
|
destinationPorts: [4000],
|
||||||
publicKey: 'your-cert-content',
|
publicKey: 'your-cert-content',
|
||||||
privateKey: 'your-key-content',
|
privateKey: 'your-key-content',
|
||||||
// Optional basic auth
|
// Optional basic auth
|
||||||
@ -266,13 +267,13 @@ const redirector = new SslRedirect(80);
|
|||||||
redirector.start();
|
redirector.start();
|
||||||
```
|
```
|
||||||
|
|
||||||
### TCP Port Forwarding with Domain-based Routing
|
### TCP Connection Handling with Domain-based Routing
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { PortProxy } from '@push.rocks/smartproxy';
|
import { SmartProxy } from '@push.rocks/smartproxy';
|
||||||
|
|
||||||
// Configure port proxy with domain-based routing
|
// Configure SmartProxy with domain-based routing
|
||||||
const portProxy = new PortProxy({
|
const smartProxy = new SmartProxy({
|
||||||
fromPort: 443,
|
fromPort: 443,
|
||||||
toPort: 8443,
|
toPort: 8443,
|
||||||
targetIP: 'localhost', // Default target host
|
targetIP: 'localhost', // Default target host
|
||||||
@ -312,7 +313,7 @@ const portProxy = new PortProxy({
|
|||||||
preserveSourceIP: true
|
preserveSourceIP: true
|
||||||
});
|
});
|
||||||
|
|
||||||
portProxy.start();
|
smartProxy.start();
|
||||||
```
|
```
|
||||||
|
|
||||||
### NfTables Port Forwarding
|
### NfTables Port Forwarding
|
||||||
@ -376,7 +377,13 @@ await basicProxy.start();
|
|||||||
import { Port80Handler } from '@push.rocks/smartproxy';
|
import { Port80Handler } from '@push.rocks/smartproxy';
|
||||||
|
|
||||||
// Create an ACME handler for Let's Encrypt
|
// Create an ACME handler for Let's Encrypt
|
||||||
const acmeHandler = new Port80Handler();
|
const acmeHandler = new Port80Handler({
|
||||||
|
port: 80,
|
||||||
|
contactEmail: 'admin@example.com',
|
||||||
|
useProduction: true, // Use Let's Encrypt production servers (default is staging)
|
||||||
|
renewThresholdDays: 30, // Renew certificates 30 days before expiry
|
||||||
|
httpsRedirectPort: 443 // Redirect HTTP to HTTPS on this port
|
||||||
|
});
|
||||||
|
|
||||||
// Add domains to manage certificates for
|
// Add domains to manage certificates for
|
||||||
acmeHandler.addDomain({
|
acmeHandler.addDomain({
|
||||||
@ -407,8 +414,14 @@ acmeHandler.addDomain({
|
|||||||
| Option | Description | Default |
|
| Option | Description | Default |
|
||||||
|----------------|---------------------------------------------------|---------|
|
|----------------|---------------------------------------------------|---------|
|
||||||
| `port` | Port to listen on for HTTPS connections | - |
|
| `port` | Port to listen on for HTTPS connections | - |
|
||||||
|
| `maxConnections` | Maximum concurrent connections | 10000 |
|
||||||
|
| `keepAliveTimeout` | Keep-alive timeout in milliseconds | 60000 |
|
||||||
|
| `headersTimeout` | Headers timeout in milliseconds | 60000 |
|
||||||
|
| `logLevel` | Logging level ('error', 'warn', 'info', 'debug') | 'info' |
|
||||||
|
| `cors` | CORS configuration object | - |
|
||||||
|
| `rewriteHostHeader` | Whether to rewrite the Host header | false |
|
||||||
|
|
||||||
### PortProxy Settings
|
### SmartProxy Settings
|
||||||
|
|
||||||
| Option | Description | Default |
|
| Option | Description | Default |
|
||||||
|---------------------------|--------------------------------------------------------|-------------|
|
|---------------------------|--------------------------------------------------------|-------------|
|
||||||
@ -460,28 +473,11 @@ acmeHandler.addDomain({
|
|||||||
| `qos` | Quality of Service options (object) | - |
|
| `qos` | Quality of Service options (object) | - |
|
||||||
| `netProxyIntegration` | NetworkProxy integration options (object) | - |
|
| `netProxyIntegration` | NetworkProxy integration options (object) | - |
|
||||||
|
|
||||||
#### NfTablesProxy QoS Options
|
|
||||||
|
|
||||||
| Option | Description | Default |
|
|
||||||
|----------------------|---------------------------------------------------|---------|
|
|
||||||
| `enabled` | Enable Quality of Service features | false |
|
|
||||||
| `maxRate` | Maximum bandwidth rate (e.g. "10mbps") | - |
|
|
||||||
| `priority` | Traffic priority (1-10, 1 is highest) | - |
|
|
||||||
| `markConnections` | Mark connections for easier management | false |
|
|
||||||
|
|
||||||
#### NfTablesProxy NetworkProxy Integration Options
|
|
||||||
|
|
||||||
| Option | Description | Default |
|
|
||||||
|----------------------|---------------------------------------------------|---------|
|
|
||||||
| `enabled` | Enable NetworkProxy integration | false |
|
|
||||||
| `redirectLocalhost` | Redirect localhost traffic to NetworkProxy | false |
|
|
||||||
| `sslTerminationPort` | Port where NetworkProxy handles SSL termination | - |
|
|
||||||
|
|
||||||
## Advanced Features
|
## Advanced Features
|
||||||
|
|
||||||
### TLS Handshake Optimization
|
### TLS Handshake Optimization
|
||||||
|
|
||||||
The enhanced `PortProxy` implementation includes significant improvements for TLS handshake handling:
|
The enhanced `SmartProxy` implementation includes significant improvements for TLS handshake handling:
|
||||||
|
|
||||||
- Robust SNI extraction with improved error handling
|
- Robust SNI extraction with improved error handling
|
||||||
- Increased buffer size for complex TLS handshakes (10MB)
|
- Increased buffer size for complex TLS handshakes (10MB)
|
||||||
@ -492,7 +488,7 @@ The enhanced `PortProxy` implementation includes significant improvements for TL
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Example configuration to solve Chrome certificate errors
|
// Example configuration to solve Chrome certificate errors
|
||||||
const portProxy = new PortProxy({
|
const portProxy = new SmartProxy({
|
||||||
// ... other settings
|
// ... other settings
|
||||||
initialDataTimeout: 60000, // Give browser more time for handshake
|
initialDataTimeout: 60000, // Give browser more time for handshake
|
||||||
maxPendingDataSize: 10 * 1024 * 1024, // Larger buffer for complex handshakes
|
maxPendingDataSize: 10 * 1024 * 1024, // Larger buffer for complex handshakes
|
||||||
@ -502,7 +498,7 @@ const portProxy = new PortProxy({
|
|||||||
|
|
||||||
### Connection Management and Monitoring
|
### Connection Management and Monitoring
|
||||||
|
|
||||||
The `PortProxy` class includes built-in connection tracking and monitoring:
|
The `SmartProxy` class includes built-in connection tracking and monitoring:
|
||||||
|
|
||||||
- Automatic cleanup of idle connections with configurable timeouts
|
- Automatic cleanup of idle connections with configurable timeouts
|
||||||
- Timeouts for connections that exceed maximum lifetime
|
- Timeouts for connections that exceed maximum lifetime
|
||||||
@ -521,7 +517,7 @@ The `NetworkProxy` class provides WebSocket support with:
|
|||||||
|
|
||||||
### SNI-based Routing
|
### SNI-based Routing
|
||||||
|
|
||||||
The `PortProxy` class can inspect the SNI (Server Name Indication) field in TLS handshakes to route connections based on the requested domain:
|
The `SmartProxy` class can inspect the SNI (Server Name Indication) field in TLS handshakes to route connections based on the requested domain:
|
||||||
|
|
||||||
- Multiple backend targets per domain
|
- Multiple backend targets per domain
|
||||||
- Round-robin load balancing
|
- Round-robin load balancing
|
||||||
@ -530,7 +526,7 @@ The `PortProxy` class can inspect the SNI (Server Name Indication) field in TLS
|
|||||||
|
|
||||||
### Enhanced NfTables Management
|
### Enhanced NfTables Management
|
||||||
|
|
||||||
The `NfTablesProxy` class offers advanced capabilities compared to the previous IPTablesProxy:
|
The `NfTablesProxy` class offers advanced capabilities:
|
||||||
|
|
||||||
- Support for multiple port ranges and individual ports
|
- Support for multiple port ranges and individual ports
|
||||||
- More efficient IP filtering using nftables sets
|
- More efficient IP filtering using nftables sets
|
||||||
@ -544,7 +540,7 @@ The `NfTablesProxy` class offers advanced capabilities compared to the previous
|
|||||||
|
|
||||||
### Port80Handler with Glob Pattern Support
|
### Port80Handler with Glob Pattern Support
|
||||||
|
|
||||||
The `Port80Handler` class now includes support for glob pattern domain matching:
|
The `Port80Handler` class includes support for glob pattern domain matching:
|
||||||
|
|
||||||
- Supports wildcard domains like `*.example.com` for HTTP request routing
|
- Supports wildcard domains like `*.example.com` for HTTP request routing
|
||||||
- Detects glob patterns and skips certificate issuance for them
|
- Detects glob patterns and skips certificate issuance for them
|
||||||
@ -566,7 +562,7 @@ If you experience certificate errors in browsers, especially in Chrome, try thes
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Configuration to fix Chrome certificate errors
|
// Configuration to fix Chrome certificate errors
|
||||||
const portProxy = new PortProxy({
|
const smartProxy = new SmartProxy({
|
||||||
// ... other settings
|
// ... other settings
|
||||||
initialDataTimeout: 60000,
|
initialDataTimeout: 60000,
|
||||||
maxPendingDataSize: 10 * 1024 * 1024,
|
maxPendingDataSize: 10 * 1024 * 1024,
|
||||||
@ -585,14 +581,14 @@ For improved connection stability in high-traffic environments:
|
|||||||
4. **Monitor Connection Statistics**: Enable detailed logging to track termination reasons
|
4. **Monitor Connection Statistics**: Enable detailed logging to track termination reasons
|
||||||
5. **Fine-tune Inactivity Checks**: Adjust `inactivityCheckInterval` based on your traffic patterns
|
5. **Fine-tune Inactivity Checks**: Adjust `inactivityCheckInterval` based on your traffic patterns
|
||||||
|
|
||||||
### IPTables Troubleshooting
|
### NfTables Troubleshooting
|
||||||
|
|
||||||
If you're experiencing issues with IPTablesProxy:
|
If you're experiencing issues with NfTablesProxy:
|
||||||
|
|
||||||
1. **Enable Detailed Logging**: Set `enableLogging: true` to see all rule operations
|
1. **Enable Detailed Logging**: Set `enableLogging: true` to see all rule operations
|
||||||
2. **Force Clean Slate**: Use `forceCleanSlate: true` to remove any lingering rules
|
2. **Force Clean Slate**: Use `forceCleanSlate: true` to remove any lingering rules
|
||||||
3. **Use Custom Chains**: Enable `addJumpRule: true` for cleaner rule management
|
3. **Use IP Sets**: Enable `useIPSets: true` for cleaner rule management
|
||||||
4. **Check Permissions**: Ensure your process has sufficient permissions to modify iptables
|
4. **Check Permissions**: Ensure your process has sufficient permissions to modify nftables
|
||||||
5. **Verify IPv6 Support**: If using `ipv6Support: true`, ensure ip6tables is available
|
5. **Verify IPv6 Support**: If using `ipv6Support: true`, ensure ip6tables is available
|
||||||
|
|
||||||
## License and Legal Information
|
## License and Legal Information
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@push.rocks/smartproxy',
|
name: '@push.rocks/smartproxy',
|
||||||
version: '5.1.0',
|
version: '7.0.1',
|
||||||
description: 'A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.'
|
description: 'A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.'
|
||||||
}
|
}
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
import * as plugins from './plugins.js';
|
|
||||||
|
|
||||||
export class SslRedirect {
|
|
||||||
httpServer: plugins.http.Server;
|
|
||||||
port: number;
|
|
||||||
constructor(portArg: number) {
|
|
||||||
this.port = portArg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async start() {
|
|
||||||
this.httpServer = plugins.http.createServer((request, response) => {
|
|
||||||
const requestUrl = new URL(request.url, `http://${request.headers.host}`);
|
|
||||||
const completeUrlWithoutProtocol = `${requestUrl.host}${requestUrl.pathname}${requestUrl.search}`;
|
|
||||||
const redirectUrl = `https://${completeUrlWithoutProtocol}`;
|
|
||||||
console.log(`Got http request for http://${completeUrlWithoutProtocol}`);
|
|
||||||
console.log(`Redirecting to ${redirectUrl}`);
|
|
||||||
response.writeHead(302, {
|
|
||||||
Location: redirectUrl,
|
|
||||||
});
|
|
||||||
response.end();
|
|
||||||
});
|
|
||||||
this.httpServer.listen(this.port);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async stop() {
|
|
||||||
const done = plugins.smartpromise.defer();
|
|
||||||
this.httpServer.close(() => {
|
|
||||||
done.resolve();
|
|
||||||
});
|
|
||||||
await done.promise;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
export * from './nfttablesproxy/classes.nftablesproxy.js';
|
export * from './nfttablesproxy/classes.nftablesproxy.js';
|
||||||
export * from './networkproxy/classes.np.networkproxy.js';
|
export * from './networkproxy/classes.np.networkproxy.js';
|
||||||
export * from './port80handler/classes.port80handler.js';
|
export * from './port80handler/classes.port80handler.js';
|
||||||
export * from './classes.sslredirect.js';
|
export * from './redirect/classes.redirect.js';
|
||||||
export * from './smartproxy/classes.smartproxy.js';
|
export * from './smartproxy/classes.smartproxy.js';
|
||||||
export * from './smartproxy/classes.pp.snihandler.js';
|
export * from './smartproxy/classes.pp.snihandler.js';
|
||||||
export * from './smartproxy/classes.pp.interfaces.js';
|
export * from './smartproxy/classes.pp.interfaces.js';
|
||||||
|
295
ts/redirect/classes.redirect.ts
Normal file
295
ts/redirect/classes.redirect.ts
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
import * as plugins from '../plugins.js';
|
||||||
|
|
||||||
|
export interface RedirectRule {
|
||||||
|
/**
|
||||||
|
* Optional protocol to match (http or https). If not specified, matches both.
|
||||||
|
*/
|
||||||
|
fromProtocol?: 'http' | 'https';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional hostname pattern to match. Can use * as wildcard.
|
||||||
|
* If not specified, matches all hosts.
|
||||||
|
*/
|
||||||
|
fromHost?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional path prefix to match. If not specified, matches all paths.
|
||||||
|
*/
|
||||||
|
fromPath?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target protocol for the redirect (http or https)
|
||||||
|
*/
|
||||||
|
toProtocol: 'http' | 'https';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target hostname for the redirect. Can use $1, $2, etc. to reference
|
||||||
|
* captured groups from wildcard matches in fromHost.
|
||||||
|
*/
|
||||||
|
toHost: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional target path prefix. If not specified, keeps original path.
|
||||||
|
* Can use $path to reference the original path.
|
||||||
|
*/
|
||||||
|
toPath?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP status code for the redirect (301 for permanent, 302 for temporary)
|
||||||
|
*/
|
||||||
|
statusCode?: 301 | 302 | 307 | 308;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Redirect {
|
||||||
|
private httpServer?: plugins.http.Server;
|
||||||
|
private httpsServer?: plugins.https.Server;
|
||||||
|
private rules: RedirectRule[] = [];
|
||||||
|
private httpPort: number = 80;
|
||||||
|
private httpsPort: number = 443;
|
||||||
|
private sslOptions?: {
|
||||||
|
key: Buffer;
|
||||||
|
cert: Buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Redirect instance
|
||||||
|
* @param options Configuration options
|
||||||
|
*/
|
||||||
|
constructor(options: {
|
||||||
|
httpPort?: number;
|
||||||
|
httpsPort?: number;
|
||||||
|
sslOptions?: {
|
||||||
|
key: Buffer;
|
||||||
|
cert: Buffer;
|
||||||
|
};
|
||||||
|
rules?: RedirectRule[];
|
||||||
|
} = {}) {
|
||||||
|
if (options.httpPort) this.httpPort = options.httpPort;
|
||||||
|
if (options.httpsPort) this.httpsPort = options.httpsPort;
|
||||||
|
if (options.sslOptions) this.sslOptions = options.sslOptions;
|
||||||
|
if (options.rules) this.rules = options.rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a redirect rule
|
||||||
|
*/
|
||||||
|
public addRule(rule: RedirectRule): void {
|
||||||
|
this.rules.push(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all redirect rules
|
||||||
|
*/
|
||||||
|
public clearRules(): void {
|
||||||
|
this.rules = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set SSL options for HTTPS redirects
|
||||||
|
*/
|
||||||
|
public setSslOptions(options: { key: Buffer; cert: Buffer }): void {
|
||||||
|
this.sslOptions = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a request according to the configured rules
|
||||||
|
*/
|
||||||
|
private handleRequest(
|
||||||
|
request: plugins.http.IncomingMessage,
|
||||||
|
response: plugins.http.ServerResponse,
|
||||||
|
protocol: 'http' | 'https'
|
||||||
|
): void {
|
||||||
|
const requestUrl = new URL(
|
||||||
|
request.url || '/',
|
||||||
|
`${protocol}://${request.headers.host || 'localhost'}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const host = requestUrl.hostname;
|
||||||
|
const path = requestUrl.pathname + requestUrl.search;
|
||||||
|
|
||||||
|
// Find matching rule
|
||||||
|
const matchedRule = this.findMatchingRule(protocol, host, path);
|
||||||
|
|
||||||
|
if (matchedRule) {
|
||||||
|
const targetUrl = this.buildTargetUrl(matchedRule, host, path);
|
||||||
|
|
||||||
|
console.log(`Redirecting ${protocol}://${host}${path} to ${targetUrl}`);
|
||||||
|
|
||||||
|
response.writeHead(matchedRule.statusCode || 302, {
|
||||||
|
Location: targetUrl,
|
||||||
|
});
|
||||||
|
response.end();
|
||||||
|
} else {
|
||||||
|
// No matching rule, send 404
|
||||||
|
response.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||||
|
response.end('Not Found');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a matching redirect rule for the given request
|
||||||
|
*/
|
||||||
|
private findMatchingRule(
|
||||||
|
protocol: 'http' | 'https',
|
||||||
|
host: string,
|
||||||
|
path: string
|
||||||
|
): RedirectRule | undefined {
|
||||||
|
return this.rules.find((rule) => {
|
||||||
|
// Check protocol match
|
||||||
|
if (rule.fromProtocol && rule.fromProtocol !== protocol) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check host match
|
||||||
|
if (rule.fromHost) {
|
||||||
|
const pattern = rule.fromHost.replace(/\*/g, '(.*)');
|
||||||
|
const regex = new RegExp(`^${pattern}$`);
|
||||||
|
if (!regex.test(host)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check path match
|
||||||
|
if (rule.fromPath && !path.startsWith(rule.fromPath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the target URL for a redirect
|
||||||
|
*/
|
||||||
|
private buildTargetUrl(rule: RedirectRule, originalHost: string, originalPath: string): string {
|
||||||
|
let targetHost = rule.toHost;
|
||||||
|
|
||||||
|
// Replace wildcards in host
|
||||||
|
if (rule.fromHost && rule.fromHost.includes('*')) {
|
||||||
|
const pattern = rule.fromHost.replace(/\*/g, '(.*)');
|
||||||
|
const regex = new RegExp(`^${pattern}$`);
|
||||||
|
const matches = originalHost.match(regex);
|
||||||
|
|
||||||
|
if (matches) {
|
||||||
|
for (let i = 1; i < matches.length; i++) {
|
||||||
|
targetHost = targetHost.replace(`$${i}`, matches[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build target path
|
||||||
|
let targetPath = originalPath;
|
||||||
|
if (rule.toPath) {
|
||||||
|
if (rule.toPath.includes('$path')) {
|
||||||
|
// Replace $path with original path, optionally removing the fromPath prefix
|
||||||
|
const pathSuffix = rule.fromPath ?
|
||||||
|
originalPath.substring(rule.fromPath.length) :
|
||||||
|
originalPath;
|
||||||
|
|
||||||
|
targetPath = rule.toPath.replace('$path', pathSuffix);
|
||||||
|
} else {
|
||||||
|
targetPath = rule.toPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${rule.toProtocol}://${targetHost}${targetPath}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the redirect server(s)
|
||||||
|
*/
|
||||||
|
public async start(): Promise<void> {
|
||||||
|
const tasks = [];
|
||||||
|
|
||||||
|
// Create and start HTTP server if we have a port
|
||||||
|
if (this.httpPort) {
|
||||||
|
this.httpServer = plugins.http.createServer((req, res) =>
|
||||||
|
this.handleRequest(req, res, 'http')
|
||||||
|
);
|
||||||
|
|
||||||
|
const httpStartPromise = new Promise<void>((resolve) => {
|
||||||
|
this.httpServer?.listen(this.httpPort, () => {
|
||||||
|
console.log(`HTTP redirect server started on port ${this.httpPort}`);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tasks.push(httpStartPromise);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and start HTTPS server if we have SSL options and a port
|
||||||
|
if (this.httpsPort && this.sslOptions) {
|
||||||
|
this.httpsServer = plugins.https.createServer(this.sslOptions, (req, res) =>
|
||||||
|
this.handleRequest(req, res, 'https')
|
||||||
|
);
|
||||||
|
|
||||||
|
const httpsStartPromise = new Promise<void>((resolve) => {
|
||||||
|
this.httpsServer?.listen(this.httpsPort, () => {
|
||||||
|
console.log(`HTTPS redirect server started on port ${this.httpsPort}`);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tasks.push(httpsStartPromise);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all servers to start
|
||||||
|
await Promise.all(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the redirect server(s)
|
||||||
|
*/
|
||||||
|
public async stop(): Promise<void> {
|
||||||
|
const tasks = [];
|
||||||
|
|
||||||
|
if (this.httpServer) {
|
||||||
|
const httpStopPromise = new Promise<void>((resolve) => {
|
||||||
|
this.httpServer?.close(() => {
|
||||||
|
console.log('HTTP redirect server stopped');
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
tasks.push(httpStopPromise);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.httpsServer) {
|
||||||
|
const httpsStopPromise = new Promise<void>((resolve) => {
|
||||||
|
this.httpsServer?.close(() => {
|
||||||
|
console.log('HTTPS redirect server stopped');
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
tasks.push(httpsStopPromise);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(tasks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For backward compatibility
|
||||||
|
export class SslRedirect {
|
||||||
|
private redirect: Redirect;
|
||||||
|
port: number;
|
||||||
|
|
||||||
|
constructor(portArg: number) {
|
||||||
|
this.port = portArg;
|
||||||
|
this.redirect = new Redirect({
|
||||||
|
httpPort: portArg,
|
||||||
|
rules: [{
|
||||||
|
fromProtocol: 'http',
|
||||||
|
toProtocol: 'https',
|
||||||
|
toHost: '$1',
|
||||||
|
statusCode: 302
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async start() {
|
||||||
|
await this.redirect.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async stop() {
|
||||||
|
await this.redirect.stop();
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user