react-native-nitro-net 0.1.5 → 0.3.0

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 (71) hide show
  1. package/README.md +122 -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 +74 -0
  8. package/cpp/HybridNetServerDriver.hpp +16 -0
  9. package/cpp/HybridNetSocketDriver.hpp +176 -0
  10. package/cpp/NetBindings.hpp +67 -1
  11. package/ios/Frameworks/RustCNet.xcframework/ios-arm64/RustCNet.framework/RustCNet +0 -0
  12. package/ios/Frameworks/RustCNet.xcframework/ios-arm64_x86_64-simulator/RustCNet.framework/RustCNet +0 -0
  13. package/lib/Net.nitro.d.ts +46 -1
  14. package/lib/Net.nitro.js +3 -1
  15. package/lib/http.d.ts +203 -0
  16. package/lib/http.js +1138 -0
  17. package/lib/https.d.ts +24 -0
  18. package/lib/https.js +144 -0
  19. package/lib/index.d.ts +50 -11
  20. package/lib/index.js +179 -31
  21. package/lib/tls.d.ts +145 -0
  22. package/lib/tls.js +521 -0
  23. package/nitrogen/generated/android/RustCNet+autolinking.cmake +2 -0
  24. package/nitrogen/generated/android/RustCNetOnLoad.cpp +2 -0
  25. package/nitrogen/generated/android/c++/JHybridHttpParserSpec.cpp +54 -0
  26. package/nitrogen/generated/android/c++/JHybridHttpParserSpec.hpp +65 -0
  27. package/nitrogen/generated/android/c++/JHybridNetDriverSpec.cpp +47 -1
  28. package/nitrogen/generated/android/c++/JHybridNetDriverSpec.hpp +9 -0
  29. package/nitrogen/generated/android/c++/JHybridNetServerDriverSpec.cpp +8 -0
  30. package/nitrogen/generated/android/c++/JHybridNetServerDriverSpec.hpp +2 -0
  31. package/nitrogen/generated/android/c++/JHybridNetSocketDriverSpec.cpp +79 -0
  32. package/nitrogen/generated/android/c++/JHybridNetSocketDriverSpec.hpp +17 -0
  33. package/nitrogen/generated/android/c++/JNetConfig.hpp +7 -3
  34. package/nitrogen/generated/android/kotlin/com/margelo/nitro/net/HybridHttpParserSpec.kt +58 -0
  35. package/nitrogen/generated/android/kotlin/com/margelo/nitro/net/HybridNetDriverSpec.kt +37 -0
  36. package/nitrogen/generated/android/kotlin/com/margelo/nitro/net/HybridNetServerDriverSpec.kt +8 -0
  37. package/nitrogen/generated/android/kotlin/com/margelo/nitro/net/HybridNetSocketDriverSpec.kt +68 -0
  38. package/nitrogen/generated/android/kotlin/com/margelo/nitro/net/NetConfig.kt +6 -3
  39. package/nitrogen/generated/ios/RustCNet-Swift-Cxx-Bridge.cpp +17 -0
  40. package/nitrogen/generated/ios/RustCNet-Swift-Cxx-Bridge.hpp +118 -41
  41. package/nitrogen/generated/ios/RustCNet-Swift-Cxx-Umbrella.hpp +5 -0
  42. package/nitrogen/generated/ios/c++/HybridHttpParserSpecSwift.cpp +11 -0
  43. package/nitrogen/generated/ios/c++/HybridHttpParserSpecSwift.hpp +79 -0
  44. package/nitrogen/generated/ios/c++/HybridNetDriverSpecSwift.hpp +69 -0
  45. package/nitrogen/generated/ios/c++/HybridNetServerDriverSpecSwift.hpp +12 -0
  46. package/nitrogen/generated/ios/c++/HybridNetSocketDriverSpecSwift.hpp +123 -0
  47. package/nitrogen/generated/ios/swift/HybridHttpParserSpec.swift +56 -0
  48. package/nitrogen/generated/ios/swift/HybridHttpParserSpec_cxx.swift +131 -0
  49. package/nitrogen/generated/ios/swift/HybridNetDriverSpec.swift +9 -0
  50. package/nitrogen/generated/ios/swift/HybridNetDriverSpec_cxx.swift +133 -0
  51. package/nitrogen/generated/ios/swift/HybridNetServerDriverSpec.swift +2 -0
  52. package/nitrogen/generated/ios/swift/HybridNetServerDriverSpec_cxx.swift +36 -0
  53. package/nitrogen/generated/ios/swift/HybridNetSocketDriverSpec.swift +17 -0
  54. package/nitrogen/generated/ios/swift/HybridNetSocketDriverSpec_cxx.swift +314 -0
  55. package/nitrogen/generated/ios/swift/NetConfig.swift +19 -1
  56. package/nitrogen/generated/shared/c++/HybridHttpParserSpec.cpp +21 -0
  57. package/nitrogen/generated/shared/c++/HybridHttpParserSpec.hpp +63 -0
  58. package/nitrogen/generated/shared/c++/HybridNetDriverSpec.cpp +9 -0
  59. package/nitrogen/generated/shared/c++/HybridNetDriverSpec.hpp +13 -0
  60. package/nitrogen/generated/shared/c++/HybridNetServerDriverSpec.cpp +2 -0
  61. package/nitrogen/generated/shared/c++/HybridNetServerDriverSpec.hpp +2 -0
  62. package/nitrogen/generated/shared/c++/HybridNetSocketDriverSpec.cpp +17 -0
  63. package/nitrogen/generated/shared/c++/HybridNetSocketDriverSpec.hpp +18 -0
  64. package/nitrogen/generated/shared/c++/NetConfig.hpp +6 -2
  65. package/package.json +7 -5
  66. package/react-native-nitro-net.podspec +1 -3
  67. package/src/Net.nitro.ts +44 -1
  68. package/src/http.ts +1304 -0
  69. package/src/https.ts +127 -0
  70. package/src/index.ts +167 -27
  71. package/src/tls.ts +608 -0
