@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
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Shared Fragment Handler for Protocol Detection
3
+ *
4
+ * Provides unified fragment buffering and reassembly for protocols
5
+ * that may span multiple TCP packets.
6
+ */
7
+
8
+ import { Buffer } from 'buffer';
9
+
10
+ /**
11
+ * Fragment tracking information
12
+ */
13
+ export interface IFragmentInfo {
14
+ buffer: Buffer;
15
+ timestamp: number;
16
+ connectionId: string;
17
+ }
18
+
19
+ /**
20
+ * Options for fragment handling
21
+ */
22
+ export interface IFragmentOptions {
23
+ maxBufferSize?: number;
24
+ timeout?: number;
25
+ cleanupInterval?: number;
26
+ }
27
+
28
+ /**
29
+ * Result of fragment processing
30
+ */
31
+ export interface IFragmentResult {
32
+ isComplete: boolean;
33
+ buffer?: Buffer;
34
+ needsMoreData: boolean;
35
+ error?: string;
36
+ }
37
+
38
+ /**
39
+ * Shared fragment handler for protocol detection
40
+ */
41
+ export class FragmentHandler {
42
+ private fragments = new Map<string, IFragmentInfo>();
43
+ private cleanupTimer?: NodeJS.Timeout;
44
+
45
+ constructor(private options: IFragmentOptions = {}) {
46
+ // Start cleanup timer if not already running
47
+ if (options.cleanupInterval && !this.cleanupTimer) {
48
+ this.cleanupTimer = setInterval(
49
+ () => this.cleanup(),
50
+ options.cleanupInterval
51
+ );
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Add a fragment for a connection
57
+ */
58
+ addFragment(connectionId: string, fragment: Buffer): IFragmentResult {
59
+ const existing = this.fragments.get(connectionId);
60
+
61
+ if (existing) {
62
+ // Append to existing buffer
63
+ const newBuffer = Buffer.concat([existing.buffer, fragment]);
64
+
65
+ // Check size limit
66
+ const maxSize = this.options.maxBufferSize || 65536;
67
+ if (newBuffer.length > maxSize) {
68
+ this.fragments.delete(connectionId);
69
+ return {
70
+ isComplete: false,
71
+ needsMoreData: false,
72
+ error: 'Buffer size exceeded maximum allowed'
73
+ };
74
+ }
75
+
76
+ // Update fragment info
77
+ this.fragments.set(connectionId, {
78
+ buffer: newBuffer,
79
+ timestamp: Date.now(),
80
+ connectionId
81
+ });
82
+
83
+ return {
84
+ isComplete: false,
85
+ buffer: newBuffer,
86
+ needsMoreData: true
87
+ };
88
+ } else {
89
+ // New fragment
90
+ this.fragments.set(connectionId, {
91
+ buffer: fragment,
92
+ timestamp: Date.now(),
93
+ connectionId
94
+ });
95
+
96
+ return {
97
+ isComplete: false,
98
+ buffer: fragment,
99
+ needsMoreData: true
100
+ };
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Get the current buffer for a connection
106
+ */
107
+ getBuffer(connectionId: string): Buffer | undefined {
108
+ return this.fragments.get(connectionId)?.buffer;
109
+ }
110
+
111
+ /**
112
+ * Mark a connection as complete and clean up
113
+ */
114
+ complete(connectionId: string): void {
115
+ this.fragments.delete(connectionId);
116
+ }
117
+
118
+ /**
119
+ * Check if we're tracking a connection
120
+ */
121
+ hasConnection(connectionId: string): boolean {
122
+ return this.fragments.has(connectionId);
123
+ }
124
+
125
+ /**
126
+ * Clean up expired fragments
127
+ */
128
+ cleanup(): void {
129
+ const now = Date.now();
130
+ const timeout = this.options.timeout || 5000;
131
+
132
+ for (const [connectionId, info] of this.fragments.entries()) {
133
+ if (now - info.timestamp > timeout) {
134
+ this.fragments.delete(connectionId);
135
+ }
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Clear all fragments
141
+ */
142
+ clear(): void {
143
+ this.fragments.clear();
144
+ }
145
+
146
+ /**
147
+ * Destroy the handler and clean up resources
148
+ */
149
+ destroy(): void {
150
+ if (this.cleanupTimer) {
151
+ clearInterval(this.cleanupTimer);
152
+ this.cleanupTimer = undefined;
153
+ }
154
+ this.clear();
155
+ }
156
+
157
+ /**
158
+ * Get the number of tracked connections
159
+ */
160
+ get size(): number {
161
+ return this.fragments.size;
162
+ }
163
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Common Protocol Infrastructure
3
+ *
4
+ * Shared utilities and types for protocol handling
5
+ */
6
+
7
+ export * from './fragment-handler.js';
8
+ export * from './types.js';
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Common Protocol Types
3
+ *
4
+ * Shared types used across different protocol implementations
5
+ */
6
+
7
+ /**
8
+ * Supported protocol types
9
+ */
10
+ export type TProtocolType = 'tls' | 'http' | 'https' | 'websocket' | 'unknown';
11
+
12
+ /**
13
+ * Protocol detection result
14
+ */
15
+ export interface IProtocolDetectionResult {
16
+ protocol: TProtocolType;
17
+ confidence: number; // 0-100
18
+ requiresMoreData?: boolean;
19
+ metadata?: {
20
+ version?: string;
21
+ method?: string;
22
+ [key: string]: any;
23
+ };
24
+ }
25
+
26
+ /**
27
+ * Routing information extracted from protocols
28
+ */
29
+ export interface IRoutingInfo {
30
+ domain?: string;
31
+ port?: number;
32
+ path?: string;
33
+ protocol: TProtocolType;
34
+ }
35
+
36
+ /**
37
+ * Connection context for protocol operations
38
+ */
39
+ export interface IConnectionContext {
40
+ id: string;
41
+ sourceIp?: string;
42
+ sourcePort?: number;
43
+ destIp?: string;
44
+ destPort?: number;
45
+ timestamp?: number;
46
+ }
47
+
48
+ /**
49
+ * Protocol detection options
50
+ */
51
+ export interface IProtocolDetectionOptions {
52
+ quickMode?: boolean; // Only do minimal detection
53
+ extractRouting?: boolean; // Extract routing information
54
+ maxWaitTime?: number; // Max time to wait for complete data
55
+ maxBufferSize?: number; // Max buffer size for fragmented data
56
+ }
57
+
58
+ /**
59
+ * Base interface for protocol detectors
60
+ */
61
+ export interface IProtocolDetector {
62
+ /**
63
+ * Check if this detector can handle the data
64
+ */
65
+ canHandle(data: Buffer): boolean;
66
+
67
+ /**
68
+ * Perform quick detection (first few bytes only)
69
+ */
70
+ quickDetect(data: Buffer): IProtocolDetectionResult;
71
+
72
+ /**
73
+ * Extract routing information if possible
74
+ */
75
+ extractRouting?(data: Buffer, context?: IConnectionContext): IRoutingInfo | null;
76
+ }
@@ -0,0 +1,219 @@
1
+ /**
2
+ * HTTP Protocol Constants
3
+ */
4
+
5
+ /**
6
+ * HTTP methods
7
+ */
8
+ export const HTTP_METHODS = [
9
+ 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS', 'CONNECT', 'TRACE'
10
+ ] as const;
11
+
12
+ export type THttpMethod = typeof HTTP_METHODS[number];
13
+
14
+ /**
15
+ * HTTP version strings
16
+ */
17
+ export const HTTP_VERSIONS = ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2', 'HTTP/3'] as const;
18
+
19
+ export type THttpVersion = typeof HTTP_VERSIONS[number];
20
+
21
+ /**
22
+ * HTTP status codes
23
+ */
24
+ export enum HttpStatus {
25
+ // 1xx Informational
26
+ CONTINUE = 100,
27
+ SWITCHING_PROTOCOLS = 101,
28
+ PROCESSING = 102,
29
+ EARLY_HINTS = 103,
30
+
31
+ // 2xx Success
32
+ OK = 200,
33
+ CREATED = 201,
34
+ ACCEPTED = 202,
35
+ NON_AUTHORITATIVE_INFORMATION = 203,
36
+ NO_CONTENT = 204,
37
+ RESET_CONTENT = 205,
38
+ PARTIAL_CONTENT = 206,
39
+ MULTI_STATUS = 207,
40
+ ALREADY_REPORTED = 208,
41
+ IM_USED = 226,
42
+
43
+ // 3xx Redirection
44
+ MULTIPLE_CHOICES = 300,
45
+ MOVED_PERMANENTLY = 301,
46
+ FOUND = 302,
47
+ SEE_OTHER = 303,
48
+ NOT_MODIFIED = 304,
49
+ USE_PROXY = 305,
50
+ TEMPORARY_REDIRECT = 307,
51
+ PERMANENT_REDIRECT = 308,
52
+
53
+ // 4xx Client Error
54
+ BAD_REQUEST = 400,
55
+ UNAUTHORIZED = 401,
56
+ PAYMENT_REQUIRED = 402,
57
+ FORBIDDEN = 403,
58
+ NOT_FOUND = 404,
59
+ METHOD_NOT_ALLOWED = 405,
60
+ NOT_ACCEPTABLE = 406,
61
+ PROXY_AUTHENTICATION_REQUIRED = 407,
62
+ REQUEST_TIMEOUT = 408,
63
+ CONFLICT = 409,
64
+ GONE = 410,
65
+ LENGTH_REQUIRED = 411,
66
+ PRECONDITION_FAILED = 412,
67
+ PAYLOAD_TOO_LARGE = 413,
68
+ URI_TOO_LONG = 414,
69
+ UNSUPPORTED_MEDIA_TYPE = 415,
70
+ RANGE_NOT_SATISFIABLE = 416,
71
+ EXPECTATION_FAILED = 417,
72
+ IM_A_TEAPOT = 418,
73
+ MISDIRECTED_REQUEST = 421,
74
+ UNPROCESSABLE_ENTITY = 422,
75
+ LOCKED = 423,
76
+ FAILED_DEPENDENCY = 424,
77
+ TOO_EARLY = 425,
78
+ UPGRADE_REQUIRED = 426,
79
+ PRECONDITION_REQUIRED = 428,
80
+ TOO_MANY_REQUESTS = 429,
81
+ REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
82
+ UNAVAILABLE_FOR_LEGAL_REASONS = 451,
83
+
84
+ // 5xx Server Error
85
+ INTERNAL_SERVER_ERROR = 500,
86
+ NOT_IMPLEMENTED = 501,
87
+ BAD_GATEWAY = 502,
88
+ SERVICE_UNAVAILABLE = 503,
89
+ GATEWAY_TIMEOUT = 504,
90
+ HTTP_VERSION_NOT_SUPPORTED = 505,
91
+ VARIANT_ALSO_NEGOTIATES = 506,
92
+ INSUFFICIENT_STORAGE = 507,
93
+ LOOP_DETECTED = 508,
94
+ NOT_EXTENDED = 510,
95
+ NETWORK_AUTHENTICATION_REQUIRED = 511,
96
+ }
97
+
98
+ /**
99
+ * HTTP status text mapping
100
+ */
101
+ export const HTTP_STATUS_TEXT: Record<HttpStatus, string> = {
102
+ // 1xx
103
+ [HttpStatus.CONTINUE]: 'Continue',
104
+ [HttpStatus.SWITCHING_PROTOCOLS]: 'Switching Protocols',
105
+ [HttpStatus.PROCESSING]: 'Processing',
106
+ [HttpStatus.EARLY_HINTS]: 'Early Hints',
107
+
108
+ // 2xx
109
+ [HttpStatus.OK]: 'OK',
110
+ [HttpStatus.CREATED]: 'Created',
111
+ [HttpStatus.ACCEPTED]: 'Accepted',
112
+ [HttpStatus.NON_AUTHORITATIVE_INFORMATION]: 'Non-Authoritative Information',
113
+ [HttpStatus.NO_CONTENT]: 'No Content',
114
+ [HttpStatus.RESET_CONTENT]: 'Reset Content',
115
+ [HttpStatus.PARTIAL_CONTENT]: 'Partial Content',
116
+ [HttpStatus.MULTI_STATUS]: 'Multi-Status',
117
+ [HttpStatus.ALREADY_REPORTED]: 'Already Reported',
118
+ [HttpStatus.IM_USED]: 'IM Used',
119
+
120
+ // 3xx
121
+ [HttpStatus.MULTIPLE_CHOICES]: 'Multiple Choices',
122
+ [HttpStatus.MOVED_PERMANENTLY]: 'Moved Permanently',
123
+ [HttpStatus.FOUND]: 'Found',
124
+ [HttpStatus.SEE_OTHER]: 'See Other',
125
+ [HttpStatus.NOT_MODIFIED]: 'Not Modified',
126
+ [HttpStatus.USE_PROXY]: 'Use Proxy',
127
+ [HttpStatus.TEMPORARY_REDIRECT]: 'Temporary Redirect',
128
+ [HttpStatus.PERMANENT_REDIRECT]: 'Permanent Redirect',
129
+
130
+ // 4xx
131
+ [HttpStatus.BAD_REQUEST]: 'Bad Request',
132
+ [HttpStatus.UNAUTHORIZED]: 'Unauthorized',
133
+ [HttpStatus.PAYMENT_REQUIRED]: 'Payment Required',
134
+ [HttpStatus.FORBIDDEN]: 'Forbidden',
135
+ [HttpStatus.NOT_FOUND]: 'Not Found',
136
+ [HttpStatus.METHOD_NOT_ALLOWED]: 'Method Not Allowed',
137
+ [HttpStatus.NOT_ACCEPTABLE]: 'Not Acceptable',
138
+ [HttpStatus.PROXY_AUTHENTICATION_REQUIRED]: 'Proxy Authentication Required',
139
+ [HttpStatus.REQUEST_TIMEOUT]: 'Request Timeout',
140
+ [HttpStatus.CONFLICT]: 'Conflict',
141
+ [HttpStatus.GONE]: 'Gone',
142
+ [HttpStatus.LENGTH_REQUIRED]: 'Length Required',
143
+ [HttpStatus.PRECONDITION_FAILED]: 'Precondition Failed',
144
+ [HttpStatus.PAYLOAD_TOO_LARGE]: 'Payload Too Large',
145
+ [HttpStatus.URI_TOO_LONG]: 'URI Too Long',
146
+ [HttpStatus.UNSUPPORTED_MEDIA_TYPE]: 'Unsupported Media Type',
147
+ [HttpStatus.RANGE_NOT_SATISFIABLE]: 'Range Not Satisfiable',
148
+ [HttpStatus.EXPECTATION_FAILED]: 'Expectation Failed',
149
+ [HttpStatus.IM_A_TEAPOT]: "I'm a teapot",
150
+ [HttpStatus.MISDIRECTED_REQUEST]: 'Misdirected Request',
151
+ [HttpStatus.UNPROCESSABLE_ENTITY]: 'Unprocessable Entity',
152
+ [HttpStatus.LOCKED]: 'Locked',
153
+ [HttpStatus.FAILED_DEPENDENCY]: 'Failed Dependency',
154
+ [HttpStatus.TOO_EARLY]: 'Too Early',
155
+ [HttpStatus.UPGRADE_REQUIRED]: 'Upgrade Required',
156
+ [HttpStatus.PRECONDITION_REQUIRED]: 'Precondition Required',
157
+ [HttpStatus.TOO_MANY_REQUESTS]: 'Too Many Requests',
158
+ [HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE]: 'Request Header Fields Too Large',
159
+ [HttpStatus.UNAVAILABLE_FOR_LEGAL_REASONS]: 'Unavailable For Legal Reasons',
160
+
161
+ // 5xx
162
+ [HttpStatus.INTERNAL_SERVER_ERROR]: 'Internal Server Error',
163
+ [HttpStatus.NOT_IMPLEMENTED]: 'Not Implemented',
164
+ [HttpStatus.BAD_GATEWAY]: 'Bad Gateway',
165
+ [HttpStatus.SERVICE_UNAVAILABLE]: 'Service Unavailable',
166
+ [HttpStatus.GATEWAY_TIMEOUT]: 'Gateway Timeout',
167
+ [HttpStatus.HTTP_VERSION_NOT_SUPPORTED]: 'HTTP Version Not Supported',
168
+ [HttpStatus.VARIANT_ALSO_NEGOTIATES]: 'Variant Also Negotiates',
169
+ [HttpStatus.INSUFFICIENT_STORAGE]: 'Insufficient Storage',
170
+ [HttpStatus.LOOP_DETECTED]: 'Loop Detected',
171
+ [HttpStatus.NOT_EXTENDED]: 'Not Extended',
172
+ [HttpStatus.NETWORK_AUTHENTICATION_REQUIRED]: 'Network Authentication Required',
173
+ };
174
+
175
+ /**
176
+ * Common HTTP headers
177
+ */
178
+ export const HTTP_HEADERS = {
179
+ // Request headers
180
+ HOST: 'host',
181
+ USER_AGENT: 'user-agent',
182
+ ACCEPT: 'accept',
183
+ ACCEPT_LANGUAGE: 'accept-language',
184
+ ACCEPT_ENCODING: 'accept-encoding',
185
+ AUTHORIZATION: 'authorization',
186
+ CACHE_CONTROL: 'cache-control',
187
+ CONNECTION: 'connection',
188
+ CONTENT_TYPE: 'content-type',
189
+ CONTENT_LENGTH: 'content-length',
190
+ COOKIE: 'cookie',
191
+
192
+ // Response headers
193
+ SET_COOKIE: 'set-cookie',
194
+ LOCATION: 'location',
195
+ SERVER: 'server',
196
+ DATE: 'date',
197
+ EXPIRES: 'expires',
198
+ LAST_MODIFIED: 'last-modified',
199
+ ETAG: 'etag',
200
+
201
+ // CORS headers
202
+ ACCESS_CONTROL_ALLOW_ORIGIN: 'access-control-allow-origin',
203
+ ACCESS_CONTROL_ALLOW_METHODS: 'access-control-allow-methods',
204
+ ACCESS_CONTROL_ALLOW_HEADERS: 'access-control-allow-headers',
205
+
206
+ // Security headers
207
+ STRICT_TRANSPORT_SECURITY: 'strict-transport-security',
208
+ X_CONTENT_TYPE_OPTIONS: 'x-content-type-options',
209
+ X_FRAME_OPTIONS: 'x-frame-options',
210
+ X_XSS_PROTECTION: 'x-xss-protection',
211
+ CONTENT_SECURITY_POLICY: 'content-security-policy',
212
+ } as const;
213
+
214
+ /**
215
+ * Get HTTP status text
216
+ */
217
+ export function getStatusText(status: HttpStatus): string {
218
+ return HTTP_STATUS_TEXT[status] || 'Unknown';
219
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * HTTP Protocol Module
3
+ * Generic HTTP protocol knowledge and parsing utilities
4
+ */
5
+
6
+ export * from './constants.js';
7
+ export * from './types.js';
8
+ export * from './parser.js';
@@ -0,0 +1,219 @@
1
+ /**
2
+ * HTTP Protocol Parser
3
+ * Generic HTTP parsing utilities
4
+ */
5
+
6
+ import { HTTP_METHODS, type THttpMethod, type THttpVersion } from './constants.js';
7
+ import type { IHttpRequestLine, IHttpHeader } from './types.js';
8
+
9
+ /**
10
+ * HTTP parser utilities
11
+ */
12
+ export class HttpParser {
13
+ /**
14
+ * Check if string is a valid HTTP method
15
+ */
16
+ static isHttpMethod(str: string): str is THttpMethod {
17
+ return HTTP_METHODS.includes(str as THttpMethod);
18
+ }
19
+
20
+ /**
21
+ * Parse HTTP request line
22
+ */
23
+ static parseRequestLine(line: string): IHttpRequestLine | null {
24
+ const parts = line.trim().split(' ');
25
+
26
+ if (parts.length !== 3) {
27
+ return null;
28
+ }
29
+
30
+ const [method, path, version] = parts;
31
+
32
+ // Validate method
33
+ if (!this.isHttpMethod(method)) {
34
+ return null;
35
+ }
36
+
37
+ // Validate version
38
+ if (!version.startsWith('HTTP/')) {
39
+ return null;
40
+ }
41
+
42
+ return {
43
+ method: method as THttpMethod,
44
+ path,
45
+ version: version as THttpVersion
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Parse HTTP header line
51
+ */
52
+ static parseHeaderLine(line: string): IHttpHeader | null {
53
+ const colonIndex = line.indexOf(':');
54
+
55
+ if (colonIndex === -1) {
56
+ return null;
57
+ }
58
+
59
+ const name = line.slice(0, colonIndex).trim();
60
+ const value = line.slice(colonIndex + 1).trim();
61
+
62
+ if (!name) {
63
+ return null;
64
+ }
65
+
66
+ return { name, value };
67
+ }
68
+
69
+ /**
70
+ * Parse HTTP headers from lines
71
+ */
72
+ static parseHeaders(lines: string[]): Record<string, string> {
73
+ const headers: Record<string, string> = {};
74
+
75
+ for (const line of lines) {
76
+ const header = this.parseHeaderLine(line);
77
+ if (header) {
78
+ // Convert header names to lowercase for consistency
79
+ headers[header.name.toLowerCase()] = header.value;
80
+ }
81
+ }
82
+
83
+ return headers;
84
+ }
85
+
86
+ /**
87
+ * Extract domain from Host header value
88
+ */
89
+ static extractDomainFromHost(hostHeader: string): string {
90
+ // Remove port if present
91
+ const colonIndex = hostHeader.lastIndexOf(':');
92
+ if (colonIndex !== -1) {
93
+ // Check if it's not part of IPv6 address
94
+ const beforeColon = hostHeader.slice(0, colonIndex);
95
+ if (!beforeColon.includes(']')) {
96
+ return beforeColon;
97
+ }
98
+ }
99
+ return hostHeader;
100
+ }
101
+
102
+ /**
103
+ * Validate domain name
104
+ */
105
+ static isValidDomain(domain: string): boolean {
106
+ // Basic domain validation
107
+ if (!domain || domain.length > 253) {
108
+ return false;
109
+ }
110
+
111
+ // Check for valid characters and structure
112
+ const domainRegex = /^(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.[A-Za-z0-9-]{1,63})*$/;
113
+ return domainRegex.test(domain);
114
+ }
115
+
116
+ /**
117
+ * Extract line from buffer
118
+ */
119
+ static extractLine(buffer: Buffer, offset: number = 0): { line: string; nextOffset: number } | null {
120
+ // Look for CRLF
121
+ const crlfIndex = buffer.indexOf('\r\n', offset);
122
+ if (crlfIndex === -1) {
123
+ // Look for just LF
124
+ const lfIndex = buffer.indexOf('\n', offset);
125
+ if (lfIndex === -1) {
126
+ return null;
127
+ }
128
+
129
+ return {
130
+ line: buffer.slice(offset, lfIndex).toString('utf8'),
131
+ nextOffset: lfIndex + 1
132
+ };
133
+ }
134
+
135
+ return {
136
+ line: buffer.slice(offset, crlfIndex).toString('utf8'),
137
+ nextOffset: crlfIndex + 2
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Check if buffer contains printable ASCII
143
+ */
144
+ static isPrintableAscii(buffer: Buffer, length?: number): boolean {
145
+ const checkLength = Math.min(length || buffer.length, buffer.length);
146
+
147
+ for (let i = 0; i < checkLength; i++) {
148
+ const byte = buffer[i];
149
+ // Allow printable ASCII (32-126) plus tab (9), LF (10), and CR (13)
150
+ if (byte < 32 || byte > 126) {
151
+ if (byte !== 9 && byte !== 10 && byte !== 13) {
152
+ return false;
153
+ }
154
+ }
155
+ }
156
+
157
+ return true;
158
+ }
159
+
160
+ /**
161
+ * Quick check if buffer starts with HTTP method
162
+ */
163
+ static quickCheck(buffer: Buffer): boolean {
164
+ if (buffer.length < 3) {
165
+ return false;
166
+ }
167
+
168
+ // Check common HTTP methods
169
+ const start = buffer.slice(0, 7).toString('ascii');
170
+ return start.startsWith('GET ') ||
171
+ start.startsWith('POST ') ||
172
+ start.startsWith('PUT ') ||
173
+ start.startsWith('DELETE ') ||
174
+ start.startsWith('HEAD ') ||
175
+ start.startsWith('OPTIONS') ||
176
+ start.startsWith('PATCH ') ||
177
+ start.startsWith('CONNECT') ||
178
+ start.startsWith('TRACE ');
179
+ }
180
+
181
+ /**
182
+ * Parse query string
183
+ */
184
+ static parseQueryString(queryString: string): Record<string, string> {
185
+ const params: Record<string, string> = {};
186
+
187
+ if (!queryString) {
188
+ return params;
189
+ }
190
+
191
+ // Remove leading '?' if present
192
+ if (queryString.startsWith('?')) {
193
+ queryString = queryString.slice(1);
194
+ }
195
+
196
+ const pairs = queryString.split('&');
197
+ for (const pair of pairs) {
198
+ const [key, value] = pair.split('=');
199
+ if (key) {
200
+ params[decodeURIComponent(key)] = value ? decodeURIComponent(value) : '';
201
+ }
202
+ }
203
+
204
+ return params;
205
+ }
206
+
207
+ /**
208
+ * Build query string from params
209
+ */
210
+ static buildQueryString(params: Record<string, string>): string {
211
+ const pairs: string[] = [];
212
+
213
+ for (const [key, value] of Object.entries(params)) {
214
+ pairs.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
215
+ }
216
+
217
+ return pairs.length > 0 ? '?' + pairs.join('&') : '';
218
+ }
219
+ }