react-native-nitro-net 0.2.0 → 0.3.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 (83) hide show
  1. package/README.md +70 -12
  2. package/android/libs/arm64-v8a/librust_c_net.so +0 -0
  3. package/android/libs/armeabi-v7a/librust_c_net.so +0 -0
  4. package/android/libs/x86/librust_c_net.so +0 -0
  5. package/android/libs/x86_64/librust_c_net.so +0 -0
  6. package/cpp/HybridHttpParser.hpp +67 -0
  7. package/cpp/HybridNetDriver.hpp +6 -0
  8. package/cpp/HybridNetServerDriver.hpp +7 -0
  9. package/cpp/HybridNetSocketDriver.hpp +27 -0
  10. package/cpp/NetBindings.hpp +15 -0
  11. package/ios/Frameworks/RustCNet.xcframework/Info.plist +5 -5
  12. package/ios/Frameworks/RustCNet.xcframework/ios-arm64/RustCNet.framework/RustCNet +0 -0
  13. package/ios/Frameworks/RustCNet.xcframework/ios-arm64_x86_64-simulator/RustCNet.framework/RustCNet +0 -0
  14. package/lib/Driver.d.ts +1 -0
  15. package/lib/Driver.d.ts.map +1 -0
  16. package/lib/Driver.js +2 -5
  17. package/lib/Net.nitro.d.ts +20 -0
  18. package/lib/Net.nitro.d.ts.map +1 -0
  19. package/lib/Net.nitro.js +4 -7
  20. package/lib/http.d.ts +204 -0
  21. package/lib/http.d.ts.map +1 -0
  22. package/lib/http.js +1126 -0
  23. package/lib/https.d.ts +25 -0
  24. package/lib/https.d.ts.map +1 -0
  25. package/lib/https.js +102 -0
  26. package/lib/index.d.ts +41 -160
  27. package/lib/index.d.ts.map +1 -0
  28. package/lib/index.js +11 -821
  29. package/lib/net.d.ts +197 -0
  30. package/lib/net.d.ts.map +1 -0
  31. package/lib/net.js +875 -0
  32. package/lib/tls.d.ts +23 -1
  33. package/lib/tls.d.ts.map +1 -0
  34. package/lib/tls.js +108 -54
  35. package/nitrogen/generated/android/RustCNet+autolinking.cmake +2 -0
  36. package/nitrogen/generated/android/RustCNetOnLoad.cpp +2 -0
  37. package/nitrogen/generated/android/c++/JHybridHttpParserSpec.cpp +54 -0
  38. package/nitrogen/generated/android/c++/JHybridHttpParserSpec.hpp +65 -0
  39. package/nitrogen/generated/android/c++/JHybridNetDriverSpec.cpp +9 -0
  40. package/nitrogen/generated/android/c++/JHybridNetDriverSpec.hpp +1 -0
  41. package/nitrogen/generated/android/c++/JHybridNetServerDriverSpec.cpp +4 -0
  42. package/nitrogen/generated/android/c++/JHybridNetServerDriverSpec.hpp +1 -0
  43. package/nitrogen/generated/android/c++/JHybridNetSocketDriverSpec.cpp +9 -0
  44. package/nitrogen/generated/android/c++/JHybridNetSocketDriverSpec.hpp +2 -0
  45. package/nitrogen/generated/android/c++/JNetConfig.hpp +7 -3
  46. package/nitrogen/generated/android/kotlin/com/margelo/nitro/net/HybridHttpParserSpec.kt +58 -0
  47. package/nitrogen/generated/android/kotlin/com/margelo/nitro/net/HybridNetDriverSpec.kt +4 -0
  48. package/nitrogen/generated/android/kotlin/com/margelo/nitro/net/HybridNetServerDriverSpec.kt +4 -0
  49. package/nitrogen/generated/android/kotlin/com/margelo/nitro/net/HybridNetSocketDriverSpec.kt +8 -0
  50. package/nitrogen/generated/android/kotlin/com/margelo/nitro/net/NetConfig.kt +6 -3
  51. package/nitrogen/generated/ios/RustCNet-Swift-Cxx-Bridge.cpp +17 -0
  52. package/nitrogen/generated/ios/RustCNet-Swift-Cxx-Bridge.hpp +26 -0
  53. package/nitrogen/generated/ios/RustCNet-Swift-Cxx-Umbrella.hpp +5 -0
  54. package/nitrogen/generated/ios/c++/HybridHttpParserSpecSwift.cpp +11 -0
  55. package/nitrogen/generated/ios/c++/HybridHttpParserSpecSwift.hpp +79 -0
  56. package/nitrogen/generated/ios/c++/HybridNetDriverSpecSwift.hpp +11 -0
  57. package/nitrogen/generated/ios/c++/HybridNetServerDriverSpecSwift.hpp +6 -0
  58. package/nitrogen/generated/ios/c++/HybridNetSocketDriverSpecSwift.hpp +14 -0
  59. package/nitrogen/generated/ios/swift/HybridHttpParserSpec.swift +56 -0
  60. package/nitrogen/generated/ios/swift/HybridHttpParserSpec_cxx.swift +131 -0
  61. package/nitrogen/generated/ios/swift/HybridNetDriverSpec.swift +1 -0
  62. package/nitrogen/generated/ios/swift/HybridNetDriverSpec_cxx.swift +15 -0
  63. package/nitrogen/generated/ios/swift/HybridNetServerDriverSpec.swift +1 -0
  64. package/nitrogen/generated/ios/swift/HybridNetServerDriverSpec_cxx.swift +11 -0
  65. package/nitrogen/generated/ios/swift/HybridNetSocketDriverSpec.swift +2 -0
  66. package/nitrogen/generated/ios/swift/HybridNetSocketDriverSpec_cxx.swift +36 -0
  67. package/nitrogen/generated/ios/swift/NetConfig.swift +19 -1
  68. package/nitrogen/generated/shared/c++/HybridHttpParserSpec.cpp +21 -0
  69. package/nitrogen/generated/shared/c++/HybridHttpParserSpec.hpp +63 -0
  70. package/nitrogen/generated/shared/c++/HybridNetDriverSpec.cpp +1 -0
  71. package/nitrogen/generated/shared/c++/HybridNetDriverSpec.hpp +4 -0
  72. package/nitrogen/generated/shared/c++/HybridNetServerDriverSpec.cpp +1 -0
  73. package/nitrogen/generated/shared/c++/HybridNetServerDriverSpec.hpp +1 -0
  74. package/nitrogen/generated/shared/c++/HybridNetSocketDriverSpec.cpp +2 -0
  75. package/nitrogen/generated/shared/c++/HybridNetSocketDriverSpec.hpp +2 -0
  76. package/nitrogen/generated/shared/c++/NetConfig.hpp +6 -2
  77. package/package.json +9 -7
  78. package/src/Net.nitro.ts +17 -0
  79. package/src/http.ts +1303 -0
  80. package/src/https.ts +125 -0
  81. package/src/index.ts +13 -874
  82. package/src/net.ts +1005 -0
  83. package/src/tls.ts +82 -6
