@push.rocks/smartproxy 21.0.0 → 21.1.1

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 (146) hide show
  1. package/changelog.md +18 -0
  2. package/dist_ts/core/utils/proxy-protocol.d.ts +5 -17
  3. package/dist_ts/core/utils/proxy-protocol.js +13 -97
  4. package/dist_ts/core/utils/websocket-utils.d.ts +6 -7
  5. package/dist_ts/core/utils/websocket-utils.js +10 -66
  6. package/dist_ts/detection/detectors/http-detector-v2.d.ts +33 -0
  7. package/dist_ts/detection/detectors/http-detector-v2.js +87 -0
  8. package/dist_ts/detection/detectors/http-detector.d.ts +33 -0
  9. package/dist_ts/detection/detectors/http-detector.js +89 -0
  10. package/dist_ts/detection/detectors/quick-detector.d.ts +28 -0
  11. package/dist_ts/detection/detectors/quick-detector.js +131 -0
  12. package/dist_ts/detection/detectors/routing-extractor.d.ts +28 -0
  13. package/dist_ts/detection/detectors/routing-extractor.js +122 -0
  14. package/dist_ts/detection/detectors/tls-detector-v2.d.ts +33 -0
  15. package/dist_ts/detection/detectors/tls-detector-v2.js +80 -0
  16. package/dist_ts/detection/detectors/tls-detector.d.ts +55 -0
  17. package/dist_ts/detection/detectors/tls-detector.js +206 -0
  18. package/dist_ts/detection/index.d.ts +17 -0
  19. package/dist_ts/detection/index.js +22 -0
  20. package/dist_ts/detection/models/detection-types.d.ts +87 -0
  21. package/dist_ts/detection/models/detection-types.js +5 -0
  22. package/dist_ts/detection/models/interfaces.d.ts +97 -0
  23. package/dist_ts/detection/models/interfaces.js +5 -0
  24. package/dist_ts/detection/protocol-detector-v2.d.ts +46 -0
  25. package/dist_ts/detection/protocol-detector-v2.js +116 -0
  26. package/dist_ts/detection/protocol-detector.d.ts +74 -0
  27. package/dist_ts/detection/protocol-detector.js +173 -0
  28. package/dist_ts/detection/utils/buffer-utils.d.ts +61 -0
  29. package/dist_ts/detection/utils/buffer-utils.js +127 -0
  30. package/dist_ts/detection/utils/fragment-manager.d.ts +31 -0
  31. package/dist_ts/detection/utils/fragment-manager.js +53 -0
  32. package/dist_ts/detection/utils/parser-utils.d.ts +42 -0
  33. package/dist_ts/detection/utils/parser-utils.js +63 -0
  34. package/dist_ts/index.d.ts +2 -0
  35. package/dist_ts/index.js +3 -1
  36. package/dist_ts/protocols/common/fragment-handler.d.ts +73 -0
  37. package/dist_ts/protocols/common/fragment-handler.js +117 -0
  38. package/dist_ts/protocols/common/index.d.ts +7 -0
  39. package/dist_ts/protocols/common/index.js +8 -0
  40. package/dist_ts/protocols/common/types.d.ts +68 -0
  41. package/dist_ts/protocols/common/types.js +7 -0
  42. package/dist_ts/protocols/http/constants.d.ts +119 -0
  43. package/dist_ts/protocols/http/constants.js +200 -0
  44. package/dist_ts/protocols/http/index.d.ts +7 -0
  45. package/dist_ts/protocols/http/index.js +8 -0
  46. package/dist_ts/protocols/http/parser.d.ts +58 -0
  47. package/dist_ts/protocols/http/parser.js +184 -0
  48. package/dist_ts/protocols/http/types.d.ts +62 -0
  49. package/dist_ts/protocols/http/types.js +5 -0
  50. package/dist_ts/protocols/index.d.ts +11 -0
  51. package/dist_ts/protocols/index.js +12 -0
  52. package/dist_ts/protocols/proxy/index.d.ts +6 -0
  53. package/dist_ts/protocols/proxy/index.js +7 -0
  54. package/dist_ts/protocols/proxy/parser.d.ts +44 -0
  55. package/dist_ts/protocols/proxy/parser.js +153 -0
  56. package/dist_ts/protocols/proxy/types.d.ts +47 -0
  57. package/dist_ts/protocols/proxy/types.js +6 -0
  58. package/dist_ts/protocols/tls/alerts/index.d.ts +4 -0
  59. package/dist_ts/protocols/tls/alerts/index.js +5 -0
  60. package/dist_ts/protocols/tls/alerts/tls-alert.d.ts +150 -0
  61. package/dist_ts/protocols/tls/alerts/tls-alert.js +226 -0
  62. package/dist_ts/protocols/tls/constants.d.ts +122 -0
  63. package/dist_ts/protocols/tls/constants.js +135 -0
  64. package/dist_ts/protocols/tls/index.d.ts +12 -0
  65. package/dist_ts/protocols/tls/index.js +27 -0
  66. package/dist_ts/protocols/tls/parser.d.ts +53 -0
  67. package/dist_ts/protocols/tls/parser.js +294 -0
  68. package/dist_ts/protocols/tls/sni/client-hello-parser.d.ts +100 -0
  69. package/dist_ts/protocols/tls/sni/client-hello-parser.js +463 -0
  70. package/dist_ts/protocols/tls/sni/index.d.ts +5 -0
  71. package/dist_ts/protocols/tls/sni/index.js +6 -0
  72. package/dist_ts/protocols/tls/sni/sni-extraction.d.ts +58 -0
  73. package/dist_ts/protocols/tls/sni/sni-extraction.js +275 -0
  74. package/dist_ts/protocols/tls/types.d.ts +65 -0
  75. package/dist_ts/protocols/tls/types.js +5 -0
  76. package/dist_ts/protocols/tls/utils/index.d.ts +4 -0
  77. package/dist_ts/protocols/tls/utils/index.js +5 -0
  78. package/dist_ts/protocols/tls/utils/tls-utils.d.ts +158 -0
  79. package/dist_ts/protocols/tls/utils/tls-utils.js +187 -0
  80. package/dist_ts/protocols/websocket/constants.d.ts +55 -0
  81. package/dist_ts/protocols/websocket/constants.js +58 -0
  82. package/dist_ts/protocols/websocket/index.d.ts +7 -0
  83. package/dist_ts/protocols/websocket/index.js +8 -0
  84. package/dist_ts/protocols/websocket/types.d.ts +47 -0
  85. package/dist_ts/protocols/websocket/types.js +5 -0
  86. package/dist_ts/protocols/websocket/utils.d.ts +25 -0
  87. package/dist_ts/protocols/websocket/utils.js +103 -0
  88. package/dist_ts/proxies/http-proxy/models/http-types.d.ts +25 -27
  89. package/dist_ts/proxies/http-proxy/models/http-types.js +24 -44
  90. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -0
  91. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +81 -61
  92. package/dist_ts/proxies/smart-proxy/tls-manager.js +2 -1
  93. package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +2 -0
  94. package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +61 -52
  95. package/dist_ts/tls/index.d.ts +5 -7
  96. package/dist_ts/tls/index.js +8 -11
  97. package/dist_ts/tls/sni/client-hello-parser.js +3 -2
  98. package/dist_ts/tls/sni/sni-handler.js +4 -4
  99. package/dist_ts/tls/utils/tls-utils.d.ts +1 -110
  100. package/dist_ts/tls/utils/tls-utils.js +4 -116
  101. package/package.json +1 -1
  102. package/readme.plan.md +0 -0
  103. package/ts/core/utils/proxy-protocol.ts +14 -131
  104. package/ts/core/utils/websocket-utils.ts +12 -60
  105. package/ts/detection/detectors/http-detector.ts +114 -0
  106. package/ts/detection/detectors/quick-detector.ts +148 -0
  107. package/ts/detection/detectors/routing-extractor.ts +147 -0
  108. package/ts/detection/detectors/tls-detector.ts +252 -0
  109. package/ts/detection/index.ts +25 -0
  110. package/ts/detection/models/detection-types.ts +102 -0
  111. package/ts/detection/models/interfaces.ts +115 -0
  112. package/ts/detection/protocol-detector.ts +230 -0
  113. package/ts/detection/utils/buffer-utils.ts +141 -0
  114. package/ts/detection/utils/fragment-manager.ts +64 -0
  115. package/ts/detection/utils/parser-utils.ts +77 -0
  116. package/ts/index.ts +3 -1
  117. package/ts/protocols/common/fragment-handler.ts +163 -0
  118. package/ts/protocols/common/index.ts +8 -0
  119. package/ts/protocols/common/types.ts +76 -0
  120. package/ts/protocols/http/constants.ts +219 -0
  121. package/ts/protocols/http/index.ts +8 -0
  122. package/ts/protocols/http/parser.ts +219 -0
  123. package/ts/protocols/http/types.ts +70 -0
  124. package/ts/protocols/index.ts +12 -0
  125. package/ts/protocols/proxy/index.ts +7 -0
  126. package/ts/protocols/proxy/parser.ts +183 -0
  127. package/ts/protocols/proxy/types.ts +53 -0
  128. package/ts/{tls → protocols/tls}/alerts/tls-alert.ts +1 -1
  129. package/ts/protocols/tls/index.ts +37 -0
  130. package/ts/protocols/tls/sni/index.ts +6 -0
  131. package/ts/{tls → protocols/tls}/utils/tls-utils.ts +1 -1
  132. package/ts/protocols/websocket/constants.ts +60 -0
  133. package/ts/protocols/websocket/index.ts +8 -0
  134. package/ts/protocols/websocket/types.ts +53 -0
  135. package/ts/protocols/websocket/utils.ts +98 -0
  136. package/ts/proxies/http-proxy/models/http-types.ts +29 -46
  137. package/ts/proxies/smart-proxy/models/interfaces.ts +7 -0
  138. package/ts/proxies/smart-proxy/route-connection-handler.ts +91 -68
  139. package/ts/proxies/smart-proxy/tls-manager.ts +1 -0
  140. package/ts/proxies/smart-proxy/utils/route-helpers.ts +72 -56
  141. package/ts/tls/index.ts +8 -12
  142. package/ts/tls/sni/sni-handler.ts +3 -3
  143. /package/ts/{tls → protocols/tls}/alerts/index.ts +0 -0
  144. /package/ts/{tls → protocols/tls}/sni/client-hello-parser.ts +0 -0
  145. /package/ts/{tls → protocols/tls}/sni/sni-extraction.ts +0 -0
  146. /package/ts/{tls → protocols/tls}/utils/index.ts +0 -0
