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/README.md CHANGED
@@ -1,13 +1,20 @@
1
1
  # react-native-nitro-net
2
2
 
3
- Node.js `net` API implementation for React Native using [Nitro Modules](https://github.com/mrousavy/nitro) and Rust.
3
+ Ultra-high-performance networking to React Native by combining a memory-safe Rust core with the zero-overhead Nitro Modules JSI bridge. Provides Node.js-compatible net, tls, http(s) API.
4
+
5
+ [![license](https://img.shields.io/badge/license-ISC-blue.svg)]()
6
+ [![platform](https://img.shields.io/badge/platform-ios%20%7C%20android-lightgrey.svg)]()
7
+ [![compatibility](https://img.shields.io/badge/Node.js-100%25%20dns-green.svg)]()
8
+ [中文文档](./README_zh.md)
4
9
 
5
10
  ## Features
6
11
 
7
12
  * 🚀 **High Performance**: Built on top of Rust's `tokio` asynchronous runtime.
8
- * 🤝 **Node.js Compatible**: Implements the standard `net` API including `Socket` (Duplex stream) and `Server`.
13
+ * 🤝 **Node.js Compatible**: Implements standard `net`, `tls`, `http`, and `https` APIs.
14
+ * 🛡️ **Modern Security**: TLS implementation powered by **Rustls 0.23** (Ring provider), supporting TLS 1.2 and 1.3.
15
+ * 🔒 **Full Protocol Support**: Support for PEM/PFX certificates, SNI, HTTP Trailers, 100 Continue, Protocol Upgrades (101), and HTTP Tunneling (CONNECT).
9
16
  * ⚡ **Nitro Modules**: Uses JSI for zero-overhead communication between JavaScript and Native code.
10
- * 🛡️ **Robust & Stable**: Advanced fixes for common networking issues like port reuse, deadlocks, and DNS reliability.
17
+ * 🛡️ **Robust & Stable**: Advanced fixes for port reuse, deadlocks, and connection pooling hangs.
11
18
  * 📱 **Cross-Platform**: Supports both iOS and Android.
12
19
 
13
20
  ## Installation
@@ -30,7 +37,7 @@ cd ios && pod install
30
37
 
31
38
  This library uses a high-performance three-layer architecture:
32
39
 
33
- 1. **JavaScript Layer**: Provides the high-level Node.js compatible `net.Socket` (Duplex) and `net.Server` APIs using `readable-stream` and `EventEmitter`.
40
+ 1. **JavaScript Layer**: Provides high-level Node.js compatible `net` and `tls` APIs using `readable-stream` and `EventEmitter`.
34
41
  2. **C++ Bridge (Nitro)**: Handles the zero-copy orchestration between JS and Rust using Nitro Hybrid Objects and JSI.
35
42
  3. **Rust Core**: Implements the actual networking logic using the **Tokio** asynchronous runtime, providing memory safety and high concurrency.
36
43
 
@@ -73,15 +80,97 @@ server.listen(0, '127.0.0.1', () => {
73
80
  });
74
81
  ```
75
82
 
76
- ## Stability Improvements
83
+ ### TLS (Secure Socket)
84
+
85
+ ```typescript
86
+ import { tls } from 'react-native-nitro-net';
87
+
88
+ // Client connection
89
+ const socket = tls.connect({
90
+ host: 'example.com',
91
+ port: 443,
92
+ servername: 'example.com', // SNI
93
+ }, () => {
94
+ console.log('Securely connected!');
95
+ console.log('Protocol:', socket.getProtocol());
96
+ });
77
97
 
78
- We have implemented several critical fixes to ensure production-grade stability:
98
+ // Server with PFX
99
+ const server = tls.createServer({
100
+ pfx: fs.readFileSync('server.pfx'),
101
+ passphrase: 'your-password'
102
+ }, (socket) => {
103
+ socket.write('Secure hello!');
104
+ });
105
+ server.listen(443);
106
+ ```
79
107
 
80
- * **Port Reuse (`SO_REUSEPORT`)**: Automatically enabled on Unix/iOS to allow immediate server restarts without the "Address already in use" error.
81
- * **Anti-Deadlock Logic**: C++ layer uses lock-free callback dispatching to prevent UI freezes during high-frequency events.
82
- * **DNS Reliability**: Automatically retries all resolved IP addresses if the first one fails to connect.
108
+ * **Advanced Features**: Supports `keylog` event re-emission for Wireshark, session resumption, and `asyncDispose`.
109
+ * **Performance Tuning**: Configurable `headersTimeout`, `keepAliveTimeout`, and `requestTimeout`.
83
110
  * **Resource Management**: Strict protective shutdown logic in Rust to prevent socket and Unix domain socket file leaks.
84
111
 
112
+ ## Usage
113
+
114
+ ### HTTP Request
115
+
116
+ Implementation of the standard Node.js `http` API.
117
+
118
+ ```typescript
119
+ import { http } from 'react-native-nitro-net';
120
+
121
+ http.get('http://google.com', (res) => {
122
+ console.log(`Status: ${res.statusCode}`);
123
+ res.on('data', (chunk) => console.log(`Body segment: ${chunk.length} bytes`));
124
+ res.on('end', () => console.log('Request complete'));
125
+ });
126
+ ```
127
+
128
+ ### HTTPS with Connection Pooling
129
+
130
+ Uses `https` and the built-in `Agent` for connection reuse.
131
+
132
+ ```typescript
133
+ import { https } from 'react-native-nitro-net';
134
+
135
+ const agent = new https.Agent({ keepAlive: true });
136
+
137
+ https.get('https://api.github.com/users/margelo', { agent }, (res) => {
138
+ // ... handle response
139
+ });
140
+ ```
141
+
142
+ ### TCP Client (Socket)
143
+
144
+ ```typescript
145
+ import net from 'react-native-nitro-net';
146
+
147
+ const client = net.createConnection({ port: 8080, host: '127.0.0.1' }, () => {
148
+ client.write('Hello Server!');
149
+ });
150
+ ```
151
+
152
+ ### Server (Dynamic Port Support)
153
+
154
+ The server supports binding to a dynamic port by using `0`.
155
+
156
+ ```typescript
157
+ import net from 'react-native-nitro-net';
158
+
159
+ const server = net.createServer((socket) => {
160
+ socket.write('Echo: ' + socket.read());
161
+ });
162
+
163
+ server.listen(0, '127.0.0.1', () => {
164
+ const address = server.address();
165
+ console.log(`Server listening on dynamic port: ${address?.port}`);
166
+ });
167
+ ```
168
+
169
+ ## Compatibility Notes
170
+
171
+ > [!IMPORTANT]
172
+ > **`Server.close()` Behavior**: Unlike Node.js's default behavior where `server.close()` only stops accepting new connections, this implementation **immediately destroys all active connections** when `close()` is called. This ensures clean resource release and is more intuitive for mobile applications.
173
+
85
174
  ## API Reference
86
175
 
87
176
  ### `net.Socket`
@@ -97,11 +186,25 @@ We have implemented several critical fixes to ensure production-grade stability:
97
186
 
98
187
  **Events**: `connect`, `ready`, `data`, `error`, `close`, `timeout`, `lookup`.
99
188
 
189
+ ### `tls.TLSSocket`
190
+ *Extends `net.Socket`*
191
+
192
+ | Property / Method | Description |
193
+ | --- | --- |
194
+ | `authorized` | `true` if peer certificate is verified. |
195
+ | `getProtocol()` | Returns negotiated TLS version (e.g., "TLSv1.3"). |
196
+ | `getCipher()` | Returns current cipher information. |
197
+ | `getPeerCertificate()`| Returns detailed JSON of the peer certificate. |
198
+ | `getSession()` | Returns the session ticket for resumption. |
199
+ | `encrypted` | Always `true`. |
200
+
201
+ **Events**: `secureConnect`, `session`, `keylog`, `OCSPResponse`.
202
+
100
203
  ### Global APIs
101
204
 
102
205
  | Method | Description |
103
206
  | --- | --- |
104
- | `initWithConfig(options)` | Optional. Initializes the Rust runtime with custom settings (e.g., `workerThreads`). Must be called before any other operation. |
207
+ | `initWithConfig(options)` | Optional. Initializes the Rust runtime with custom settings (e.g., `workerThreads`, `debug`). Must be called before any other operation. |
105
208
  | `setVerbose(bool)` | Toggle detailed logging for JS, C++, and Rust. |
106
209
  | `isIP(string)` | Returns `0`, `4`, or `6`. |
107
210
 
@@ -110,11 +213,18 @@ We have implemented several critical fixes to ensure production-grade stability:
110
213
  | Method | Description |
111
214
  | --- | --- |
112
215
  | `listen(options)` | Start listening. Supports `port: 0` for dynamic allocation. |
113
- | `close()` | Stops the server from accepting new connections. |
216
+ | `close()` | Stops the server and **destroys all active connections**. |
114
217
  | `address()` | Returns the bound address (crucial for dynamic ports). |
115
218
  | `getConnections(cb)`| Get count of active connections. |
219
+ | `renegotiate(opt, cb)`| **Shim**: Returns `ERR_TLS_RENEGOTIATION_DISABLED` (Rustls security policy). |
220
+
221
+ **Events**: `listening`, `connection`, `error`, `close`, `connect` (HTTP Tunneling).
222
+
223
+ ### `tls.Server`
224
+ *Extends `net.Server`*
116
225
 
117
- **Events**: `listening`, `connection`, `error`, `close`.
226
+ Supported methods: `listen`, `close`, `addContext`, `setTicketKeys`, `getTicketKeys`.
227
+ **Events**: `secureConnection`, `keylog`, `newSession`.
118
228
 
119
229
  ## Debugging
120
230
 
Binary file
@@ -0,0 +1,67 @@
1
+ #pragma once
2
+
3
+ #include "../nitrogen/generated/shared/c++/HybridHttpParserSpec.hpp"
4
+ #include "NetBindings.hpp"
5
+ #include <NitroModules/ArrayBuffer.hpp>
6
+ #include <string>
7
+
8
+ namespace margelo {
9
+ namespace nitro {
10
+ namespace net {
11
+
12
+ using namespace margelo::nitro;
13
+
14
+ class HybridHttpParser : public HybridHttpParserSpec {
15
+ public:
16
+ HybridHttpParser(int mode) : HybridObject(TAG) {
17
+ _id = net_http_parser_create(mode);
18
+ }
19
+
20
+ ~HybridHttpParser() { net_http_parser_destroy(_id); }
21
+
22
+ std::string feed(const std::shared_ptr<ArrayBuffer> &data) override {
23
+ if (!data)
24
+ return "";
25
+
26
+ char buf[4096];
27
+ int res =
28
+ net_http_parser_feed(_id, data->data(), data->size(), buf, sizeof(buf));
29
+
30
+ if (res > 0) {
31
+ // Complete message
32
+ return std::string(buf, res);
33
+ } else if (res == 0) {
34
+ // Partial message
35
+ return "";
36
+ } else if (res < -3) {
37
+ // Buffer too small, required size is -res
38
+ size_t requiredSize = static_cast<size_t>(-res);
39
+ std::string largerBuf(requiredSize, '\0');
40
+ res = net_http_parser_feed(_id, nullptr, 0, &largerBuf[0],
41
+ requiredSize + 1);
42
+ if (res > 0) {
43
+ return std::string(largerBuf.data(), res);
44
+ }
45
+ return "ERROR: Re-parse failed after enlarging buffer";
46
+ } else {
47
+ // Error
48
+ switch (res) {
49
+ case -1:
50
+ return "ERROR: JSON serialization failed";
51
+ case -2:
52
+ return "ERROR: HTTP parse failed";
53
+ case -3:
54
+ return "ERROR: Parser not found";
55
+ default:
56
+ return "ERROR: Unknown error";
57
+ }
58
+ }
59
+ }
60
+
61
+ private:
62
+ uint32_t _id;
63
+ };
64
+
65
+ } // namespace net
66
+ } // namespace nitro
67
+ } // namespace margelo
@@ -1,14 +1,21 @@
1
1
  #pragma once
2
2
 
3
+ #include "../nitrogen/generated/shared/c++/HybridHttpParserSpec.hpp"
3
4
  #include "../nitrogen/generated/shared/c++/HybridNetDriverSpec.hpp"
5
+ #include "HybridHttpParser.hpp"
4
6
  #include "HybridNetServerDriver.hpp"
5
7
  #include "HybridNetSocketDriver.hpp"
6
8
  #include "NetManager.hpp"
9
+ #include <NitroModules/ArrayBuffer.hpp>
10
+ #include <optional>
11
+ #include <string>
7
12
 
8
13
  namespace margelo {
9
14
  namespace nitro {
10
15
  namespace net {
11
16
 
17
+ using namespace margelo::nitro;
18
+
12
19
  class HybridNetDriver : public HybridNetDriverSpec {
13
20
  public:
14
21
  HybridNetDriver() : HybridObject(TAG) {}
@@ -31,6 +38,73 @@ public:
31
38
  return std::make_shared<HybridNetServerDriver>();
32
39
  }
33
40
 
41
+ std::shared_ptr<HybridHttpParserSpec> createHttpParser(double mode) override {
42
+ return std::make_shared<HybridHttpParser>(static_cast<int>(mode));
43
+ }
44
+
45
+ double
46
+ createSecureContext(const std::string &cert, const std::string &key,
47
+ const std::optional<std::string> &passphrase) override {
48
+ return static_cast<double>(net_create_secure_context(
49
+ cert.c_str(), key.c_str(),
50
+ passphrase.has_value() ? passphrase.value().c_str() : nullptr));
51
+ }
52
+
53
+ double createEmptySecureContext() override {
54
+ return static_cast<double>(net_secure_context_create());
55
+ }
56
+
57
+ void addCACertToSecureContext(double scId, const std::string &ca) override {
58
+ net_secure_context_add_ca(static_cast<uint32_t>(scId), ca.c_str());
59
+ }
60
+
61
+ void addContextToSecureContext(
62
+ double scId, const std::string &hostname, const std::string &cert,
63
+ const std::string &key,
64
+ const std::optional<std::string> &passphrase) override {
65
+ net_secure_context_add_context(
66
+ static_cast<uint32_t>(scId), hostname.c_str(), cert.c_str(),
67
+ key.c_str(),
68
+ passphrase.has_value() ? passphrase.value().c_str() : nullptr);
69
+ }
70
+
71
+ void
72
+ setPFXToSecureContext(double scId, const std::shared_ptr<ArrayBuffer> &pfx,
73
+ const std::optional<std::string> &passphrase) override {
74
+ if (pfx) {
75
+ net_secure_context_set_pfx(
76
+ static_cast<uint32_t>(scId), pfx->data(), pfx->size(),
77
+ passphrase.has_value() ? passphrase.value().c_str() : nullptr);
78
+ }
79
+ }
80
+
81
+ void setOCSPResponseToSecureContext(
82
+ double scId, const std::shared_ptr<ArrayBuffer> &ocsp) override {
83
+ if (ocsp) {
84
+ net_secure_context_set_ocsp_response(static_cast<uint32_t>(scId),
85
+ ocsp->data(), ocsp->size());
86
+ }
87
+ }
88
+
89
+ std::optional<std::shared_ptr<ArrayBuffer>>
90
+ getTicketKeys(double scId) override {
91
+ uint8_t buf[256];
92
+ size_t len = net_server_get_ticket_keys(static_cast<uint32_t>(scId), buf,
93
+ sizeof(buf));
94
+ if (len > 0) {
95
+ return ArrayBuffer::copy(buf, len);
96
+ }
97
+ return std::nullopt;
98
+ }
99
+
100
+ void setTicketKeys(double scId,
101
+ const std::shared_ptr<ArrayBuffer> &keys) override {
102
+ if (keys) {
103
+ net_server_set_ticket_keys(static_cast<uint32_t>(scId), keys->data(),
104
+ keys->size());
105
+ }
106
+ }
107
+
34
108
  void initWithConfig(const NetConfig &config) override {
35
109
  uint32_t workerThreads = config.workerThreads.value_or(0);
36
110
  NetManager::shared().initWithConfig(workerThreads);
@@ -49,11 +49,27 @@ public:
49
49
  ipv6Only.value_or(false), reusePort.value_or(false));
50
50
  }
51
51
 
52
+ void listenTLS(double port, double secureContextId,
53
+ std::optional<double> backlog, std::optional<bool> ipv6Only,
54
+ std::optional<bool> reusePort) override {
55
+ net_listen_tls(_id, static_cast<int>(port),
56
+ static_cast<int>(backlog.value_or(128)),
57
+ ipv6Only.value_or(false), reusePort.value_or(false),
58
+ static_cast<uint32_t>(secureContextId));
59
+ }
60
+
52
61
  void listenUnix(const std::string &path,
53
62
  std::optional<double> backlog) override {
54
63
  net_listen_unix(_id, path.c_str(), static_cast<int>(backlog.value_or(128)));
55
64
  }
56
65
 
66
+ void listenTLSUnix(const std::string &path, double secureContextId,
67
+ std::optional<double> backlog) override {
68
+ net_listen_tls_unix(_id, path.c_str(),
69
+ static_cast<int>(backlog.value_or(128)),
70
+ static_cast<uint32_t>(secureContextId));
71
+ }
72
+
57
73
  void listenHandle(double fd, std::optional<double> backlog) override {
58
74
  net_listen_handle(_id, static_cast<int>(fd),
59
75
  static_cast<int>(backlog.value_or(128)));
@@ -4,12 +4,17 @@
4
4
  #include "NetBindings.hpp"
5
5
  #include "NetManager.hpp"
6
6
  #include <NitroModules/ArrayBuffer.hpp>
7
+ #include <memory>
8
+ #include <optional>
7
9
  #include <string>
10
+ #include <vector>
8
11
 
9
12
  namespace margelo {
10
13
  namespace nitro {
11
14
  namespace net {
12
15
 
16
+ using namespace margelo::nitro;
17
+
13
18
  class HybridNetSocketDriver : public HybridNetSocketDriverSpec {
14
19
  public:
15
20
  HybridNetSocketDriver() : HybridObject(TAG) {
@@ -48,6 +53,111 @@ public:
48
53
  net_connect(_id, host.c_str(), static_cast<int>(port));
49
54
  }
50
55
 
56
+ void connectTLS(const std::string &host, double port,
57
+ const std::optional<std::string> &serverName,
58
+ std::optional<bool> rejectUnauthorized) override {
59
+ const char *sni = serverName.has_value() ? serverName->c_str() : nullptr;
60
+ bool ru = rejectUnauthorized.value_or(true);
61
+ net_connect_tls(_id, host.c_str(), static_cast<int>(port), sni,
62
+ static_cast<int>(ru));
63
+ }
64
+
65
+ void connectTLSWithContext(const std::string &host, double port,
66
+ const std::optional<std::string> &serverName,
67
+ std::optional<bool> rejectUnauthorized,
68
+ std::optional<double> secureContextId) override {
69
+ const char *sni = serverName.has_value() ? serverName->c_str() : nullptr;
70
+ bool ru = rejectUnauthorized.value_or(true);
71
+ if (secureContextId.has_value()) {
72
+ net_connect_tls_with_context(
73
+ _id, host.c_str(), static_cast<int>(port), sni, static_cast<int>(ru),
74
+ static_cast<uint32_t>(secureContextId.value()));
75
+ } else {
76
+ net_connect_tls(_id, host.c_str(), static_cast<int>(port), sni,
77
+ static_cast<int>(ru));
78
+ }
79
+ }
80
+
81
+ std::optional<std::string> getAuthorizationError() override {
82
+ char buf[1024];
83
+ size_t len = net_get_authorization_error(_id, buf, sizeof(buf));
84
+ if (len > 0) {
85
+ return std::string(buf);
86
+ }
87
+ return std::nullopt;
88
+ }
89
+
90
+ std::optional<std::string> getProtocol() override {
91
+ char buf[128];
92
+ size_t len = net_get_protocol(_id, buf, sizeof(buf));
93
+ if (len > 0) {
94
+ return std::string(buf);
95
+ }
96
+ return std::nullopt;
97
+ }
98
+
99
+ std::optional<std::string> getCipher() override {
100
+ char buf[256];
101
+ size_t len = net_get_cipher(_id, buf, sizeof(buf));
102
+ if (len > 0) {
103
+ return std::string(buf);
104
+ }
105
+ return std::nullopt;
106
+ }
107
+
108
+ std::optional<std::string> getALPN() override {
109
+ char buf[64];
110
+ size_t len = net_get_alpn(_id, buf, sizeof(buf));
111
+ if (len > 0) {
112
+ return std::string(buf);
113
+ }
114
+ return std::nullopt;
115
+ }
116
+
117
+ std::optional<std::string> getPeerCertificateJSON() override {
118
+ char buf[16384];
119
+ size_t len = net_get_peer_certificate_json(_id, buf, sizeof(buf));
120
+ if (len > 0) {
121
+ return std::string(buf, len);
122
+ }
123
+ return std::nullopt;
124
+ }
125
+
126
+ std::optional<std::string> getEphemeralKeyInfo() override {
127
+ char buf[512];
128
+ size_t len = net_get_ephemeral_key_info(_id, buf, sizeof(buf));
129
+ if (len > 0) {
130
+ return std::string(buf, len);
131
+ }
132
+ return std::nullopt;
133
+ }
134
+
135
+ std::optional<std::string> getSharedSigalgs() override {
136
+ char buf[1024];
137
+ size_t len = net_get_shared_sigalgs(_id, buf, sizeof(buf));
138
+ if (len > 0) {
139
+ return std::string(buf, len);
140
+ }
141
+ return std::nullopt;
142
+ }
143
+
144
+ bool isSessionReused() override { return net_is_session_reused(_id); }
145
+
146
+ std::optional<std::shared_ptr<ArrayBuffer>> getSession() override {
147
+ uint8_t buf[2048];
148
+ size_t len = net_get_session(_id, buf, sizeof(buf));
149
+ if (len > 0) {
150
+ return ArrayBuffer::copy(buf, len);
151
+ }
152
+ return std::nullopt;
153
+ }
154
+
155
+ void setSession(const std::shared_ptr<ArrayBuffer> &session) override {
156
+ if (session && session->size() > 0) {
157
+ net_set_session(_id, session->data(), session->size());
158
+ }
159
+ }
160
+
51
161
  void write(const std::shared_ptr<ArrayBuffer> &data) override {
52
162
  if (!data)
53
163
  return;
@@ -70,6 +180,33 @@ public:
70
180
  }
71
181
  }
72
182
 
183
+ void enableKeylog() override { net_socket_enable_keylog(_id); }
184
+
185
+ void enableTrace() override { net_socket_enable_trace(_id); }
186
+
187
+ std::optional<std::shared_ptr<ArrayBuffer>> exportKeyingMaterial(
188
+ double length, const std::string &label,
189
+ const std::optional<std::shared_ptr<ArrayBuffer>> &context) override {
190
+ size_t len = static_cast<size_t>(length);
191
+ std::vector<uint8_t> output(len);
192
+
193
+ const uint8_t *ctx_data = nullptr;
194
+ size_t ctx_len = 0;
195
+ if (context.has_value() && context.value()) {
196
+ ctx_data = context.value()->data();
197
+ ctx_len = context.value()->size();
198
+ }
199
+
200
+ int result = net_socket_export_keying_material(
201
+ _id, len, label.c_str(), ctx_data, ctx_len, output.data(),
202
+ output.size());
203
+
204
+ if (result > 0) {
205
+ return ArrayBuffer::copy(output.data(), static_cast<size_t>(result));
206
+ }
207
+ return std::nullopt;
208
+ }
209
+
73
210
  void setNoDelay(bool enable) override { net_set_nodelay(_id, enable); }
74
211
 
75
212
  void setKeepAlive(bool enable, double delay) override {
@@ -107,6 +244,45 @@ public:
107
244
  net_connect_unix(_id, path.c_str());
108
245
  }
109
246
 
247
+ void connectUnixTLS(const std::string &path,
248
+ const std::optional<std::string> &serverName,
249
+ std::optional<bool> rejectUnauthorized) override {
250
+ #if !defined(__ANDROID__)
251
+ const char *sni = serverName.has_value() ? serverName->c_str() : "";
252
+ bool ru = rejectUnauthorized.value_or(true);
253
+ net_connect_unix_tls(_id, path.c_str(), sni, static_cast<int>(ru));
254
+ #else
255
+ // Unix TLS not supported on Android
256
+ (void)path;
257
+ (void)serverName;
258
+ (void)rejectUnauthorized;
259
+ #endif
260
+ }
261
+
262
+ void
263
+ connectUnixTLSWithContext(const std::string &path,
264
+ const std::optional<std::string> &serverName,
265
+ std::optional<bool> rejectUnauthorized,
266
+ std::optional<double> secureContextId) override {
267
+ #if !defined(__ANDROID__)
268
+ const char *sni = serverName.has_value() ? serverName->c_str() : "";
269
+ bool ru = rejectUnauthorized.value_or(true);
270
+ if (secureContextId.has_value()) {
271
+ net_connect_unix_tls_with_context(
272
+ _id, path.c_str(), sni, static_cast<int>(ru),
273
+ static_cast<uint32_t>(secureContextId.value()));
274
+ } else {
275
+ net_connect_unix_tls(_id, path.c_str(), sni, static_cast<int>(ru));
276
+ }
277
+ #else
278
+ // Unix TLS not supported on Android
279
+ (void)path;
280
+ (void)serverName;
281
+ (void)rejectUnauthorized;
282
+ (void)secureContextId;
283
+ #endif
284
+ }
285
+
110
286
  private:
111
287
  void onNativeEvent(int type, const uint8_t *data, size_t len) {
112
288
  if (!_onEvent)