package/src/index.ts CHANGED
@@ -1,879 +1,18 @@
1
- import { Duplex, DuplexOptions } from 'readable-stream'
2
- import { EventEmitter } from 'eventemitter3'
3
- import { Driver } from './Driver'
4
- import type { NetSocketDriver, NetServerDriver, NetConfig } from './Net.nitro'
5
- import { NetSocketEvent, NetServerEvent } from './Net.nitro'
6
- import { Buffer } from 'react-native-nitro-buffer'
7
-
8
- // -----------------------------------------------------------------------------
9
- // Utils
10
- // -----------------------------------------------------------------------------
11
-
12
- function isIP(input: string): number {
13
- // Simple regex check
14
- if (/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/.test(input)) return 4;
15
- // Basic IPv6 check allowing double colons
16
- if (/^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/.test(input)) return 6;
17
- if (/^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$/.test(input)) return 6;
18
- return 0;
19
- }
20
-
21
- function isIPv4(input: string): boolean {
22
- return isIP(input) === 4;
23
- }
24
-
25
- function isIPv6(input: string): boolean {
26
- return isIP(input) === 6;
27
- }
28
- // -----------------------------------------------------------------------------
29
- // Global Configuration
30
- // -----------------------------------------------------------------------------
31
-
32
- let _autoSelectFamilyDefault = 4; // Node default is usually 4/6 independent, but we mock it.
33
- let _isVerbose = false;
34
- let _isInitialized = false;
35
-
36
- function debugLog(message: string) {
37
- if (_isVerbose) {
38
- console.log(`[NET DEBUG] ${message}`);
39
- }
40
- }
41
-
42
- function setVerbose(enabled: boolean): void {
43
- _isVerbose = enabled;
44
- }
45
-
46
- function getDefaultAutoSelectFamily(): number {
47
- return _autoSelectFamilyDefault;
48
- }
49
-
50
- function setDefaultAutoSelectFamily(family: number): void {
51
- if (family !== 4 && family !== 6) throw new Error('Family must be 4 or 6');
52
- _autoSelectFamilyDefault = family;
53
- }
54
-
55
- /**
56
- * Ensures that the network module is initialized.
57
- * If initWithConfig hasn't been called, it will be called with default options.
58
- */
59
- function ensureInitialized(): void {
60
- if (!_isInitialized) {
61
- initWithConfig({});
62
- }
63
- }
64
-
65
- /**
66
- * Initialize the network module with custom configuration.
67
- * Must be called before any socket/server operations, or the config will be ignored.
68
- *
69
- * @param config Configuration options
70
- * @param config.workerThreads Number of worker threads (0 = use CPU core count)
71
- *
72
- * @example
73
- * ```ts
74
- * import { initWithConfig } from 'react-native-nitro-net';
75
- *
76
- * // Initialize with 4 worker threads
77
- * initWithConfig({ workerThreads: 4 });
78
- * ```
79
- */
80
- function initWithConfig(config: NetConfig): void {
81
- _isInitialized = true;
82
- Driver.initWithConfig(config);
83
- }
84
-
85
- // -----------------------------------------------------------------------------
86
- // SocketAddress
87
-
88
- // -----------------------------------------------------------------------------
89
- // SocketAddress
90
- // -----------------------------------------------------------------------------
91
-
92
- export class SocketAddress {
93
- readonly address: string;
94
- readonly family: 'ipv4' | 'ipv6';
95
- readonly port: number;
96
- readonly flowlabel: number;
97
-
98
- constructor(options: { address: string, family?: 'ipv4' | 'ipv6', port: number, flowlabel?: number }) {
99
- this.address = options.address;
100
- this.family = options.family || (isIPv6(options.address) ? 'ipv6' : 'ipv4');
101
- this.port = options.port;
102
- this.flowlabel = options.flowlabel || 0;
103
- }
104
- }
105
-
106
- // -----------------------------------------------------------------------------
107
- // BlockList
108
- // -----------------------------------------------------------------------------
109
-
110
- export class BlockList {
111
- private _rules: Array<{ type: 'address' | 'range' | 'subnet', data: any }> = [];
112
-
113
- addAddress(address: string, family?: 'ipv4' | 'ipv6'): void {
114
- this._rules.push({ type: 'address', data: { address, family: family || (isIPv6(address) ? 'ipv6' : 'ipv4') } });
115
- }
116
-
117
- addRange(start: string, end: string, family?: 'ipv4' | 'ipv6'): void {
118
- this._rules.push({ type: 'range', data: { start, end, family: family || (isIPv6(start) ? 'ipv6' : 'ipv4') } });
119
- }
120
-
121
- addSubnet(net: string, prefix: number, family?: 'ipv4' | 'ipv6'): void {
122
- this._rules.push({ type: 'subnet', data: { net, prefix, family: family || (isIPv6(net) ? 'ipv6' : 'ipv4') } });
123
- }
124
-
125
- check(address: string, family?: 'ipv4' | 'ipv6'): boolean {
126
- const addrFamily = family || (isIPv6(address) ? 'ipv6' : 'ipv4');
127
- const addrNum = addrFamily === 'ipv4' ? ipv4ToLong(address) : null;
128
-
129
- for (const rule of this._rules) {
130
- if (rule.data.family !== addrFamily) continue;
131
-
132
- if (rule.type === 'address') {
133
- if (rule.data.address === address) return true;
134
- } else if (rule.type === 'range' && addrNum !== null) {
135
- const start = ipv4ToLong(rule.data.start);
136
- const end = ipv4ToLong(rule.data.end);
137
- if (addrNum >= start && addrNum <= end) return true;
138
- } else if (rule.type === 'subnet' && addrNum !== null) {
139
- const net = ipv4ToLong(rule.data.net);
140
- const mask = ~(Math.pow(2, 32 - rule.data.prefix) - 1);
141
- if ((addrNum & mask) === (net & mask)) return true;
142
- }
143
- }
144
- return false;
145
- }
146
- }
147
-
148
- function ipv4ToLong(ip: string): number {
149
- return ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet, 10), 0) >>> 0;
150
- }
151
-
152
- // -----------------------------------------------------------------------------
153
- // Socket
154
- // -----------------------------------------------------------------------------
155
-
156
- export interface SocketOptions extends DuplexOptions {
157
- fd?: any;
158
- allowHalfOpen?: boolean;
159
- readable?: boolean;
160
- writable?: boolean;
161
- path?: string;
162
- // Extension for internal use
163
- socketDriver?: NetSocketDriver;
164
- remoteFamily?: string;
165
- }
166
-
167
- export class Socket extends Duplex {
168
- protected _driver: NetSocketDriver | undefined;
169
- public connecting: boolean = false; // Changed from private _connecting
170
- protected _connected: boolean = false;
171
- protected _hadError: boolean = false; // Added
172
- public remoteAddress?: string;
173
- public remotePort?: number;
174
- public remoteFamily?: string;
175
- public localAddress?: string;
176
- public localPort?: number;
177
- public bytesRead: number = 0;
178
- public bytesWritten: number = 0;
179
- public autoSelectFamilyAttemptedAddresses: string[] = [];
180
- private _autoSelectFamily: boolean = false;
181
-
182
- get localFamily(): string {
183
- return this.localAddress && this.localAddress.includes(':') ? 'IPv6' : 'IPv4';
184
- }
185
-
186
- get readyState(): string {
187
- if (this.connecting) return 'opening';
188
- if (this._connected) {
189
- // @ts-ignore
190
- if (this.writable && this.readable) return 'open';
191
- // @ts-ignore
192
- if (this.writable) return 'writeOnly';
193
- // @ts-ignore
194
- if (this.readable) return 'readOnly';
195
- }
196
- return 'closed';
197
- }
198
-
199
- get pending(): boolean {
200
- return this.connecting;
201
- }
202
- constructor(options?: SocketOptions) {
203
- super({
204
- allowHalfOpen: options?.allowHalfOpen ?? false,
205
- readable: options?.readable ?? true,
206
- writable: options?.writable ?? true
207
- });
208
-
209
- if (options?.socketDriver) {
210
- // Wrapping existing socket (from Server)
211
- this._driver = options.socketDriver;
212
- this._connected = true;
213
- this._setupEvents();
214
- // Resume the socket since it starts paused on server-accept
215
- this.resume();
216
- // Emit connect for server-side socket? No, it's already connected.
217
- } else {
218
- // New client socket
219
- ensureInitialized();
220
- this._driver = Driver.createSocket();
221
- this._setupEvents();
222
- // Also resume client socket initially so it's ready to receive
223
- this.resume();
224
- }
225
-
226
- this.on('finish', () => {
227
- // Writable side finished
228
- });
229
- }
230
-
231
- on(event: string | symbol, listener: (...args: any[]) => void): this {
232
- if (event === 'connect' && this._connected) {
233
- process.nextTick(listener);
234
- return this;
235
- }
236
- const ret = super.on(event, listener);
237
- if (event === 'data' && !this.isPaused() && (this as any).readableFlowing !== true) {
238
- debugLog(`Socket on('data'), flowing: ${(this as any).readableFlowing}`);
239
- this.resume();
240
- }
241
- return ret;
242
- }
243
-
244
- private _setupEvents() {
245
- if (!this._driver) return;
246
- const id = (this._driver as any).id ?? (this._driver as any)._id;
247
- this._driver.onEvent = (eventType: number, data: ArrayBuffer) => {
248
- this.emit('event', eventType, data);
249
- if (eventType === 3) { // ERROR
250
- const msg = new TextDecoder().decode(data);
251
- debugLog(`Socket (id: ${id}) NATIVE ERROR: ${msg}`);
252
- }
253
- if (eventType === 9) { // SESSION/DEBUG
254
- debugLog(`Socket (id: ${id}) NATIVE SESSION EVENT RECEIVED`);
255
- this.emit('session', data);
256
- return;
257
- }
258
- debugLog(`Socket (id: ${id}, localPort: ${this.localPort}) Event TYPE: ${eventType}, data len: ${data?.byteLength}`);
259
- switch (eventType) {
260
- case NetSocketEvent.CONNECT:
261
- this.connecting = false;
262
- this._connected = true;
263
- this._updateAddresses();
264
- this.emit('connect');
265
- this.emit('ready');
266
- break;
267
- case NetSocketEvent.DATA:
268
- debugLog(`Socket onEvent(DATA), len: ${data?.byteLength}, flowing: ${(this as any).readableFlowing}`);
269
- if (data && data.byteLength > 0) {
270
- const buffer = Buffer.from(data);
271
- this.bytesRead += buffer.length;
272
- if (!this.push(buffer)) {
273
- this.pause();
274
- }
275
- }
276
- break;
277
- case NetSocketEvent.ERROR: {
278
- this._hadError = true;
279
- const errorMsg = data ? Buffer.from(data).toString() : 'Unknown socket error';
280
- const error = new Error(errorMsg);
281
-
282
- if (this.connecting && this._autoSelectFamily) {
283
- // If we were connecting, this is a connection attempt failure
284
- // We attempt to get the last attempted address if available
285
- const lastAttempt = this.autoSelectFamilyAttemptedAddresses[this.autoSelectFamilyAttemptedAddresses.length - 1];
286
- if (lastAttempt) {
287
- const [ip, port] = lastAttempt.split(':'); // distinct if ipv6?
288
- // Simple parsing for event emission
289
- const family = ip.includes(':') ? 6 : 4;
290
- this.emit('connectionAttemptFailed', ip, parseInt(port || '0', 10), family, error);
291
- }
292
- }
293
-
294
- this.emit('error', error);
295
- this.destroy();
296
- break;
297
- }
298
- case NetSocketEvent.CLOSE:
299
- this._connected = false;
300
- this.connecting = false;
301
- this.push(null); // EOF
302
- this.emit('close', this._hadError);
303
- break;
304
- case NetSocketEvent.DRAIN:
305
- this.emit('drain');
306
- break;
307
- case NetSocketEvent.TIMEOUT:
308
- if (this.connecting && this._autoSelectFamily) {
309
- const lastAttempt = this.autoSelectFamilyAttemptedAddresses[this.autoSelectFamilyAttemptedAddresses.length - 1];
310
- if (lastAttempt) {
311
- const [ip, port] = lastAttempt.split(':');
312
- const family = ip.includes(':') ? 6 : 4;
313
- this.emit('connectionAttemptTimeout', ip, parseInt(port || '0', 10), family);
314
- }
315
- }
316
- this.emit('timeout');
317
- break;
318
- case NetSocketEvent.LOOKUP: {
319
- if (data) {
320
- const lookupStr = Buffer.from(data).toString();
321
- const parts = lookupStr.split(',');
322
- if (parts.length >= 2) {
323
- const [ip, family] = parts;
324
- this.remoteAddress = ip;
325
- this.remoteFamily = family === '6' ? 'IPv6' : 'IPv4';
326
-
327
- // Emit connectionAttempt
328
- // We don't have the port in LOOKUP data usually, but we stored it in this.remotePort (dest)
329
- // actually remotePort might not be set yet if we used _connect with port arg.
330
- // But _connect sets this.remotePort = port.
331
- const port = this.remotePort || 0;
332
- const fam = family === '6' ? 6 : 4;
333
- if (this._autoSelectFamily) {
334
- this.emit('connectionAttempt', ip, port, fam);
335
- }
336
- this.autoSelectFamilyAttemptedAddresses.push(`${ip}:${port}`);
337
- }
338
- const host = parts.length > 2 ? parts[2] : undefined;
339
- this.emit('lookup', null, parts[0], parts[1] ? parseInt(parts[1], 10) : undefined, host);
340
- }
341
- break;
342
- }
343
- }
344
- };
345
- }
346
-
347
-
348
- private _updateAddresses() {
349
- try {
350
- const local = this._driver?.getLocalAddress();
351
- if (local) {
352
- const parts = local.split(':');
353
- if (parts.length >= 2) {
354
- this.localPort = parseInt(parts[parts.length - 1], 10);
355
- this.localAddress = parts.slice(0, parts.length - 1).join(':').replace(/[\[\]]/g, '');
356
- }
357
- }
358
- const remote = this._driver?.getRemoteAddress();
359
- if (remote) {
360
- const parts = remote.split(':');
361
- if (parts.length >= 2) {
362
- this.remotePort = parseInt(parts[parts.length - 1], 10);
363
- this.remoteAddress = parts.slice(0, parts.length - 1).join(':').replace(/[\[\]]/g, '');
364
- this.remoteFamily = this.remoteAddress.includes(':') ? 'IPv6' : 'IPv4';
365
- }
366
- }
367
- } catch (e) {
368
- // Ignore errors for now
369
- }
370
- }
371
-
372
- address(): { port: number; family: string; address: string } | null {
373
- if (!this.localAddress) return null;
374
- return {
375
- port: this.localPort || 0,
376
- family: this.localAddress.includes(':') ? 'IPv6' : 'IPv4',
377
- address: this.localAddress
378
- };
379
- }
380
-
381
- connect(options: any, connectionListener?: () => void): this {
382
- if (typeof options === 'string') {
383
- // Path?
384
- if (isNaN(Number(options))) {
385
- return this._connectUnix(options, connectionListener);
386
- }
387
- }
388
-
389
- if (typeof options === 'number' || typeof options === 'string') {
390
- const port = Number(options);
391
- const host = (arguments.length > 1 && typeof arguments[1] === 'string') ? arguments[1] : 'localhost';
392
- const cb = typeof arguments[1] === 'function' ? arguments[1] : connectionListener;
393
- // Default: Node 20 defaults autoSelectFamily to true
394
- this._autoSelectFamily = true;
395
- return this._connect(port, host, cb || arguments[2]);
396
- }
397
-
398
- if (options.path) {
399
- return this._connectUnix(options.path, connectionListener, options.signal);
400
- }
401
-
402
- const port = options.port;
403
- const host = options.host || 'localhost';
404
-
405
- // Handle autoSelectFamily option
406
- if (typeof options.autoSelectFamily === 'boolean') {
407
- this._autoSelectFamily = options.autoSelectFamily;
408
- } else {
409
- this._autoSelectFamily = true;
410
- }
411
-
412
- return this._connect(port, host, connectionListener, options.signal);
413
- }
414
-
415
- private _connect(port: number, host: string, listener?: () => void, signal?: AbortSignal): this {
416
- this.remotePort = port; // Store intended remote port
417
- if (this.connecting || this._connected) return this;
418
- if (signal?.aborted) {
419
- process.nextTick(() => this.emit('error', new Error('The operation was aborted')));
420
- return this;
421
- }
422
- this.connecting = true;
423
- if (listener) this.once('connect', listener);
424
-
425
- if (signal) {
426
- const abortHandler = () => {
427
- this.destroy(new Error('The operation was aborted'));
428
- };
429
- signal.addEventListener('abort', abortHandler, { once: true });
430
- this.once('connect', () => signal.removeEventListener('abort', abortHandler));
431
- this.once('close', () => signal.removeEventListener('abort', abortHandler));
432
- }
433
-
434
- this._driver?.connect(host, port);
435
- return this;
436
- }
437
-
438
- private _connectUnix(path: string, listener?: () => void, signal?: AbortSignal): this {
439
- if (this.connecting || this._connected) return this;
440
- if (signal?.aborted) {
441
- process.nextTick(() => this.emit('error', new Error('The operation was aborted')));
442
- return this;
443
- }
444
- this.connecting = true;
445
- if (listener) this.once('connect', listener);
446
-
447
- if (signal) {
448
- const abortHandler = () => {
449
- this.destroy(new Error('The operation was aborted'));
450
- };
451
- signal.addEventListener('abort', abortHandler, { once: true });
452
- this.once('connect', () => signal.removeEventListener('abort', abortHandler));
453
- this.once('close', () => signal.removeEventListener('abort', abortHandler));
454
- }
455
-
456
- this._driver?.connectUnix(path);
457
- return this;
458
- }
459
-
460
- end(cb?: () => void): this;
461
- end(chunk: any, cb?: () => void): this;
462
- end(chunk: any, encoding: string, cb?: () => void): this;
463
- end(chunk?: any, encoding?: any, cb?: any): this {
464
- debugLog(`Socket (localPort: ${this.localPort}) .end() called`);
465
- return super.end(chunk, encoding, cb);
466
- }
467
-
468
- _write(chunk: any, encoding: string, callback: (error?: Error | null) => void): void {
469
- if (!this._driver) {
470
- return callback(new Error('Socket not connected'));
471
- }
472
- try {
473
- const buffer = (chunk instanceof Buffer) ? chunk : Buffer.from(chunk, encoding as any);
474
- this.bytesWritten += buffer.length;
475
- const ab = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
476
- debugLog(`Socket _write, len: ${ab.byteLength}`);
477
- this._driver.write(ab);
478
- callback(null);
479
- } catch (err: any) {
480
- callback(err);
481
- }
482
- }
483
-
484
- _read(size: number): void {
485
- if (this._driver) this._driver.resume();
486
- }
487
-
488
- _final(callback: (error?: Error | null) => void): void {
489
- if (this._driver) {
490
- this._driver.shutdown();
491
- }
492
- callback(null);
493
- }
494
-
495
- destroy(reason?: Error): this {
496
- debugLog(`Socket (localPort: ${this.localPort}) .destroy() called, reason: ${reason?.message}`);
497
- return super.destroy(reason);
498
- }
499
-
500
- _destroy(err: Error | null, callback: (error: Error | null) => void) {
501
- debugLog(`Socket (localPort: ${this.localPort}) ._destroy() called`);
502
- this._connected = false;
503
- this.connecting = false;
504
- this.destroyed = true;
505
- if (this._driver) {
506
- this._driver.destroy();
507
- this._driver = undefined;
508
- }
509
- callback(err);
510
- }
511
-
512
- // Standard net.Socket methods
513
- setTimeout(msecs: number, callback?: () => void): this {
514
- if (this._driver) {
515
- this._driver.setTimeout(msecs);
516
- }
517
- if (callback) this.once('timeout', callback);
518
- return this;
519
- }
520
-
521
- /**
522
- * Pause the reading of data. That is, 'data' events will not be emitted.
523
- * Useful to throttle back an upload.
524
- */
525
- pause(): this {
526
- super.pause();
527
- if (this._driver) {
528
- this._driver.pause();
529
- }
530
- return this;
531
- }
532
-
533
- /**
534
- * Resume reading after a call to pause().
535
- */
536
- resume(): this {
537
- const id = (this._driver as any)?.id;
538
- debugLog(`Socket.resume() called, id: ${id}`);
539
- super.resume();
540
- if (this._driver) {
541
- debugLog(`Socket.resume() calling driver.resume(), id: ${id}`);
542
- this._driver.resume();
543
- }
544
- return this;
545
- }
546
-
547
- /**
548
- * Enable/disable the use of Nagle's algorithm.
549
- */
550
- setNoDelay(noDelay?: boolean): this {
551
- this._driver?.setNoDelay(noDelay !== false);
552
- return this;
553
- }
554
-
555
- setKeepAlive(enable?: boolean, initialDelay?: number): this {
556
- this._driver?.setKeepAlive(enable !== false, initialDelay || 0);
557
- return this;
558
- }
559
-
560
- ref(): this { return this; }
561
- unref(): this { return this; }
562
-
563
- /**
564
- * Set the encoding for the socket as a Readable Stream.
565
- * Use 'utf8', 'hex', etc.
566
- */
567
- setEncoding(encoding: BufferEncoding): this {
568
- super.setEncoding(encoding);
569
- return this;
570
- }
571
-
572
- get timeout(): number | undefined {
573
- return undefined; // Not tracked strictly as a property yet
574
- }
575
-
576
- get bufferSize(): number {
577
- return 0; // Deprecated but often accessed
578
- }
579
-
580
- resetAndDestroy(): this {
581
- if (this._driver) {
582
- this._driver.resetAndDestroy();
583
- this._driver = undefined;
584
- }
585
- this._connected = false;
586
- this.connecting = false;
587
- this.destroyed = true;
588
- return this;
589
- }
590
- }
591
-
592
- // -----------------------------------------------------------------------------
593
- // Server
594
- // -----------------------------------------------------------------------------
595
-
596
- export class Server extends EventEmitter {
597
- private _driver: NetServerDriver;
598
- private _sockets = new Set<Socket>();
599
- private _connections: number = 0;
600
-
601
- private _maxConnections: number = 0;
602
- private _dropMaxConnection: boolean = false;
603
-
604
- get maxConnections(): number {
605
- return this._maxConnections;
606
- }
607
-
608
- set maxConnections(value: number) {
609
- this._maxConnections = value;
610
- // We handle maxConnections in JS to support 'drop' event.
611
- // Disable native limit to ensure we receive the connection attempt.
612
- this._driver.maxConnections = 0;
613
- }
614
-
615
- get dropMaxConnection(): boolean {
616
- return this._dropMaxConnection;
617
- }
618
-
619
- set dropMaxConnection(value: boolean) {
620
- this._dropMaxConnection = value;
621
- }
622
-
623
- get listening(): boolean {
624
- // If we have a driver and we assume it's listening if it has been started?
625
- // Actually, checking _driver state might be hard if not exposed.
626
- // But typically 'listening' is true after 'listening' event.
627
- // We can track it with a private flag or by checking address() which returns null if not listening.
628
- return !!this.address();
629
- }
630
-
631
- constructor(options?: any, connectionListener?: (socket: Socket) => void) {
632
- super();
633
- ensureInitialized();
634
- this._driver = Driver.createServer();
635
-
636
- if (typeof options === 'function') {
637
- connectionListener = options;
638
- options = {};
639
- }
640
-
641
- if (connectionListener) {
642
- this.on('connection', connectionListener);
643
- }
644
-
645
- this._driver.onEvent = (eventType: number, data: ArrayBuffer) => {
646
- switch (eventType) {
647
- case NetServerEvent.CONNECTION: {
648
- const payload = data ? Buffer.from(data).toString() : '';
649
- if (payload === 'success') {
650
- this.emit('listening');
651
- } else {
652
- const clientId = payload;
653
- debugLog(`Server connection clientId: '${clientId}', current connections: ${this._connections}, max: ${this._maxConnections}`);
654
- if (clientId) {
655
- // Check maxConnections
656
- if (this._maxConnections > 0 && this._connections >= this._maxConnections) {
657
- debugLog(`Server maxConnections reached (${this._connections} >= ${this._maxConnections}). Dropping connection. clientId: ${clientId}`);
658
-
659
- const socketDriver = Driver.createSocket(clientId);
660
- const socket = new Socket({
661
- socketDriver: socketDriver,
662
- readable: true,
663
- writable: true
664
- });
665
- // @ts-ignore
666
- socket._updateAddresses();
667
-
668
- this.emit('drop', {
669
- localAddress: socket.localAddress,
670
- localPort: socket.localPort,
671
- localFamily: socket.localFamily,
672
- remoteAddress: socket.remoteAddress,
673
- remotePort: socket.remotePort,
674
- remoteFamily: socket.remoteFamily
675
- });
676
-
677
- socket.destroy();
678
- return;
679
- }
680
-
681
- const socketDriver = Driver.createSocket(clientId);
682
- const socket = new Socket({
683
- socketDriver: socketDriver,
684
- readable: true,
685
- writable: true
686
- });
687
-
688
- // Initialize addresses immediately for server-side socket
689
- // @ts-ignore
690
- socket._updateAddresses();
691
- debugLog(`Socket initialized addresses: local=${socket.localAddress}:${socket.localPort}, remote=${socket.remoteAddress}:${socket.remotePort}`);
692
-
693
- // Keep reference to prevent GC
694
- this._sockets.add(socket);
695
- this._connections++;
696
- socket.on('close', () => {
697
- this._connections--;
698
- this._sockets.delete(socket);
699
- });
700
- this.emit('connection', socket);
701
- }
702
- }
703
- break;
704
- }
705
- case NetServerEvent.ERROR:
706
- this.emit('error', new Error(data ? Buffer.from(data).toString() : 'Unknown server error'));
707
- break;
708
- case NetServerEvent.DEBUG: {
709
- debugLog(`Server NATIVE SESSION/DEBUG EVENT RECEIVED`);
710
- this.emit('session', data);
711
- break;
712
- }
713
- case NetServerEvent.CLOSE:
714
- this.emit('close');
715
- break;
716
- }
717
- };
718
- }
719
-
720
-
721
- ref(): this { return this; }
722
- unref(): this { return this; }
723
-
724
- // @ts-ignore
725
- [Symbol.asyncDispose](): Promise<void> {
726
- return new Promise((resolve) => {
727
- this.close(() => resolve());
728
- });
729
- }
730
- listen(port?: any, host?: any, backlog?: any, callback?: any): this {
731
- let _port = 0;
732
- let _host: string | undefined;
733
- let _backlog: number | undefined;
734
- let _path: string | undefined;
735
- let _callback: (() => void) | undefined;
736
- let signal: AbortSignal | undefined;
737
- let ipv6Only = false;
738
- let reusePort = false;
739
- let handle: { fd?: number } | undefined;
740
-
741
- if (typeof port === 'object' && port !== null) {
742
- // Check if it's a handle object with fd property
743
- if (typeof port.fd === 'number') {
744
- handle = port;
745
- _backlog = port.backlog;
746
- _callback = host; // listen(handle, cb)
747
- } else {
748
- _port = port.port;
749
- _host = port.host;
750
- _backlog = port.backlog;
751
- _path = port.path;
752
- signal = port.signal;
753
- ipv6Only = port.ipv6Only === true;
754
- reusePort = port.reusePort === true;
755
- _callback = host; // listen(options, cb)
756
- }
757
- } else {
758
- _port = typeof port === 'number' ? port : (typeof port === 'string' && !isNaN(Number(port)) ? Number(port) : 0);
759
- if (typeof port === 'string' && isNaN(Number(port))) _path = port;
760
-
761
- if (typeof host === 'string') _host = host;
762
- else if (typeof host === 'function') _callback = host;
763
-
764
- if (typeof backlog === 'number') _backlog = backlog;
765
- else if (typeof backlog === 'function') _callback = backlog;
766
-
767
- if (typeof callback === 'function') _callback = callback;
768
- }
769
-
770
- if (_callback) this.once('listening', _callback);
771
-
772
- if (signal?.aborted) {
773
- process.nextTick(() => this.emit('error', new Error('The operation was aborted')));
774
- return this;
775
- }
776
-
777
- if (signal) {
778
- const abortHandler = () => {
779
- this.close();
780
- this.emit('error', new Error('The operation was aborted'));
781
- };
782
- signal.addEventListener('abort', abortHandler, { once: true });
783
- this.once('listening', () => signal.removeEventListener('abort', abortHandler));
784
- this.once('close', () => signal.removeEventListener('abort', abortHandler));
785
- }
786
-
787
- if (handle && typeof handle.fd === 'number') {
788
- // Listen on an existing file descriptor (handle)
789
- this._driver.listenHandle(handle.fd, _backlog);
790
- } else if (_path) {
791
- this._driver.listenUnix(_path, _backlog);
792
- } else {
793
- this._driver.listen(_port || 0, _backlog, ipv6Only, reusePort);
794
- }
795
-
796
- return this;
797
- }
798
-
799
- close(callback?: (err?: Error) => void): this {
800
- // Destroy all active connections first
801
- for (const socket of this._sockets) {
802
- socket.destroy();
803
- }
804
- this._sockets.clear();
805
- this._connections = 0;
806
-
807
- this._driver.close();
808
- if (callback) this.once('close', callback);
809
- return this;
810
- }
811
-
812
- address(): { port: number; family: string; address: string } | null {
813
- try {
814
- const addr = this._driver.getLocalAddress();
815
- if (addr) {
816
- const parts = addr.split(':');
817
- if (parts.length >= 2) {
818
- const port = parseInt(parts[parts.length - 1], 10);
819
- const address = parts.slice(0, parts.length - 1).join(':').replace(/[\[\]]/g, '');
820
- const family = address.includes(':') ? 'IPv6' : 'IPv4';
821
- return { port, family, address };
822
- }
823
- }
824
- } catch (e) {
825
- // Ignore
826
- }
827
- return null;
828
- }
829
-
830
- getConnections(cb: (err: Error | null, count: number) => void): void {
831
- cb(null, this._connections);
832
- }
833
- }
834
-
835
- // -----------------------------------------------------------------------------
836
- // Exports
837
- // -----------------------------------------------------------------------------
838
-
839
- export function createConnection(options: any, connectionListener?: () => void): Socket {
840
- const socket = new Socket(options);
841
- return socket.connect(options, connectionListener);
842
- }
843
-
844
- export const connect = createConnection;
845
-
846
- export function createServer(options?: any, connectionListener?: (socket: Socket) => void): Server {
847
- return new Server(options, connectionListener);
848
- }
849
-
850
- export * as tls from './tls'
1
+ import * as net from './net'
2
+ import * as tls from './tls'
3
+ import * as http from './http'
4
+ import * as https from './https'
851
5
 
6
+ export * from './net'
852
7
  export {
853
- isIP,
854
- isIPv4,
855
- isIPv6,
856
- getDefaultAutoSelectFamily,
857
- setDefaultAutoSelectFamily,
858
- setVerbose,
859
- initWithConfig,
860
- };
861
-
862
- export type { NetConfig };
8
+ tls,
9
+ http,
10
+ https
11
+ }
863
12
 
864
13
  export default {
865
- Socket,
866
- Server,
867
- SocketAddress,
868
- BlockList,
869
- createConnection,
870
- createServer,
871
- connect,
872
- isIP,
873
- isIPv4,
874
- isIPv6,
875
- getDefaultAutoSelectFamily,
876
- setDefaultAutoSelectFamily,
877
- setVerbose,
878
- initWithConfig,
14
+ ...net,
15
+ tls,
16
+ http,
17
+ https,
879
18
  };