package/lib/tls.d.ts ADDED
@@ -0,0 +1,145 @@
1
+ import { Socket, Server as NetServer, SocketOptions } from './index';
2
+ export interface PeerCertificate {
3
+ subject: {
4
+ [key: string]: string;
5
+ };
6
+ issuer: {
7
+ [key: string]: string;
8
+ };
9
+ valid_from: string;
10
+ valid_to: string;
11
+ fingerprint: string;
12
+ fingerprint256: string;
13
+ serialNumber: string;
14
+ }
15
+ export interface ConnectionOptions extends SocketOptions {
16
+ host?: string;
17
+ port?: number;
18
+ path?: string;
19
+ servername?: string;
20
+ rejectUnauthorized?: boolean;
21
+ session?: ArrayBuffer;
22
+ secureContext?: SecureContext;
23
+ ca?: string | string[];
24
+ cert?: string | string[];
25
+ key?: string | string[];
26
+ pfx?: string | ArrayBuffer;
27
+ passphrase?: string;
28
+ keylog?: boolean;
29
+ /**
30
+ * Custom hostname verification function.
31
+ * If provided, it will be called after the TLS handshake to verify the peer certificate.
32
+ * Return `undefined` if valid, or an `Error` if invalid.
33
+ * If not provided, the default `checkServerIdentity` is used.
34
+ */
35
+ checkServerIdentity?: (hostname: string, cert: PeerCertificate) => Error | undefined;
36
+ }
37
+ export interface SecureContextOptions {
38
+ pfx?: string | ArrayBuffer;
39
+ passphrase?: string;
40
+ cert?: string | string[];
41
+ key?: string | string[];
42
+ ca?: string | string[];
43
+ }
44
+ export declare const DEFAULT_MIN_VERSION = "TLSv1.2";
45
+ export declare const DEFAULT_MAX_VERSION = "TLSv1.3";
46
+ export declare const rootCertificates: string[];
47
+ export declare const DEFAULT_ECDH_CURVE = "auto";
48
+ export declare const SLAB_BUFFER_SIZE: number;
49
+ export declare class SecureContext {
50
+ private _id;
51
+ constructor(options?: SecureContextOptions);
52
+ setOCSPResponse(ocsp: ArrayBuffer): void;
53
+ getTicketKeys(): ArrayBuffer | undefined;
54
+ setTicketKeys(keys: ArrayBuffer): void;
55
+ get id(): number;
56
+ addCACert(ca: string): void;
57
+ }
58
+ export declare function createSecureContext(options?: SecureContextOptions): SecureContext;
59
+ export declare class TLSSocket extends Socket {
60
+ private _servername?;
61
+ get encrypted(): boolean;
62
+ get servername(): string | undefined;
63
+ get authorized(): boolean;
64
+ get authorizationError(): string | undefined;
65
+ get alpnProtocol(): string | undefined;
66
+ getProtocol(): string | undefined;
67
+ getCipher(): {
68
+ name: string;
69
+ version: string;
70
+ } | undefined;
71
+ getPeerCertificate(detailed?: boolean): PeerCertificate | {};
72
+ isSessionReused(): boolean;
73
+ getSession(): ArrayBuffer | undefined;
74
+ getEphemeralKeyInfo(): string | undefined;
75
+ getFinished(): Buffer | undefined;
76
+ getPeerFinished(): Buffer | undefined;
77
+ getSharedSigalgs(): string | undefined;
78
+ renegotiate(options: any, callback: (err: Error | null) => void): boolean;
79
+ disableRenegotiation(): void;
80
+ /**
81
+ * Enables trace output for this socket.
82
+ */
83
+ enableTrace(): void;
84
+ /**
85
+ * Exports keying material for use by external protocols.
86
+ *
87
+ * @param length The number of bytes to return.
88
+ * @param label A label identifying the keying material.
89
+ * @param context An optional context.
90
+ * @returns Buffer containing keying material.
91
+ * @throws Error if export fails (e.g., TLS not connected).
92
+ */
93
+ exportKeyingMaterial(length: number, label: string, context?: Buffer): Buffer;
94
+ constructor(socket: Socket, options?: ConnectionOptions);
95
+ constructor(options: ConnectionOptions);
96
+ connect(options: any, connectionListener?: () => void): this;
97
+ }
98
+ export declare function connect(options: ConnectionOptions, connectionListener?: () => void): TLSSocket;
99
+ export declare function connect(port: number, host?: string, options?: ConnectionOptions, connectionListener?: () => void): TLSSocket;
100
+ export declare function connect(port: number, options?: ConnectionOptions, connectionListener?: () => void): TLSSocket;
101
+ export declare class Server extends NetServer {
102
+ private _secureContextId;
103
+ constructor(options?: any, connectionListener?: (socket: Socket) => void);
104
+ addContext(hostname: string, context: {
105
+ key: string;
106
+ cert: string;
107
+ }): void;
108
+ setSecureContext(options: {
109
+ key: string;
110
+ cert: string;
111
+ ca?: string | string[];
112
+ }): void;
113
+ getTicketKeys(): ArrayBuffer | undefined;
114
+ setTicketKeys(keys: ArrayBuffer): void;
115
+ listen(port?: any, host?: any, backlog?: any, callback?: any): this;
116
+ }
117
+ export declare function createServer(options?: any, connectionListener?: (socket: Socket) => void): Server;
118
+ export declare function getCiphers(): string[];
119
+ export declare function checkServerIdentity(hostname: string, cert: PeerCertificate): Error | undefined;
120
+ /**
121
+ * Legacy CryptoStream for Node.js compatibility.
122
+ * In this implementation, it's a simple wrapper around TLSSocket.
123
+ */
124
+ export declare class CryptoStream extends TLSSocket {
125
+ constructor(options?: ConnectionOptions);
126
+ }
127
+ /**
128
+ * Legacy SecurePair for Node.js compatibility.
129
+ */
130
+ export declare class SecurePair {
131
+ cleartext: CryptoStream;
132
+ encrypted: CryptoStream;
133
+ constructor(secureContext?: SecureContext, isServer?: boolean, requestCert?: boolean, rejectUnauthorized?: boolean);
134
+ }
135
+ export declare function createSecurePair(secureContext?: SecureContext, isServer?: boolean, requestCert?: boolean, rejectUnauthorized?: boolean): SecurePair;
136
+ /**
137
+ * Legacy certificate string parser.
138
+ */
139
+ export declare function parseCertString(certString: string): {
140
+ [key: string]: string;
141
+ };
142
+ /**
143
+ * Mock implementation of convertTLSV1CertToPEM.
144
+ */
145
+ export declare function convertTLSV1CertToPEM(cert: string | Buffer): string;
package/lib/tls.js ADDED
@@ -0,0 +1,521 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SecurePair = exports.CryptoStream = exports.Server = exports.TLSSocket = exports.SecureContext = exports.SLAB_BUFFER_SIZE = exports.DEFAULT_ECDH_CURVE = exports.rootCertificates = exports.DEFAULT_MAX_VERSION = exports.DEFAULT_MIN_VERSION = void 0;
4
+ exports.createSecureContext = createSecureContext;
5
+ exports.connect = connect;
6
+ exports.createServer = createServer;
7
+ exports.getCiphers = getCiphers;
8
+ exports.checkServerIdentity = checkServerIdentity;
9
+ exports.createSecurePair = createSecurePair;
10
+ exports.parseCertString = parseCertString;
11
+ exports.convertTLSV1CertToPEM = convertTLSV1CertToPEM;
12
+ const index_1 = require("./index");
13
+ const Driver_1 = require("./Driver");
14
+ function debugLog(message) {
15
+ if ((0, index_1.isVerbose)()) {
16
+ const timestamp = new Date().toISOString().split('T')[1].split('Z')[0];
17
+ console.log(`[NET DEBUG ${timestamp}] ${message}`);
18
+ }
19
+ }
20
+ exports.DEFAULT_MIN_VERSION = 'TLSv1.2';
21
+ exports.DEFAULT_MAX_VERSION = 'TLSv1.3';
22
+ exports.rootCertificates = [];
23
+ exports.DEFAULT_ECDH_CURVE = 'auto'; // Managed by rustls
24
+ exports.SLAB_BUFFER_SIZE = 10 * 1024 * 1024; // 10MB default
25
+ class SecureContext {
26
+ constructor(options) {
27
+ if (options && options.pfx) {
28
+ this._id = Driver_1.Driver.createEmptySecureContext();
29
+ const pfx = typeof options.pfx === 'string' ? Buffer.from(options.pfx).buffer : options.pfx;
30
+ Driver_1.Driver.setPFXToSecureContext(this._id, pfx, options.passphrase);
31
+ }
32
+ else if (options && options.cert && options.key) {
33
+ const cert = Array.isArray(options.cert) ? options.cert[0] : options.cert;
34
+ const key = Array.isArray(options.key) ? options.key[0] : options.key;
35
+ this._id = Driver_1.Driver.createSecureContext(cert, key, options.passphrase);
36
+ }
37
+ else {
38
+ this._id = Driver_1.Driver.createEmptySecureContext();
39
+ }
40
+ if (options && options.ca) {
41
+ const cas = Array.isArray(options.ca) ? options.ca : [options.ca];
42
+ for (const ca of cas) {
43
+ Driver_1.Driver.addCACertToSecureContext(this._id, ca);
44
+ }
45
+ }
46
+ }
47
+ setOCSPResponse(ocsp) {
48
+ Driver_1.Driver.setOCSPResponseToSecureContext(this._id, ocsp);
49
+ }
50
+ getTicketKeys() {
51
+ return Driver_1.Driver.getTicketKeys(this._id);
52
+ }
53
+ setTicketKeys(keys) {
54
+ Driver_1.Driver.setTicketKeys(this._id, keys);
55
+ }
56
+ get id() {
57
+ return this._id;
58
+ }
59
+ // Node.js doesn't have these on SecureContext but we might need them
60
+ addCACert(ca) {
61
+ Driver_1.Driver.addCACertToSecureContext(this._id, ca);
62
+ }
63
+ }
64
+ exports.SecureContext = SecureContext;
65
+ function createSecureContext(options) {
66
+ return new SecureContext(options);
67
+ }
68
+ class TLSSocket extends index_1.Socket {
69
+ get encrypted() {
70
+ return true;
71
+ }
72
+ get servername() {
73
+ return this._servername;
74
+ }
75
+ get authorized() {
76
+ const driver = this._driver;
77
+ return driver.getAuthorizationError() === undefined;
78
+ }
79
+ get authorizationError() {
80
+ const driver = this._driver;
81
+ return driver.getAuthorizationError();
82
+ }
83
+ get alpnProtocol() {
84
+ const driver = this._driver;
85
+ return driver.getALPN();
86
+ }
87
+ getProtocol() {
88
+ const driver = this._driver;
89
+ return driver.getProtocol();
90
+ }
91
+ getCipher() {
92
+ const driver = this._driver;
93
+ const cipher = driver.getCipher();
94
+ const protocol = driver.getProtocol();
95
+ if (cipher) {
96
+ return {
97
+ name: cipher,
98
+ version: protocol || 'Unknown'
99
+ };
100
+ }
101
+ return undefined;
102
+ }
103
+ getPeerCertificate(detailed) {
104
+ const driver = this._driver;
105
+ const json = driver.getPeerCertificateJSON();
106
+ if (json) {
107
+ try {
108
+ return JSON.parse(json);
109
+ }
110
+ catch (e) {
111
+ console.error('Failed to parse peer certificate JSON', e);
112
+ }
113
+ }
114
+ return {};
115
+ }
116
+ isSessionReused() {
117
+ const driver = this._driver;
118
+ return driver.isSessionReused();
119
+ }
120
+ getSession() {
121
+ const driver = this._driver;
122
+ return driver.getSession();
123
+ }
124
+ getEphemeralKeyInfo() {
125
+ const driver = this._driver;
126
+ return driver.getEphemeralKeyInfo();
127
+ }
128
+ getFinished() {
129
+ throw new Error('getFinished is not supported by rustls');
130
+ }
131
+ getPeerFinished() {
132
+ throw new Error('getPeerFinished is not supported by rustls');
133
+ }
134
+ getSharedSigalgs() {
135
+ const driver = this._driver;
136
+ return driver.getSharedSigalgs();
137
+ }
138
+ renegotiate(options, callback) {
139
+ if (callback) {
140
+ setTimeout(() => {
141
+ const err = new Error('Renegotiation is not supported by rustls');
142
+ err.code = 'ERR_TLS_RENEGOTIATION_DISABLED';
143
+ callback(err);
144
+ }, 0);
145
+ }
146
+ return false;
147
+ }
148
+ disableRenegotiation() {
149
+ // No-op, already effectively disabled
150
+ }
151
+ /**
152
+ * Enables trace output for this socket.
153
+ */
154
+ enableTrace() {
155
+ const driver = this._driver;
156
+ if (driver) {
157
+ driver.enableTrace();
158
+ }
159
+ }
160
+ /**
161
+ * Exports keying material for use by external protocols.
162
+ *
163
+ * @param length The number of bytes to return.
164
+ * @param label A label identifying the keying material.
165
+ * @param context An optional context.
166
+ * @returns Buffer containing keying material.
167
+ * @throws Error if export fails (e.g., TLS not connected).
168
+ */
169
+ exportKeyingMaterial(length, label, context) {
170
+ const driver = this._driver;
171
+ if (driver) {
172
+ const ctx = context ? new Uint8Array(context).buffer : undefined;
173
+ const result = driver.exportKeyingMaterial(length, label, ctx);
174
+ if (result) {
175
+ return Buffer.from(result);
176
+ }
177
+ }
178
+ const err = new Error('exportKeyingMaterial failed: TLS connection may not be established');
179
+ err.code = 'ERR_TLS_EXPORT_KEYING_MATERIAL';
180
+ throw err;
181
+ }
182
+ constructor(socketOrOptions, options) {
183
+ let opts = {};
184
+ if (socketOrOptions instanceof index_1.Socket) {
185
+ opts = { ...options, socketDriver: socketOrOptions._driver };
186
+ }
187
+ else {
188
+ opts = socketOrOptions || {};
189
+ }
190
+ super(opts);
191
+ if (socketOrOptions instanceof index_1.Socket) {
192
+ this._servername = socketOrOptions._servername;
193
+ }
194
+ this.on('event', (event, data) => {
195
+ if (event === 10 && data) { // KEYLOG
196
+ this.emit('keylog', Buffer.from(data));
197
+ }
198
+ else if (event === 11 && data) { // OCSP
199
+ this.emit('OCSPResponse', Buffer.from(data));
200
+ }
201
+ });
202
+ }
203
+ connect(options, connectionListener) {
204
+ // Override connect to use connectTLS
205
+ const port = typeof options === 'number' ? options : options.port;
206
+ const host = (typeof options === 'object' && options.host) ? options.host : (typeof options === 'string' ? arguments[1] : 'localhost');
207
+ const path = (typeof options === 'object' && options.path) ? options.path : undefined;
208
+ const servername = (typeof options === 'object' && options.servername) ? options.servername : (path ? 'localhost' : host);
209
+ this._servername = servername;
210
+ const rejectUnauthorized = (typeof options === 'object' && options.rejectUnauthorized !== undefined) ? options.rejectUnauthorized : true;
211
+ const session = (typeof options === 'object' && options.session) ? options.session : undefined;
212
+ const driver = this._driver;
213
+ if (driver) {
214
+ this.connecting = true;
215
+ if (connectionListener)
216
+ this.once('secureConnect', connectionListener);
217
+ this.once('connect', () => {
218
+ // After the native TLS handshake, perform hostname verification
219
+ if (rejectUnauthorized !== false) {
220
+ const cert = this.getPeerCertificate();
221
+ if (cert && Object.keys(cert).length > 0) {
222
+ const verifyFn = (typeof options === 'object' && options.checkServerIdentity)
223
+ ? options.checkServerIdentity
224
+ : checkServerIdentity;
225
+ const verifyErr = verifyFn(servername, cert);
226
+ if (verifyErr) {
227
+ this.emit('error', verifyErr);
228
+ this.destroy(verifyErr);
229
+ return;
230
+ }
231
+ }
232
+ }
233
+ this.emit('secureConnect');
234
+ });
235
+ if (session) {
236
+ driver.setSession(session);
237
+ }
238
+ const secureContext = (typeof options === 'object' && options.secureContext) ? options.secureContext : undefined;
239
+ let secureContextId = secureContext ? secureContext.id : undefined;
240
+ // If cert/key/ca provided directly, create a temporary secure context
241
+ if (!secureContextId && typeof options === 'object' && (options.cert || options.key || options.ca)) {
242
+ secureContextId = createSecureContext({
243
+ cert: options.cert,
244
+ key: options.key,
245
+ ca: options.ca
246
+ }).id;
247
+ }
248
+ if (options && options.keylog) {
249
+ driver.enableKeylog();
250
+ }
251
+ if (path) {
252
+ if (secureContextId !== undefined) {
253
+ debugLog(`TLSSocket.connect: Calling driver.connectUnixTLSWithContext(${path}, ${servername}, ctx=${secureContextId})`);
254
+ driver.connectUnixTLSWithContext(path, servername, rejectUnauthorized, secureContextId);
255
+ }
256
+ else {
257
+ debugLog(`TLSSocket.connect: Calling driver.connectUnixTLS(${path}, ${servername})`);
258
+ driver.connectUnixTLS(path, servername, rejectUnauthorized);
259
+ }
260
+ }
261
+ else {
262
+ if (secureContextId !== undefined) {
263
+ debugLog(`TLSSocket.connect: Calling driver.connectTLSWithContext(${host}, ${port}, ${servername}, ctx=${secureContextId})`);
264
+ driver.connectTLSWithContext(host, port, servername, rejectUnauthorized, secureContextId);
265
+ }
266
+ else {
267
+ debugLog(`TLSSocket.connect: Calling driver.connectTLS(${host}, ${port}, ${servername})`);
268
+ driver.connectTLS(host, port, servername, rejectUnauthorized);
269
+ }
270
+ }
271
+ }
272
+ return this;
273
+ }
274
+ }
275
+ exports.TLSSocket = TLSSocket;
276
+ function connect(...args) {
277
+ let port;
278
+ let host = 'localhost';
279
+ let options = {};
280
+ let listener;
281
+ if (typeof args[0] === 'object') {
282
+ options = args[0];
283
+ port = options.port || 443;
284
+ host = options.host || 'localhost';
285
+ listener = args[1];
286
+ }
287
+ else {
288
+ port = args[0];
289
+ if (typeof args[1] === 'string') {
290
+ host = args[1];
291
+ options = args[2] || {};
292
+ listener = args[3];
293
+ }
294
+ else if (typeof args[1] === 'object') {
295
+ options = args[1];
296
+ listener = args[2];
297
+ }
298
+ else if (typeof args[1] === 'function') {
299
+ listener = args[1];
300
+ }
301
+ }
302
+ const socket = new TLSSocket(options);
303
+ socket.connect({
304
+ port,
305
+ host,
306
+ ...options
307
+ }, listener);
308
+ return socket;
309
+ }
310
+ class Server extends index_1.Server {
311
+ constructor(options, connectionListener) {
312
+ super(options);
313
+ this._secureContextId = 0;
314
+ if (options && options.secureContext) {
315
+ this._secureContextId = options.secureContext.id;
316
+ }
317
+ else if (options && (options.key || options.cert || options.ca)) {
318
+ this._secureContextId = createSecureContext({
319
+ cert: options.cert,
320
+ key: options.key,
321
+ ca: options.ca
322
+ }).id;
323
+ }
324
+ else {
325
+ // Create empty secure context to allow late configuration (addContext)
326
+ this._secureContextId = createSecureContext().id;
327
+ }
328
+ this.on('connection', (socket) => {
329
+ const tlsSocket = new TLSSocket(socket);
330
+ this.emit('secureConnection', tlsSocket);
331
+ });
332
+ this.on('session', (data) => {
333
+ this.emit('newSession', data);
334
+ });
335
+ if (options && options.SNICallback) {
336
+ console.warn("SNICallback is not supported yet, use addContext() instead");
337
+ }
338
+ if (connectionListener) {
339
+ this.on('secureConnection', connectionListener);
340
+ }
341
+ }
342
+ addContext(hostname, context) {
343
+ if (!this._secureContextId) {
344
+ throw new Error("Cannot addContext to a non-TLS server. Provide initial cert/key in constructor.");
345
+ }
346
+ Driver_1.Driver.addContextToSecureContext(this._secureContextId, hostname, context.cert, context.key);
347
+ }
348
+ setSecureContext(options) {
349
+ this._secureContextId = createSecureContext(options).id;
350
+ }
351
+ getTicketKeys() {
352
+ return this._secureContextId ? Driver_1.Driver.getTicketKeys(this._secureContextId) : undefined;
353
+ }
354
+ setTicketKeys(keys) {
355
+ if (!this._secureContextId)
356
+ throw new Error("Not a TLS server");
357
+ Driver_1.Driver.setTicketKeys(this._secureContextId, keys);
358
+ }
359
+ listen(port, host, backlog, callback) {
360
+ if (!this._secureContextId) {
361
+ return super.listen(port, host, backlog, callback);
362
+ }
363
+ let _port = 0;
364
+ let _host;
365
+ let _backlog;
366
+ let _path;
367
+ let _callback;
368
+ let ipv6Only = false;
369
+ let reusePort = false;
370
+ let handle;
371
+ if (typeof port === 'object' && port !== null) {
372
+ if (typeof port.fd === 'number') {
373
+ handle = port;
374
+ _backlog = port.backlog;
375
+ _callback = host;
376
+ }
377
+ else {
378
+ _port = port.port;
379
+ _host = port.host;
380
+ _backlog = port.backlog;
381
+ _path = port.path;
382
+ ipv6Only = port.ipv6Only === true;
383
+ reusePort = port.reusePort === true;
384
+ _callback = host;
385
+ }
386
+ }
387
+ else {
388
+ _port = typeof port === 'number' ? port : (typeof port === 'string' && !isNaN(Number(port)) ? Number(port) : 0);
389
+ if (typeof port === 'string' && isNaN(Number(port)))
390
+ _path = port;
391
+ if (typeof host === 'string')
392
+ _host = host;
393
+ else if (typeof host === 'function')
394
+ _callback = host;
395
+ if (typeof backlog === 'number')
396
+ _backlog = backlog;
397
+ else if (typeof backlog === 'function')
398
+ _callback = backlog;
399
+ if (typeof callback === 'function')
400
+ _callback = callback;
401
+ }
402
+ if (_callback)
403
+ this.once('listening', _callback);
404
+ const driver = this._driver;
405
+ if (_path) {
406
+ driver.listenTLSUnix(_path, this._secureContextId, _backlog);
407
+ }
408
+ else if (handle) {
409
+ console.warn("TLS over handles not fully implemented yet");
410
+ driver.listenTLS(_port || 0, this._secureContextId, _backlog, ipv6Only, reusePort);
411
+ }
412
+ else {
413
+ driver.listenTLS(_port || 0, this._secureContextId, _backlog, ipv6Only, reusePort);
414
+ }
415
+ return this;
416
+ }
417
+ }
418
+ exports.Server = Server;
419
+ function createServer(options, connectionListener) {
420
+ return new Server(options, connectionListener);
421
+ }
422
+ function getCiphers() {
423
+ return [
424
+ 'TLS_AES_128_GCM_SHA256',
425
+ 'TLS_AES_256_GCM_SHA384',
426
+ 'TLS_CHACHA20_POLY1305_SHA256',
427
+ 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256',
428
+ 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384',
429
+ 'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256',
430
+ 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256',
431
+ 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384',
432
+ 'TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256'
433
+ ];
434
+ }
435
+ function checkServerIdentity(hostname, cert) {
436
+ const subject = cert.subject;
437
+ const dnsNames = [];
438
+ // In a real implementation we'd extract SANs from the cert object if available.
439
+ // Our PeerCertificate already has subject.CN.
440
+ if (subject && subject.CN) {
441
+ dnsNames.push(subject.CN);
442
+ }
443
+ // SANs are preferred over CN but our current peer_cert JSON might not have them exploded yet
444
+ // unless x509-parser logic is updated. For now, we match against CN.
445
+ // Wildcard matching logic:
446
+ const matchHash = (host, pattern) => {
447
+ const parts = host.split('.');
448
+ const patternParts = pattern.split('.');
449
+ if (parts.length !== patternParts.length)
450
+ return false;
451
+ for (let i = 0; i < parts.length; i++) {
452
+ if (patternParts[i] === '*')
453
+ continue;
454
+ if (parts[i].toLowerCase() !== patternParts[i].toLowerCase())
455
+ return false;
456
+ }
457
+ return true;
458
+ };
459
+ const matches = dnsNames.some(name => {
460
+ if (name.includes('*')) {
461
+ return matchHash(hostname, name);
462
+ }
463
+ return name.toLowerCase() === hostname.toLowerCase();
464
+ });
465
+ if (!matches) {
466
+ const err = new Error(`Hostname/IP does not match certificate's altnames: Host: ${hostname}. is not in cert's altnames: ${dnsNames.join(', ')}`);
467
+ err.reason = 'Host name mismatch';
468
+ err.host = hostname;
469
+ err.cert = cert;
470
+ return err;
471
+ }
472
+ return undefined;
473
+ }
474
+ // -----------------------------------------------------------------------------
475
+ // Legacy Classes & Utils
476
+ // -----------------------------------------------------------------------------
477
+ /**
478
+ * Legacy CryptoStream for Node.js compatibility.
479
+ * In this implementation, it's a simple wrapper around TLSSocket.
480
+ */
481
+ class CryptoStream extends TLSSocket {
482
+ constructor(options) {
483
+ super(options || {});
484
+ }
485
+ }
486
+ exports.CryptoStream = CryptoStream;
487
+ /**
488
+ * Legacy SecurePair for Node.js compatibility.
489
+ */
490
+ class SecurePair {
491
+ constructor(secureContext, isServer, requestCert, rejectUnauthorized) {
492
+ this.cleartext = new CryptoStream();
493
+ this.encrypted = this.cleartext; // Logically the same in our simplified model
494
+ }
495
+ }
496
+ exports.SecurePair = SecurePair;
497
+ function createSecurePair(secureContext, isServer, requestCert, rejectUnauthorized) {
498
+ return new SecurePair(secureContext, isServer, requestCert, rejectUnauthorized);
499
+ }
500
+ /**
501
+ * Legacy certificate string parser.
502
+ */
503
+ function parseCertString(certString) {
504
+ const out = {};
505
+ const parts = certString.split('/');
506
+ for (const part of parts) {
507
+ const [key, value] = part.split('=');
508
+ if (key && value)
509
+ out[key] = value;
510
+ }
511
+ return out;
512
+ }
513
+ /**
514
+ * Mock implementation of convertTLSV1CertToPEM.
515
+ */
516
+ function convertTLSV1CertToPEM(cert) {
517
+ if (typeof cert === 'string' && cert.includes('BEGIN CERTIFICATE'))
518
+ return cert;
519
+ const body = (cert instanceof Buffer) ? cert.toString('base64') : Buffer.from(cert).toString('base64');
520
+ return `-----BEGIN CERTIFICATE-----\n${body}\n-----END CERTIFICATE-----`;
521
+ }
@@ -35,11 +35,13 @@ target_sources(
35
35
  # Shared Nitrogen C++ sources
36
36
  ../nitrogen/generated/shared/c++/HybridNetSocketDriverSpec.cpp
37
37
  ../nitrogen/generated/shared/c++/HybridNetServerDriverSpec.cpp
38
+ ../nitrogen/generated/shared/c++/HybridHttpParserSpec.cpp
38
39
  ../nitrogen/generated/shared/c++/HybridNetDriverSpec.cpp
39
40
  ../nitrogen/generated/shared/c++/HybridNitroBufferSpec.cpp
40
41
  # Android-specific Nitrogen C++ sources
41
42
  ../nitrogen/generated/android/c++/JHybridNetSocketDriverSpec.cpp
42
43
  ../nitrogen/generated/android/c++/JHybridNetServerDriverSpec.cpp
44
+ ../nitrogen/generated/android/c++/JHybridHttpParserSpec.cpp
43
45
  ../nitrogen/generated/android/c++/JHybridNetDriverSpec.cpp
44
46
  )
45
47
 
@@ -18,6 +18,7 @@
18
18
  #include "JHybridNetSocketDriverSpec.hpp"
19
19
  #include "JFunc_void_double_std__shared_ptr_ArrayBuffer_.hpp"
20
20
  #include "JHybridNetServerDriverSpec.hpp"
21
+ #include "JHybridHttpParserSpec.hpp"
21
22
  #include "JHybridNetDriverSpec.hpp"
22
23
  #include "HybridNetDriver.hpp"
23
24
 
@@ -33,6 +34,7 @@ int initialize(JavaVM* vm) {
33
34
  margelo::nitro::net::JHybridNetSocketDriverSpec::registerNatives();
34
35
  margelo::nitro::net::JFunc_void_double_std__shared_ptr_ArrayBuffer__cxx::registerNatives();
35
36
  margelo::nitro::net::JHybridNetServerDriverSpec::registerNatives();
37
+ margelo::nitro::net::JHybridHttpParserSpec::registerNatives();
36
38
  margelo::nitro::net::JHybridNetDriverSpec::registerNatives();
37
39
 
38
40
  // Register Nitro Hybrid Objects