@@ -1,4 +1,6 @@
1
1
  import * as plugins from '../../../plugins.js';
2
+ // Import from protocols for consistent status codes
3
+ import { HttpStatus as ProtocolHttpStatus, getStatusText as getProtocolStatusText } from '../../../protocols/http/index.js';
2
4
 
3
5
  /**
4
6
  * HTTP-specific event types
@@ -10,34 +12,33 @@ export enum HttpEvents {
10
12
  REQUEST_ERROR = 'request-error',
11
13
  }
12
14
 
13
- /**
14
- * HTTP status codes as an enum for better type safety
15
- */
16
- export enum HttpStatus {
17
- OK = 200,
18
- MOVED_PERMANENTLY = 301,
19
- FOUND = 302,
20
- TEMPORARY_REDIRECT = 307,
21
- PERMANENT_REDIRECT = 308,
22
- BAD_REQUEST = 400,
23
- UNAUTHORIZED = 401,
24
- FORBIDDEN = 403,
25
- NOT_FOUND = 404,
26
- METHOD_NOT_ALLOWED = 405,
27
- REQUEST_TIMEOUT = 408,
28
- TOO_MANY_REQUESTS = 429,
29
- INTERNAL_SERVER_ERROR = 500,
30
- NOT_IMPLEMENTED = 501,
31
- BAD_GATEWAY = 502,
32
- SERVICE_UNAVAILABLE = 503,
33
- GATEWAY_TIMEOUT = 504,
34
- }
15
+
16
+ // Re-export for backward compatibility with subset of commonly used codes
17
+ export const HttpStatus = {
18
+ OK: ProtocolHttpStatus.OK,
19
+ MOVED_PERMANENTLY: ProtocolHttpStatus.MOVED_PERMANENTLY,
20
+ FOUND: ProtocolHttpStatus.FOUND,
21
+ TEMPORARY_REDIRECT: ProtocolHttpStatus.TEMPORARY_REDIRECT,
22
+ PERMANENT_REDIRECT: ProtocolHttpStatus.PERMANENT_REDIRECT,
23
+ BAD_REQUEST: ProtocolHttpStatus.BAD_REQUEST,
24
+ UNAUTHORIZED: ProtocolHttpStatus.UNAUTHORIZED,
25
+ FORBIDDEN: ProtocolHttpStatus.FORBIDDEN,
26
+ NOT_FOUND: ProtocolHttpStatus.NOT_FOUND,
27
+ METHOD_NOT_ALLOWED: ProtocolHttpStatus.METHOD_NOT_ALLOWED,
28
+ REQUEST_TIMEOUT: ProtocolHttpStatus.REQUEST_TIMEOUT,
29
+ TOO_MANY_REQUESTS: ProtocolHttpStatus.TOO_MANY_REQUESTS,
30
+ INTERNAL_SERVER_ERROR: ProtocolHttpStatus.INTERNAL_SERVER_ERROR,
31
+ NOT_IMPLEMENTED: ProtocolHttpStatus.NOT_IMPLEMENTED,
32
+ BAD_GATEWAY: ProtocolHttpStatus.BAD_GATEWAY,
33
+ SERVICE_UNAVAILABLE: ProtocolHttpStatus.SERVICE_UNAVAILABLE,
34
+ GATEWAY_TIMEOUT: ProtocolHttpStatus.GATEWAY_TIMEOUT,
35
+ } as const;
35
36
 
