@push.rocks/smartproxy 19.4.1 → 19.5.2

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.
Files changed (32) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/proxies/http-proxy/handlers/index.d.ts +1 -2
  3. package/dist_ts/proxies/http-proxy/handlers/index.js +3 -3
  4. package/dist_ts/proxies/smart-proxy/certificate-manager.js +30 -25
  5. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +9 -40
  6. package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
  7. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +2 -10
  8. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +69 -43
  9. package/dist_ts/proxies/smart-proxy/utils/index.d.ts +2 -2
  10. package/dist_ts/proxies/smart-proxy/utils/index.js +3 -3
  11. package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +61 -20
  12. package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +240 -45
  13. package/dist_ts/proxies/smart-proxy/utils/route-patterns.d.ts +0 -18
  14. package/dist_ts/proxies/smart-proxy/utils/route-patterns.js +4 -43
  15. package/dist_ts/proxies/smart-proxy/utils/route-utils.js +14 -15
  16. package/dist_ts/proxies/smart-proxy/utils/route-validators.js +10 -31
  17. package/package.json +7 -7
  18. package/readme.hints.md +38 -1
  19. package/readme.plan.md +314 -382
  20. package/readme.plan2.md +764 -0
  21. package/ts/00_commitinfo_data.ts +1 -1
  22. package/ts/proxies/http-proxy/handlers/index.ts +1 -2
  23. package/ts/proxies/smart-proxy/certificate-manager.ts +29 -23
  24. package/ts/proxies/smart-proxy/models/route-types.ts +12 -56
  25. package/ts/proxies/smart-proxy/route-connection-handler.ts +73 -60
  26. package/ts/proxies/smart-proxy/utils/index.ts +0 -2
  27. package/ts/proxies/smart-proxy/utils/route-helpers.ts +278 -61
  28. package/ts/proxies/smart-proxy/utils/route-patterns.ts +6 -56
  29. package/ts/proxies/smart-proxy/utils/route-utils.ts +12 -15
  30. package/ts/proxies/smart-proxy/utils/route-validators.ts +9 -31
  31. package/ts/proxies/http-proxy/handlers/redirect-handler.ts +0 -105
  32. package/ts/proxies/http-proxy/handlers/static-handler.ts +0 -261
