@push.rocks/smartproxy 19.4.2 → 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.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/proxies/http-proxy/handlers/index.d.ts +1 -2
- package/dist_ts/proxies/http-proxy/handlers/index.js +3 -3
- package/dist_ts/proxies/smart-proxy/certificate-manager.js +30 -25
- package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +9 -40
- package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +2 -10
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +69 -43
- package/dist_ts/proxies/smart-proxy/utils/index.d.ts +2 -2
- package/dist_ts/proxies/smart-proxy/utils/index.js +3 -3
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +61 -20
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +240 -45
- package/dist_ts/proxies/smart-proxy/utils/route-patterns.d.ts +0 -18
- package/dist_ts/proxies/smart-proxy/utils/route-patterns.js +4 -43
- package/dist_ts/proxies/smart-proxy/utils/route-utils.js +14 -15
- package/dist_ts/proxies/smart-proxy/utils/route-validators.js +10 -31
- package/package.json +7 -7
- package/readme.hints.md +38 -1
- package/readme.plan.md +314 -382
- package/readme.plan2.md +764 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/proxies/http-proxy/handlers/index.ts +1 -2
- package/ts/proxies/smart-proxy/certificate-manager.ts +29 -23
- package/ts/proxies/smart-proxy/models/route-types.ts +12 -56
- package/ts/proxies/smart-proxy/route-connection-handler.ts +73 -60
- package/ts/proxies/smart-proxy/utils/index.ts +0 -2
- package/ts/proxies/smart-proxy/utils/route-helpers.ts +278 -61
- package/ts/proxies/smart-proxy/utils/route-patterns.ts +6 -56
- package/ts/proxies/smart-proxy/utils/route-utils.ts +12 -15
- package/ts/proxies/smart-proxy/utils/route-validators.ts +9 -31
- package/ts/proxies/http-proxy/handlers/redirect-handler.ts +0 -105
- 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
|
-
|