36
37
  /**
37
38
  * Base error class for HTTP-related errors
38
39
  */
39
40
  export class HttpError extends Error {
40
- constructor(message: string, public readonly statusCode: HttpStatus = HttpStatus.INTERNAL_SERVER_ERROR) {
41
+ constructor(message: string, public readonly statusCode: number = HttpStatus.INTERNAL_SERVER_ERROR) {
41
42
  super(message);
42
43
  this.name = 'HttpError';
43
44
  }
@@ -61,7 +62,7 @@ export class CertificateError extends HttpError {
61
62
  * Error related to server operations
62
63
  */
63
64
  export class ServerError extends HttpError {
64
- constructor(message: string, public readonly code?: string, statusCode: HttpStatus = HttpStatus.INTERNAL_SERVER_ERROR) {
65
+ constructor(message: string, public readonly code?: string, statusCode: number = HttpStatus.INTERNAL_SERVER_ERROR) {
65
66
  super(message, statusCode);
66
67
  this.name = 'ServerError';
67
68
  }
@@ -93,7 +94,7 @@ export class NotFoundError extends HttpError {
93
94
  export interface IRedirectConfig {
94
95
  source: string; // Source path or pattern
95
96
  destination: string; // Destination URL
96
- type: HttpStatus; // Redirect status code
97
+ type: number; // Redirect status code
97
98
  preserveQuery?: boolean; // Whether to preserve query parameters
98
99
  }
99
100
 
@@ -115,30 +116,12 @@ export interface IRouterConfig {
115
116
  */
116
117
  export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
117
118
 
119
+
118
120
  /**
119
121
  * Helper function to get HTTP status text
120
122
  */
121
- export function getStatusText(status: HttpStatus): string {
122
- const statusTexts: Record<HttpStatus, string> = {
123
- [HttpStatus.OK]: 'OK',
124
- [HttpStatus.MOVED_PERMANENTLY]: 'Moved Permanently',
125
- [HttpStatus.FOUND]: 'Found',
126
- [HttpStatus.TEMPORARY_REDIRECT]: 'Temporary Redirect',
127
- [HttpStatus.PERMANENT_REDIRECT]: 'Permanent Redirect',
128
- [HttpStatus.BAD_REQUEST]: 'Bad Request',
129
- [HttpStatus.UNAUTHORIZED]: 'Unauthorized',
130
- [HttpStatus.FORBIDDEN]: 'Forbidden',
131
- [HttpStatus.NOT_FOUND]: 'Not Found',
132
- [HttpStatus.METHOD_NOT_ALLOWED]: 'Method Not Allowed',
133
- [HttpStatus.REQUEST_TIMEOUT]: 'Request Timeout',
134
- [HttpStatus.TOO_MANY_REQUESTS]: 'Too Many Requests',
135
- [HttpStatus.INTERNAL_SERVER_ERROR]: 'Internal Server Error',
136
- [HttpStatus.NOT_IMPLEMENTED]: 'Not Implemented',
137
- [HttpStatus.BAD_GATEWAY]: 'Bad Gateway',
138
- [HttpStatus.SERVICE_UNAVAILABLE]: 'Service Unavailable',
139
- [HttpStatus.GATEWAY_TIMEOUT]: 'Gateway Timeout',
140
- };
141
- return statusTexts[status] || 'Unknown';
123
+ export function getStatusText(status: number): string {
124
+ return getProtocolStatusText(status as ProtocolHttpStatus);
142
125
  }
143
126
 
144
127
  // Legacy interfaces for backward compatibility
@@ -195,4 +195,11 @@ export interface IConnectionRecord {
195
195
 
196
196
  // NFTables tracking
197
197
  nftablesHandled?: boolean; // Whether this connection is being handled by NFTables at kernel level
198
+
199
+ // HTTP-specific information (extracted from protocol detection)
200
+ httpInfo?: {
201
+ method?: string;
202
+ path?: string;
203
+ headers?: Record<string, string>;
204
+ };
198
205
  }
@@ -10,6 +10,7 @@ import { WrappedSocket } from '../../core/models/wrapped-socket.js';
10
10
  import { getUnderlyingSocket } from '../../core/models/socket-types.js';
11
11
  import { ProxyProtocolParser } from '../../core/utils/proxy-protocol.js';
12
12
  import type { SmartProxy } from './smart-proxy.js';
13
+ import { ProtocolDetector } from '../../detection/index.js';
13
14
 
14
15
  /**
15
16
  * Handles new connection processing and setup logic with support for route-based configuration
@@ -301,11 +302,27 @@ export class RouteConnectionHandler {
301
302
  });
302
303
 
303
304
  // Handler for processing initial data (after potential PROXY protocol)
304
- const processInitialData = (chunk: Buffer) => {
305
+ const processInitialData = async (chunk: Buffer) => {
306
+ // Use ProtocolDetector to identify protocol
307
+ const connectionId = ProtocolDetector.createConnectionId({
308
+ sourceIp: record.remoteIP,
309
+ sourcePort: socket.remotePort,
310
+ destIp: socket.localAddress,
311
+ destPort: socket.localPort,
312
+ socketId: record.id
313
+ });
314
+
315
+ const detectionResult = await ProtocolDetector.detectWithConnectionTracking(
316
+ chunk,
317
+ connectionId,
318
+ { extractFullHeaders: false } // Only extract essential info for routing
319
+ );
320
+
305
321
  // Block non-TLS connections on port 443
306
- if (!this.smartProxy.tlsManager.isTlsHandshake(chunk) && localPort === 443) {
307
- logger.log('warn', `Non-TLS connection ${connectionId} detected on port 443. Terminating connection - only TLS traffic is allowed on standard HTTPS port.`, {
308
- connectionId,
322
+ if (localPort === 443 && detectionResult.protocol !== 'tls') {
323
+ logger.log('warn', `Non-TLS connection ${record.id} detected on port 443. Terminating connection - only TLS traffic is allowed on standard HTTPS port.`, {
324
+ connectionId: record.id,
325
+ detectedProtocol: detectionResult.protocol,
309
326
  message: 'Terminating connection - only TLS traffic is allowed on standard HTTPS port.',
310
327
  component: 'route-handler'
311
328
  });
@@ -318,71 +335,78 @@ export class RouteConnectionHandler {
318
335
  return;
319
336
  }
320
337
 
321
- // Check if this looks like a TLS handshake
338
+ // Extract domain and protocol info
322
339
  let serverName = '';
323
- if (this.smartProxy.tlsManager.isTlsHandshake(chunk)) {
340
+ if (detectionResult.protocol === 'tls') {
324
341
  record.isTLS = true;
342
+ serverName = detectionResult.connectionInfo.domain || '';
343
+
344
+ // Lock the connection to the negotiated SNI
345
+ record.lockedDomain = serverName;
325
346
 
326
- // Check for ClientHello to extract SNI
327
- if (this.smartProxy.tlsManager.isClientHello(chunk)) {
328
- // Create connection info for SNI extraction
329
- const connInfo = {
330
- sourceIp: record.remoteIP,
331
- sourcePort: socket.remotePort || 0,
332
- destIp: socket.localAddress || '',
333
- destPort: socket.localPort || 0,
334
- };
335
-
336
- // Extract SNI
337
- serverName = this.smartProxy.tlsManager.extractSNI(chunk, connInfo) || '';
338
-
339
- // Lock the connection to the negotiated SNI
340
- record.lockedDomain = serverName;
341
-
342
- // Check if we should reject connections without SNI
343
- if (!serverName && this.smartProxy.settings.allowSessionTicket === false) {
344
- logger.log('warn', `No SNI detected in TLS ClientHello for connection ${connectionId}; sending TLS alert`, {
345
- connectionId,
346
- component: 'route-handler'
347
- });
348
- if (record.incomingTerminationReason === null) {
349
- record.incomingTerminationReason = 'session_ticket_blocked_no_sni';
350
- this.smartProxy.connectionManager.incrementTerminationStat(
351
- 'incoming',
352
- 'session_ticket_blocked_no_sni'
353
- );
354
- }
355
- const alert = Buffer.from([0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x70]);
356
- try {
357
- // Count the alert bytes being sent
358
- record.bytesSent += alert.length;
359
- if (this.smartProxy.metricsCollector) {
360
- this.smartProxy.metricsCollector.recordBytes(record.id, 0, alert.length);
361
- }
362
-
363
- socket.cork();
364
- socket.write(alert);
365
- socket.uncork();
366
- socket.end();
367
- } catch {
368
- socket.end();
347
+ // Check if we should reject connections without SNI
348
+ if (!serverName && this.smartProxy.settings.allowSessionTicket === false) {
349
+ logger.log('warn', `No SNI detected in TLS ClientHello for connection ${record.id}; sending TLS alert`, {
350
+ connectionId: record.id,
351
+ component: 'route-handler'
352
+ });
353
+ if (record.incomingTerminationReason === null) {
354
+ record.incomingTerminationReason = 'session_ticket_blocked_no_sni';
355
+ this.smartProxy.connectionManager.incrementTerminationStat(
356
+ 'incoming',
357
+ 'session_ticket_blocked_no_sni'
358
+ );
359
+ }
360
+ const alert = Buffer.from([0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x70]);
361
+ try {
362
+ // Count the alert bytes being sent
363
+ record.bytesSent += alert.length;
364
+ if (this.smartProxy.metricsCollector) {
365
+ this.smartProxy.metricsCollector.recordBytes(record.id, 0, alert.length);
369
366
  }
370
- this.smartProxy.connectionManager.cleanupConnection(record, 'session_ticket_blocked_no_sni');
371
- return;
367
+
368
+ socket.cork();
369
+ socket.write(alert);
370
+ socket.uncork();
371
+ socket.end();
372
+ } catch {
373
+ socket.end();
372
374
  }
375
+ this.smartProxy.connectionManager.cleanupConnection(record, 'session_ticket_blocked_no_sni');
376
+ return;
377
+ }
373
378
 
374
- if (this.smartProxy.settings.enableDetailedLogging) {
375
- logger.log('info', `TLS connection with SNI`, {
376
- connectionId,
377
- serverName: serverName || '(empty)',
378
- component: 'route-handler'
379
- });
380
- }
379
+ if (this.smartProxy.settings.enableDetailedLogging) {
380
+ logger.log('info', `TLS connection with SNI`, {
381
+ connectionId: record.id,
382
+ serverName: serverName || '(empty)',
383
+ component: 'route-handler'
384
+ });
385
+ }
386
+ } else if (detectionResult.protocol === 'http') {
387
+ // For HTTP, extract domain from Host header
388
+ serverName = detectionResult.connectionInfo.domain || '';
389
+
390
+ // Store HTTP-specific info for later use
391
+ record.httpInfo = {
392
+ method: detectionResult.connectionInfo.method,
393
+ path: detectionResult.connectionInfo.path,
394
+ headers: detectionResult.connectionInfo.headers
395
+ };
396
+
397
+ if (this.smartProxy.settings.enableDetailedLogging) {
398
+ logger.log('info', `HTTP connection detected`, {
399
+ connectionId: record.id,
400
+ domain: serverName || '(no host header)',
401
+ method: detectionResult.connectionInfo.method,
402
+ path: detectionResult.connectionInfo.path,
403
+ component: 'route-handler'
404
+ });
381
405
  }
382
406
  }
383
407
 
384
408
  // Find the appropriate route for this connection
385
- this.routeConnection(socket, record, serverName, chunk);
409
+ this.routeConnection(socket, record, serverName, chunk, detectionResult);
386
410
  };
387
411
 
388
412
  // First data handler to capture initial TLS handshake or PROXY protocol
@@ -454,7 +478,8 @@ export class RouteConnectionHandler {
454
478
  socket: plugins.net.Socket | WrappedSocket,
455
479
  record: IConnectionRecord,
456
480
  serverName: string,
457
- initialChunk?: Buffer
481
+ initialChunk?: Buffer,
482
+ detectionResult?: any // Using any temporarily to avoid circular dependency issues
458
483
  ): void {
459
484
  const connectionId = record.id;
460
485
  const localPort = record.localPort;
@@ -635,7 +660,7 @@ export class RouteConnectionHandler {
635
660
  // Handle the route based on its action type
636
661
  switch (route.action.type) {
637
662
  case 'forward':
638
- return this.handleForwardAction(socket, record, route, initialChunk);
663
+ return this.handleForwardAction(socket, record, route, initialChunk, detectionResult);
639
664
 
640
665
  case 'socket-handler':
641
666
  logger.log('info', `Handling socket-handler action for route ${route.name}`, {
@@ -738,7 +763,8 @@ export class RouteConnectionHandler {
738
763
  socket: plugins.net.Socket | WrappedSocket,
739
764
  record: IConnectionRecord,
740
765
  route: IRouteConfig,
741
- initialChunk?: Buffer
766
+ initialChunk?: Buffer,
767
+ detectionResult?: any // Using any temporarily to avoid circular dependency issues
742
768
  ): void {
743
769
  const connectionId = record.id;
744
770
  const action = route.action as IRouteAction;
@@ -819,14 +845,11 @@ export class RouteConnectionHandler {
819
845
  // Create context for target selection
820
846
  const targetSelectionContext = {
821
847
  port: record.localPort,
822
- path: undefined, // Will be populated from HTTP headers if available
823
- headers: undefined, // Will be populated from HTTP headers if available
824
- method: undefined // Will be populated from HTTP headers if available
848
+ path: record.httpInfo?.path,
849
+ headers: record.httpInfo?.headers,
850
+ method: record.httpInfo?.method
825
851
  };
826
852
 
827
- // TODO: Extract path, headers, and method from initialChunk if it's HTTP
828
- // For now, we'll select based on port only
829
-
830
853
  const selectedTarget = this.selectTarget(action.targets, targetSelectionContext);
831
854
  if (!selectedTarget) {
832
855
  logger.log('error', `No matching target found for connection ${connectionId}`, {
@@ -1,5 +1,6 @@
1
1
  import * as plugins from '../../plugins.js';
2
2
  import { SniHandler } from '../../tls/sni/sni-handler.js';
3
+ import { ProtocolDetector, TlsDetector } from '../../detection/index.js';
3
4
  import type { SmartProxy } from './smart-proxy.js';
4
5
 
5
6
  /**
@@ -21,6 +21,7 @@
21
21
  import * as plugins from '../../../plugins.js';
22
22
  import type { IRouteConfig, IRouteMatch, IRouteAction, IRouteTarget, TPortRange, IRouteContext } from '../models/route-types.js';
23
23
  import { mergeRouteConfigs } from './route-utils.js';
24
+ import { ProtocolDetector, HttpDetector } from '../../../detection/index.js';
24
25
 
25
26
  /**
26
27
  * Create an HTTP-only route configuration
@@ -956,83 +957,91 @@ export const SocketHandlers = {
956
957
 
957
958
  /**
958
959
  * HTTP redirect handler
960
+ * Now uses the centralized detection module for HTTP parsing
959
961
  */
960
962
  httpRedirect: (locationTemplate: string, statusCode: number = 301) => (socket: plugins.net.Socket, context: IRouteContext) => {
961
- let buffer = '';
963
+ const connectionId = ProtocolDetector.createConnectionId({
964
+ socketId: context.connectionId || `${Date.now()}-${Math.random()}`
965
+ });
962
966
 
963
- socket.once('data', (data) => {
964
- buffer += data.toString();
965
-
966
- const lines = buffer.split('\r\n');
967
- const requestLine = lines[0];
968
- const [method, path] = requestLine.split(' ');
969
-
970
- const domain = context.domain || 'localhost';
971
- const port = context.port;
972
-
973
- let finalLocation = locationTemplate
974
- .replace('{domain}', domain)
975
- .replace('{port}', String(port))
976
- .replace('{path}', path)
977
- .replace('{clientIp}', context.clientIp);
967
+ socket.once('data', async (data) => {
968
+ // Use detection module for parsing
969
+ const detectionResult = await ProtocolDetector.detectWithConnectionTracking(
970
+ data,
971
+ connectionId,
972
+ { extractFullHeaders: false } // We only need method and path
973
+ );
978
974
 
979
- const message = `Redirecting to ${finalLocation}`;
980
- const response = [
981
- `HTTP/1.1 ${statusCode} ${statusCode === 301 ? 'Moved Permanently' : 'Found'}`,
982
- `Location: ${finalLocation}`,
983
- 'Content-Type: text/plain',
984
- `Content-Length: ${message.length}`,
985
- 'Connection: close',
986
- '',
987
- message
988
- ].join('\r\n');
975
+ if (detectionResult.protocol === 'http' && detectionResult.connectionInfo.path) {
976
+ const method = detectionResult.connectionInfo.method || 'GET';
977
+ const path = detectionResult.connectionInfo.path || '/';
978
+
979
+ const domain = context.domain || 'localhost';
980
+ const port = context.port;
981
+
982
+ let finalLocation = locationTemplate
983
+ .replace('{domain}', domain)
984
+ .replace('{port}', String(port))
985
+ .replace('{path}', path)
986
+ .replace('{clientIp}', context.clientIp);
987
+
988
+ const message = `Redirecting to ${finalLocation}`;
989
+ const response = [
990
+ `HTTP/1.1 ${statusCode} ${statusCode === 301 ? 'Moved Permanently' : 'Found'}`,
991
+ `Location: ${finalLocation}`,
992
+ 'Content-Type: text/plain',
993
+ `Content-Length: ${message.length}`,
994
+ 'Connection: close',
995
+ '',
996
+ message
997
+ ].join('\r\n');
998
+
999
+ socket.write(response);
1000
+ } else {
1001
+ // Not a valid HTTP request, close connection
1002
+ socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
1003
+ }
989
1004
 
990
- socket.write(response);
991
1005
  socket.end();
1006
+ // Clean up detection state
1007
+ ProtocolDetector.cleanupConnections();
992
1008
  });
993
1009
  },
994
1010
 
995
1011
  /**
996
1012
  * HTTP server handler for ACME challenges and other HTTP needs
1013
+ * Now uses the centralized detection module for HTTP parsing
997
1014
  */
998
1015
  httpServer: (handler: (req: { method: string; url: string; headers: Record<string, string>; body?: string }, res: { status: (code: number) => void; header: (name: string, value: string) => void; send: (data: string) => void; end: () => void }) => void) => (socket: plugins.net.Socket, context: IRouteContext) => {
999
- let buffer = '';
1000
1016
  let requestParsed = false;
1017
+ const connectionId = ProtocolDetector.createConnectionId({
1018
+ socketId: context.connectionId || `${Date.now()}-${Math.random()}`
1019
+ });
1001
1020
 
1002
- socket.on('data', (data) => {
1021
+ const processData = async (data: Buffer) => {
1003
1022
  if (requestParsed) return; // Only handle the first request
1004
1023
 
1005
- buffer += data.toString();
1024
+ // Use HttpDetector for parsing
1025
+ const detectionResult = await ProtocolDetector.detectWithConnectionTracking(
1026
+ data,
1027
+ connectionId,
1028
+ { extractFullHeaders: true }
1029
+ );
1006
1030
 
1007
- // Check if we have a complete HTTP request
1008
- const headerEndIndex = buffer.indexOf('\r\n\r\n');
1009
- if (headerEndIndex === -1) return; // Need more data
1031
+ if (detectionResult.protocol !== 'http' || !detectionResult.isComplete) {
1032
+ // Not a complete HTTP request yet
1033
+ return;
1034
+ }
1010
1035
 
1011
1036
  requestParsed = true;
1037
+ const connInfo = detectionResult.connectionInfo;
1012
1038
 
1013
- // Parse the HTTP request
1014
- const headerPart = buffer.substring(0, headerEndIndex);
1015
- const bodyPart = buffer.substring(headerEndIndex + 4);
1016
-
1017
- const lines = headerPart.split('\r\n');
1018
- const [method, url] = lines[0].split(' ');
1019
-
1020
- const headers: Record<string, string> = {};
1021
- for (let i = 1; i < lines.length; i++) {
1022
- const colonIndex = lines[i].indexOf(':');
1023
- if (colonIndex > 0) {
1024
- const name = lines[i].substring(0, colonIndex).trim().toLowerCase();
1025
- const value = lines[i].substring(colonIndex + 1).trim();
1026
- headers[name] = value;
1027
- }
1028
- }
1029
-
1030
- // Create request object
1039
+ // Create request object from detection result
1031
1040
  const req = {
1032
- method: method || 'GET',
1033
- url: url || '/',
1034
- headers,
1035
- body: bodyPart
1041
+ method: connInfo.method || 'GET',
1042
+ url: connInfo.path || '/',
1043
+ headers: connInfo.headers || {},
1044
+ body: detectionResult.remainingBuffer?.toString() || ''
1036
1045
  };
1037
1046
 
1038
1047
  // Create response object
@@ -1093,13 +1102,20 @@ export const SocketHandlers = {
1093
1102
  res.send('Internal Server Error');
1094
1103
  }
1095
1104
  }
1096
- });
1105
+ };
1106
+
1107
+ socket.on('data', processData);
1097
1108
 
1098
1109
  socket.on('error', () => {
1099
1110
  if (!requestParsed) {
1100
1111
  socket.end();
1101
1112
  }
1102
1113
  });
1114
+
1115
+ socket.on('close', () => {
1116
+ // Clean up detection state
1117
+ ProtocolDetector.cleanupConnections();
1118
+ });
1103
1119
  }
1104
1120
  };
1105
1121
 
package/ts/tls/index.ts CHANGED
@@ -1,22 +1,18 @@
1
1
  /**
2
- * TLS module providing SNI extraction, TLS alerts, and other TLS-related utilities
2
+ * TLS module for smartproxy
3
+ * Re-exports protocol components and provides smartproxy-specific functionality
3
4
  */
4
5
 
5
- // Export TLS alert functionality
6
- export * from './alerts/tls-alert.js';
6
+ // Re-export all protocol components from protocols/tls
7
+ export * from '../protocols/tls/index.js';
7
8
 
8
- // Export SNI handling
9
+ // Export smartproxy-specific SNI handler
9
10
  export * from './sni/sni-handler.js';
10
- export * from './sni/sni-extraction.js';
11
- export * from './sni/client-hello-parser.js';
12
-
13
- // Export TLS utilities
14
- export * from './utils/tls-utils.js';
15
11
 
16
12
  // Create a namespace for SNI utilities
17
13
  import { SniHandler } from './sni/sni-handler.js';
18
- import { SniExtraction } from './sni/sni-extraction.js';
19
- import { ClientHelloParser } from './sni/client-hello-parser.js';
14
+ import { SniExtraction } from '../protocols/tls/sni/sni-extraction.js';
15
+ import { ClientHelloParser } from '../protocols/tls/sni/client-hello-parser.js';
20
16
 
21
17
  // Export utility objects for convenience
22
18
  export const SNI = {
@@ -30,4 +26,4 @@ export const SNI = {
30
26
  // Convenience functions
31
27
  extractSNI: SniHandler.extractSNI,
32
28
  processTlsPacket: SniHandler.processTlsPacket,
33
- };
29
+ };
@@ -4,15 +4,15 @@ import {
4
4
  TlsHandshakeType,
5
5
  TlsExtensionType,
6
6
  TlsUtils
7
- } from '../utils/tls-utils.js';
7
+ } from '../../protocols/tls/utils/tls-utils.js';
8
8
  import {
9
9
  ClientHelloParser,
10
10
  type LoggerFunction
11
- } from './client-hello-parser.js';
11
+ } from '../../protocols/tls/sni/client-hello-parser.js';
12
12
  import {
13
13
  SniExtraction,
14
14
  type ConnectionInfo
15
- } from './sni-extraction.js';
15
+ } from '../../protocols/tls/sni/sni-extraction.js';
16
16
 
17
17
  /**
18
18
  * SNI (Server Name Indication) handler for TLS connections.
File without changes
File without changes
File without changes