@@ -1,261 +0,0 @@
1
- import * as plugins from '../../../plugins.js';
2
- import type { IRouteConfig } from '../../smart-proxy/models/route-types.js';
3
- import type { IConnectionRecord } from '../../smart-proxy/models/interfaces.js';
4
- import type { ILogger } from '../models/types.js';
5
- import { createLogger } from '../models/types.js';
6
- import type { IRouteContext } from '../../../core/models/route-context.js';
7
- import { HttpStatus, getStatusText } from '../models/http-types.js';
8
-
9
- export interface IStaticHandlerContext {
10
- connectionId: string;
11
- connectionManager: any; // Avoid circular deps
12
- settings: any;
13
- logger?: ILogger;
14
- }
15
-
16
- /**
17
- * Handles static routes including ACME challenges
18
- */
19
- export class StaticHandler {
20
- /**
21
- * Handle static routes
22
- */
23
- public static async handleStatic(
24
- socket: plugins.net.Socket,
25
- route: IRouteConfig,
26
- context: IStaticHandlerContext,
27
- record: IConnectionRecord,
28
- initialChunk?: Buffer
29
- ): Promise<void> {
30
- const { connectionId, connectionManager, settings } = context;
31
- const logger = context.logger || createLogger(settings.logLevel || 'info');
32
-
33
- if (!route.action.handler) {
34
- logger.error(`[${connectionId}] Static route '${route.name}' has no handler`);
35
- socket.end();
36
- connectionManager.cleanupConnection(record, 'no_handler');
37
- return;
38
- }
39
-
40
- let buffer = Buffer.alloc(0);
41
- let processingData = false;
42
-
43
- const handleHttpData = async (chunk: Buffer) => {
44
- // Accumulate the data
45
- buffer = Buffer.concat([buffer, chunk]);
46
-
47
- // Prevent concurrent processing of the same buffer
48
- if (processingData) return;
49
- processingData = true;
50
-
51
- try {
52
- // Process data until we have a complete request or need more data
53
- await processBuffer();
54
- } finally {
55
- processingData = false;
56
- }
57
- };
58
-
59
- const processBuffer = async () => {
60
- // Look for end of HTTP headers
61
- const headerEndIndex = buffer.indexOf('\r\n\r\n');
62
- if (headerEndIndex === -1) {
63
- // Need more data
64
- if (buffer.length > 8192) {
65
- // Prevent excessive buffering
66
- logger.error(`[${connectionId}] HTTP headers too large`);
67
- socket.end();
68
- connectionManager.cleanupConnection(record, 'headers_too_large');
69
- }
70
- return; // Wait for more data to arrive
71
- }
72
-
73
- // Parse the HTTP request
74
- const headerBuffer = buffer.slice(0, headerEndIndex);
75
- const headers = headerBuffer.toString();
76
- const lines = headers.split('\r\n');
77
-
78
- if (lines.length === 0) {
79
- logger.error(`[${connectionId}] Invalid HTTP request`);
80
- socket.end();
81
- connectionManager.cleanupConnection(record, 'invalid_request');
82
- return;
83
- }
84
-
85
- // Parse request line
86
- const requestLine = lines[0];
87
- const requestParts = requestLine.split(' ');
88
- if (requestParts.length < 3) {
89
- logger.error(`[${connectionId}] Invalid HTTP request line`);
90
- socket.end();
91
- connectionManager.cleanupConnection(record, 'invalid_request_line');
92
- return;
93
- }
94
-
95
- const [method, path, httpVersion] = requestParts;
96
-
97
- // Parse headers
98
- const headersMap: Record<string, string> = {};
99
- for (let i = 1; i < lines.length; i++) {
100
- const colonIndex = lines[i].indexOf(':');
101
- if (colonIndex > 0) {
102
- const key = lines[i].slice(0, colonIndex).trim().toLowerCase();
103
- const value = lines[i].slice(colonIndex + 1).trim();
104
- headersMap[key] = value;
105
- }
106
- }
107
-
108
- // Check for Content-Length to handle request body
109
- const requestBodyLength = parseInt(headersMap['content-length'] || '0', 10);
110
- const bodyStartIndex = headerEndIndex + 4; // Skip the \r\n\r\n
111
-
112
- // If there's a body, ensure we have the full body
113
- if (requestBodyLength > 0) {
114
- const totalExpectedLength = bodyStartIndex + requestBodyLength;
115
-
116
- // If we don't have the complete body yet, wait for more data
117
- if (buffer.length < totalExpectedLength) {
118
- // Implement a reasonable body size limit to prevent memory issues
119
- if (requestBodyLength > 1024 * 1024) {
120
- // 1MB limit
121
- logger.error(`[${connectionId}] Request body too large`);
122
- socket.end();
123
- connectionManager.cleanupConnection(record, 'body_too_large');
124
- return;
125
- }
126
- return; // Wait for more data
127
- }
128
- }
129
-
130
- // Extract query string if present
131
- let pathname = path;
132
- let query: string | undefined;
133
- const queryIndex = path.indexOf('?');
134
- if (queryIndex !== -1) {
135
- pathname = path.slice(0, queryIndex);
136
- query = path.slice(queryIndex + 1);
137
- }
138
-
139
- try {
140
- // Get request body if present
141
- let requestBody: Buffer | undefined;
142
- if (requestBodyLength > 0) {
143
- requestBody = buffer.slice(bodyStartIndex, bodyStartIndex + requestBodyLength);
144
- }
145
-
146
- // Pause socket to prevent data loss during async processing
147
- socket.pause();
148
-
149
- // Remove the data listener since we're handling the request
150
- socket.removeListener('data', handleHttpData);
151
-
152
- // Build route context with parsed HTTP information
153
- const context: IRouteContext = {
154
- port: record.localPort,
155
- domain: record.lockedDomain || headersMap['host']?.split(':')[0],
156
- clientIp: record.remoteIP,
157
- serverIp: socket.localAddress!,
158
- path: pathname,
159
- query: query,
160
- headers: headersMap,
161
- isTls: record.isTLS,
162
- tlsVersion: record.tlsVersion,
163
- routeName: route.name,
164
- routeId: route.id,
165
- timestamp: Date.now(),
166
- connectionId,
167
- };
168
-
169
- // Since IRouteContext doesn't have a body property,
170
- // we need an alternative approach to handle the body
171
- let response;
172
-
173
- if (requestBody) {
174
- if (settings.enableDetailedLogging) {
175
- logger.info(
176
- `[${connectionId}] Processing request with body (${requestBody.length} bytes)`
177
- );
178
- }
179
-
180
- // Pass the body as an additional parameter by extending the context object
181
- // This is not type-safe, but it allows handlers that expect a body to work
182
- const extendedContext = {
183
- ...context,
184
- // Provide both raw buffer and string representation
185
- requestBody: requestBody,
186
- requestBodyText: requestBody.toString(),
187
- method: method,
188
- };
189
-
190
- // Call the handler with the extended context
191
- // The handler needs to know to look for the non-standard properties
192
- response = await route.action.handler(extendedContext as any);
193
- } else {
194
- // Call the handler with the standard context
195
- const extendedContext = {
196
- ...context,
197
- method: method,
198
- };
199
- response = await route.action.handler(extendedContext as any);
200
- }
201
-
202
- // Prepare the HTTP response
203
- const responseHeaders = response.headers || {};
204
- const contentLength = Buffer.byteLength(response.body || '');
205
- responseHeaders['Content-Length'] = contentLength.toString();
206
-
207
- if (!responseHeaders['Content-Type']) {
208
- responseHeaders['Content-Type'] = 'text/plain';
209
- }
210
-
211
- // Build the response
212
- let httpResponse = `HTTP/1.1 ${response.status} ${getStatusText(response.status)}\r\n`;
213
- for (const [key, value] of Object.entries(responseHeaders)) {
214
- httpResponse += `${key}: ${value}\r\n`;
215
- }
216
- httpResponse += '\r\n';
217
-
218
- // Send response
219
- socket.write(httpResponse);
220
- if (response.body) {
221
- socket.write(response.body);
222
- }
223
- socket.end();
224
-
225
- connectionManager.cleanupConnection(record, 'completed');
226
- } catch (error) {
227
- logger.error(`[${connectionId}] Error in static handler: ${error}`);
228
-
229
- // Send error response
230
- const errorResponse =
231
- 'HTTP/1.1 500 Internal Server Error\r\n' +
232
- 'Content-Type: text/plain\r\n' +
233
- 'Content-Length: 21\r\n' +
234
- '\r\n' +
235
- 'Internal Server Error';
236
- socket.write(errorResponse);
237
- socket.end();
238
-
239
- connectionManager.cleanupConnection(record, 'handler_error');
240
- }
241
- };
242
-
243
- // Process initial chunk if provided
244
- if (initialChunk && initialChunk.length > 0) {
245
- if (settings.enableDetailedLogging) {
246
- logger.info(`[${connectionId}] Processing initial data chunk (${initialChunk.length} bytes)`);
247
- }
248
- // Process the initial chunk immediately
249
- handleHttpData(initialChunk);
250
- }
251
-
252
- // Listen for additional data
253
- socket.on('data', handleHttpData);
254
-
255
- // Ensure cleanup on socket close
256
- socket.once('close', () => {
257
- socket.removeListener('data', handleHttpData);
258
- });
259
- }
260
- }
261
-