@push.rocks/smartproxy 19.6.12 → 19.6.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -51,18 +51,16 @@ export class ThroughputTracker {
|
|
|
51
51
|
if (relevantSamples.length === 0) {
|
|
52
52
|
return { in: 0, out: 0 };
|
|
53
53
|
}
|
|
54
|
-
//
|
|
54
|
+
// Calculate total bytes in window
|
|
55
55
|
const totalBytesIn = relevantSamples.reduce((sum, s) => sum + s.bytesIn, 0);
|
|
56
56
|
const totalBytesOut = relevantSamples.reduce((sum, s) => sum + s.bytesOut, 0);
|
|
57
|
-
//
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return { in: 0, out: 0 };
|
|
62
|
-
}
|
|
57
|
+
// Use actual number of seconds covered by samples for accurate rate
|
|
58
|
+
const oldestSampleTime = relevantSamples[0].timestamp;
|
|
59
|
+
const newestSampleTime = relevantSamples[relevantSamples.length - 1].timestamp;
|
|
60
|
+
const actualSeconds = Math.max(1, (newestSampleTime - oldestSampleTime) / 1000 + 1);
|
|
63
61
|
return {
|
|
64
|
-
in: Math.round(totalBytesIn /
|
|
65
|
-
out: Math.round(totalBytesOut /
|
|
62
|
+
in: Math.round(totalBytesIn / actualSeconds),
|
|
63
|
+
out: Math.round(totalBytesOut / actualSeconds)
|
|
66
64
|
};
|
|
67
65
|
}
|
|
68
66
|
/**
|
|
@@ -114,4 +112,4 @@ export class ThroughputTracker {
|
|
|
114
112
|
return this.samples.length;
|
|
115
113
|
}
|
|
116
114
|
}
|
|
117
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
115
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGhyb3VnaHB1dC10cmFja2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvcHJveGllcy9zbWFydC1wcm94eS90aHJvdWdocHV0LXRyYWNrZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUE7O0dBRUc7QUFDSCxNQUFNLE9BQU8saUJBQWlCO0lBTzVCLFlBQVksbUJBQTJCLElBQUk7UUFObkMsWUFBTyxHQUF3QixFQUFFLENBQUM7UUFFbEMsdUJBQWtCLEdBQVcsQ0FBQyxDQUFDO1FBQy9CLHdCQUFtQixHQUFXLENBQUMsQ0FBQztRQUNoQyxtQkFBYyxHQUFXLENBQUMsQ0FBQztRQUdqQywrREFBK0Q7UUFDL0QsSUFBSSxDQUFDLFVBQVUsR0FBRyxnQkFBZ0IsQ0FBQztJQUNyQyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxXQUFXLENBQUMsT0FBZSxFQUFFLFFBQWdCO1FBQ2xELElBQUksQ0FBQyxrQkFBa0IsSUFBSSxPQUFPLENBQUM7UUFDbkMsSUFBSSxDQUFDLG1CQUFtQixJQUFJLFFBQVEsQ0FBQztJQUN2QyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxVQUFVO1FBQ2YsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBRXZCLDZDQUE2QztRQUM3QyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQztZQUNoQixTQUFTLEVBQUUsR0FBRztZQUNkLE9BQU8sRUFBRSxJQUFJLENBQUMsa0JBQWtCO1lBQ2hDLFFBQVEsRUFBRSxJQUFJLENBQUMsbUJBQW1CO1NBQ25DLENBQUMsQ0FBQztRQUVILHFCQUFxQjtRQUNyQixJQUFJLENBQUMsa0JBQWtCLEdBQUcsQ0FBQyxDQUFDO1FBQzVCLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxDQUFDLENBQUM7UUFDN0IsSUFBSSxDQUFDLGNBQWMsR0FBRyxHQUFHLENBQUM7UUFFMUIsbURBQW1EO1FBQ25ELElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQzFDLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDdkIsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNJLE9BQU8sQ0FBQyxhQUFxQjtRQUNsQyxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQzlCLE9BQU8sRUFBRSxFQUFFLEVBQUUsQ0FBQyxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsQ0FBQztRQUMzQixDQUFDO1FBRUQsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ3ZCLE1BQU0sV0FBVyxHQUFHLEdBQUcsR0FBRyxDQUFDLGFBQWEsR0FBRyxJQUFJLENBQUMsQ0FBQztRQUVqRCxpQ0FBaUM7UUFDakMsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxHQUFHLFdBQVcsQ0FBQyxDQUFDO1FBRTVFLElBQUksZUFBZSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUNqQyxPQUFPLEVBQUUsRUFBRSxFQUFFLENBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLENBQUM7UUFDM0IsQ0FBQztRQUVELGtDQUFrQztRQUNsQyxNQUFNLFlBQVksR0FBRyxlQUFlLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsR0FBRyxHQUFHLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDNUUsTUFBTSxhQUFhLEdBQUcsZUFBZSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBRTlFLG9FQUFvRTtRQUNwRSxNQUFNLGdCQUFnQixHQUFHLGVBQWUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7UUFDdEQsTUFBTSxnQkFBZ0IsR0FBRyxlQUFlLENBQUMsZUFBZSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7UUFDL0UsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxnQkFBZ0IsR0FBRyxnQkFBZ0IsQ0FBQyxHQUFHLElBQUksR0FBRyxDQUFDLENBQUMsQ0FBQztRQUVwRixPQUFPO1lBQ0wsRUFBRSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxHQUFHLGFBQWEsQ0FBQztZQUM1QyxHQUFHLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxhQUFhLEdBQUcsYUFBYSxDQUFDO1NBQy9DLENBQUM7SUFDSixDQUFDO0lBRUQ7O09BRUc7SUFDSSxVQUFVLENBQUMsZUFBdUI7UUFDdkMsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ3ZCLE1BQU0sU0FBUyxHQUFHLEdBQUcsR0FBRyxDQUFDLGVBQWUsR0FBRyxJQUFJLENBQUMsQ0FBQztRQUVqRCxpQ0FBaUM7UUFDakMsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxHQUFHLFNBQVMsQ0FBQyxDQUFDO1FBRTFFLGtEQUFrRDtRQUNsRCxNQUFNLE9BQU8sR0FBOEIsRUFBRSxDQUFDO1FBRTlDLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxlQUFlLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDaEQsTUFBTSxNQUFNLEdBQUcsZUFBZSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBRWxDLHNFQUFzRTtZQUN0RSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLFNBQVMsR0FBRyxlQUFlLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLFNBQVMsR0FBRyxJQUFJLEVBQUUsQ0FBQztnQkFDMUUsT0FBTyxDQUFDLElBQUksQ0FBQztvQkFDWCxTQUFTLEVBQUUsTUFBTSxDQUFDLFNBQVM7b0JBQzNCLEVBQUUsRUFBRSxNQUFNLENBQUMsT0FBTztvQkFDbEIsR0FBRyxFQUFFLE1BQU0sQ0FBQyxRQUFRO2lCQUNyQixDQUFDLENBQUM7WUFDTCxDQUFDO2lCQUFNLENBQUM7Z0JBQ04scURBQXFEO2dCQUNyRCxNQUFNLFVBQVUsR0FBRyxlQUFlLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUMxQyxNQUFNLFNBQVMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEdBQUcsVUFBVSxDQUFDLFNBQVMsQ0FBQyxHQUFHLElBQUksQ0FBQztnQkFFbkUsT0FBTyxDQUFDLElBQUksQ0FBQztvQkFDWCxTQUFTLEVBQUUsTUFBTSxDQUFDLFNBQVM7b0JBQzNCLEVBQUUsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxPQUFPLEdBQUcsU0FBUyxDQUFDO29CQUMxQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsUUFBUSxHQUFHLFNBQVMsQ0FBQztpQkFDN0MsQ0FBQyxDQUFDO1lBQ0wsQ0FBQztRQUNILENBQUM7UUFFRCxPQUFPLE9BQU8sQ0FBQztJQUNqQixDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLO1FBQ1YsSUFBSSxDQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDbEIsSUFBSSxDQUFDLGtCQUFrQixHQUFHLENBQUMsQ0FBQztRQUM1QixJQUFJLENBQUMsbUJBQW1CLEdBQUcsQ0FBQyxDQUFDO1FBQzdCLElBQUksQ0FBQyxjQUFjLEdBQUcsQ0FBQyxDQUFDO0lBQzFCLENBQUM7SUFFRDs7T0FFRztJQUNJLGNBQWM7UUFDbkIsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQztJQUM3QixDQUFDO0NBQ0YifQ==
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartproxy",
|
|
3
|
-
"version": "19.6.
|
|
3
|
+
"version": "19.6.13",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
package/readme.hints.md
CHANGED
|
@@ -142,4 +142,45 @@ Keep-alive connections receive special treatment based on `keepAliveTreatment` s
|
|
|
142
142
|
The system supports both receiving and sending PROXY protocol:
|
|
143
143
|
- **Receiving**: Automatically detected from trusted proxy IPs (configured in `proxyIPs`)
|
|
144
144
|
- **Sending**: Enabled per-route or globally via `sendProxyProtocol` setting
|
|
145
|
-
- Real client IP is preserved and used for all connection tracking and security checks
|
|
145
|
+
- Real client IP is preserved and used for all connection tracking and security checks
|
|
146
|
+
|
|
147
|
+
## Metrics and Throughput Calculation
|
|
148
|
+
|
|
149
|
+
The metrics system tracks throughput using per-second sampling:
|
|
150
|
+
|
|
151
|
+
1. **Byte Recording**: Bytes are recorded as data flows through connections
|
|
152
|
+
2. **Sampling**: Every second, accumulated bytes are stored as a sample
|
|
153
|
+
3. **Rate Calculation**: Throughput is calculated by summing bytes over a time window
|
|
154
|
+
4. **Per-Route/IP Tracking**: Separate ThroughputTracker instances for each route and IP
|
|
155
|
+
|
|
156
|
+
Key implementation details:
|
|
157
|
+
- Bytes are recorded in the bidirectional forwarding callbacks
|
|
158
|
+
- The instant() method returns throughput over the last 1 second
|
|
159
|
+
- The recent() method returns throughput over the last 10 seconds
|
|
160
|
+
- Custom windows can be specified for different averaging periods
|
|
161
|
+
|
|
162
|
+
### Throughput Spikes Issue
|
|
163
|
+
|
|
164
|
+
There's a fundamental difference between application-layer and network-layer throughput:
|
|
165
|
+
|
|
166
|
+
**Application Layer (what we measure)**:
|
|
167
|
+
- Bytes are recorded when delivered to/from the application
|
|
168
|
+
- Large chunks can arrive "instantly" due to kernel/Node.js buffering
|
|
169
|
+
- Shows spikes when buffers are flushed (e.g., 20MB in 1 second = 160 Mbit/s)
|
|
170
|
+
|
|
171
|
+
**Network Layer (what Unifi shows)**:
|
|
172
|
+
- Actual packet flow through the network interface
|
|
173
|
+
- Limited by physical network speed (e.g., 20 Mbit/s)
|
|
174
|
+
- Data transfers over time, not in bursts
|
|
175
|
+
|
|
176
|
+
The spikes occur because:
|
|
177
|
+
1. Data flows over network at 20 Mbit/s (takes 8 seconds for 20MB)
|
|
178
|
+
2. Kernel/Node.js buffers this incoming data
|
|
179
|
+
3. When buffer is flushed, application receives large chunk at once
|
|
180
|
+
4. We record entire chunk in current second, creating artificial spike
|
|
181
|
+
|
|
182
|
+
**Potential Solutions**:
|
|
183
|
+
1. Use longer window for "instant" measurements (e.g., 5 seconds instead of 1)
|
|
184
|
+
2. Track socket write backpressure to estimate actual network flow
|
|
185
|
+
3. Implement bandwidth estimation based on connection duration
|
|
186
|
+
4. Accept that application-layer != network-layer throughput
|
|
@@ -65,24 +65,18 @@ export class ThroughputTracker {
|
|
|
65
65
|
return { in: 0, out: 0 };
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
//
|
|
68
|
+
// Calculate total bytes in window
|
|
69
69
|
const totalBytesIn = relevantSamples.reduce((sum, s) => sum + s.bytesIn, 0);
|
|
70
70
|
const totalBytesOut = relevantSamples.reduce((sum, s) => sum + s.bytesOut, 0);
|
|
71
71
|
|
|
72
|
-
//
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
// Avoid division by zero
|
|
79
|
-
if (actualWindowSeconds === 0) {
|
|
80
|
-
return { in: 0, out: 0 };
|
|
81
|
-
}
|
|
72
|
+
// Use actual number of seconds covered by samples for accurate rate
|
|
73
|
+
const oldestSampleTime = relevantSamples[0].timestamp;
|
|
74
|
+
const newestSampleTime = relevantSamples[relevantSamples.length - 1].timestamp;
|
|
75
|
+
const actualSeconds = Math.max(1, (newestSampleTime - oldestSampleTime) / 1000 + 1);
|
|
82
76
|
|
|
83
77
|
return {
|
|
84
|
-
in: Math.round(totalBytesIn /
|
|
85
|
-
out: Math.round(totalBytesOut /
|
|
78
|
+
in: Math.round(totalBytesIn / actualSeconds),
|
|
79
|
+
out: Math.round(totalBytesOut / actualSeconds)
|
|
86
80
|
};
|
|
87
81
|
}
|
|
88
82
|
|