react-native-nitro-net 0.5.2 → 0.5.3
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.
- package/cpp/HybridNetDriver.hpp +17 -0
- package/cpp/NetManager.hpp +40 -69
- package/lib/http.d.ts.map +1 -1
- package/lib/http.js +88 -17
- package/lib/net.d.ts.map +1 -1
- package/lib/net.js +4 -0
- package/package.json +2 -1
- package/react-native-nitro-net.podspec +2 -2
- package/src/http.ts +89 -22
- package/src/net.ts +4 -0
package/cpp/HybridNetDriver.hpp
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include "HybridNetSocketDriver.hpp"
|
|
8
8
|
#include "NetManager.hpp"
|
|
9
9
|
#include <NitroModules/ArrayBuffer.hpp>
|
|
10
|
+
#include <jsi/jsi.h>
|
|
10
11
|
#include <optional>
|
|
11
12
|
#include <string>
|
|
12
13
|
|
|
@@ -20,6 +21,22 @@ class HybridNetDriver : public HybridNetDriverSpec {
|
|
|
20
21
|
public:
|
|
21
22
|
HybridNetDriver() : HybridObject(TAG) {}
|
|
22
23
|
|
|
24
|
+
jsi::Value installDispatcher(jsi::Runtime &runtime, const jsi::Value &,
|
|
25
|
+
const jsi::Value *, size_t) {
|
|
26
|
+
auto dispatcher =
|
|
27
|
+
margelo::nitro::Dispatcher::getRuntimeGlobalDispatcher(runtime);
|
|
28
|
+
NetManager::shared().setDispatcher(dispatcher);
|
|
29
|
+
return jsi::Value::undefined();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
void loadHybridMethods() override {
|
|
33
|
+
HybridNetDriverSpec::loadHybridMethods();
|
|
34
|
+
registerHybrids(this, [](Prototype &prototype) {
|
|
35
|
+
prototype.registerRawHybridMethod("installDispatcher", 0,
|
|
36
|
+
&HybridNetDriver::installDispatcher);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
23
40
|
std::shared_ptr<HybridNetSocketDriverSpec>
|
|
24
41
|
createSocket(const std::optional<std::string> &id) override {
|
|
25
42
|
if (id.has_value()) {
|
package/cpp/NetManager.hpp
CHANGED
|
@@ -28,6 +28,9 @@
|
|
|
28
28
|
printf("\n")
|
|
29
29
|
#endif
|
|
30
30
|
|
|
31
|
+
#include <NitroModules/Dispatcher.hpp>
|
|
32
|
+
#include <NitroModules/NitroLogger.hpp>
|
|
33
|
+
|
|
31
34
|
namespace margelo::nitro::net {
|
|
32
35
|
|
|
33
36
|
class NetManager {
|
|
@@ -45,15 +48,18 @@ public:
|
|
|
45
48
|
initializeRuntime(0); // 0 = use default (CPU core count)
|
|
46
49
|
}
|
|
47
50
|
|
|
51
|
+
void setDispatcher(std::shared_ptr<margelo::nitro::Dispatcher> dispatcher) {
|
|
52
|
+
LOGI("NetManager: Dispatcher installed.");
|
|
53
|
+
_dispatcher = dispatcher;
|
|
54
|
+
}
|
|
55
|
+
|
|
48
56
|
/// Initialize with custom worker thread count
|
|
49
|
-
/// Must be called before any other operations, or the config will be ignored
|
|
50
57
|
void initWithConfig(uint32_t workerThreads) {
|
|
51
58
|
if (!_initialized) {
|
|
52
59
|
LOGI("Initializing NetManager with %u worker threads...", workerThreads);
|
|
53
60
|
initializeRuntime(workerThreads);
|
|
54
61
|
} else {
|
|
55
|
-
LOGW("NetManager already initialized, config ignored.
|
|
56
|
-
"initWithConfig before any socket/server operations.");
|
|
62
|
+
LOGW("NetManager already initialized, config ignored.");
|
|
57
63
|
}
|
|
58
64
|
}
|
|
59
65
|
|
|
@@ -63,26 +69,21 @@ private:
|
|
|
63
69
|
return;
|
|
64
70
|
_initialized = true;
|
|
65
71
|
|
|
66
|
-
|
|
67
|
-
net_init_with_config(
|
|
68
|
-
[](uint32_t id, int event_type, const uint8_t *data, size_t len,
|
|
72
|
+
auto callback = [](uint32_t id, int event_type, const uint8_t *data, size_t len,
|
|
69
73
|
void *context) {
|
|
70
74
|
auto mgr = static_cast<NetManager *>(context);
|
|
71
75
|
mgr->dispatch(id, event_type, data, len);
|
|
72
|
-
}
|
|
73
|
-
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
if (workerThreads > 0) {
|
|
79
|
+
net_init_with_config(callback, this, workerThreads);
|
|
74
80
|
} else {
|
|
75
|
-
net_init(
|
|
76
|
-
[](uint32_t id, int event_type, const uint8_t *data, size_t len,
|
|
77
|
-
void *context) {
|
|
78
|
-
auto mgr = static_cast<NetManager *>(context);
|
|
79
|
-
mgr->dispatch(id, event_type, data, len);
|
|
80
|
-
},
|
|
81
|
-
this);
|
|
81
|
+
net_init(callback, this);
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
bool _initialized = false;
|
|
86
|
+
std::shared_ptr<margelo::nitro::Dispatcher> _dispatcher;
|
|
86
87
|
|
|
87
88
|
public:
|
|
88
89
|
void registerHandler(uint32_t id, EventHandler handler) {
|
|
@@ -99,63 +100,33 @@ public:
|
|
|
99
100
|
|
|
100
101
|
private:
|
|
101
102
|
void dispatch(uint32_t id, int eventType, const uint8_t *data, size_t len) {
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
eventName = "CONNECT";
|
|
107
|
-
break;
|
|
108
|
-
case NET_EVENT_DATA:
|
|
109
|
-
eventName = "DATA";
|
|
110
|
-
break;
|
|
111
|
-
case NET_EVENT_ERROR:
|
|
112
|
-
eventName = "ERROR";
|
|
113
|
-
break;
|
|
114
|
-
case NET_EVENT_CLOSE:
|
|
115
|
-
eventName = "CLOSE";
|
|
116
|
-
break;
|
|
117
|
-
case NET_EVENT_WRITTEN:
|
|
118
|
-
eventName = "DRAIN";
|
|
119
|
-
break;
|
|
120
|
-
case NET_EVENT_CONNECTION:
|
|
121
|
-
eventName = "CONNECTION";
|
|
122
|
-
break;
|
|
123
|
-
case NET_EVENT_TIMEOUT:
|
|
124
|
-
eventName = "TIMEOUT";
|
|
125
|
-
break;
|
|
126
|
-
case NET_EVENT_LOOKUP:
|
|
127
|
-
eventName = "LOOKUP";
|
|
128
|
-
break;
|
|
129
|
-
case NET_EVENT_SESSION:
|
|
130
|
-
eventName = "SESSION";
|
|
131
|
-
break;
|
|
132
|
-
case NET_EVENT_KEYLOG:
|
|
133
|
-
eventName = "KEYLOG";
|
|
134
|
-
break;
|
|
135
|
-
case NET_EVENT_OCSP:
|
|
136
|
-
eventName = "OCSP";
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
LOGI("dispatch: id=%u, event=%s(%d), len=%zu", id, eventName, eventType,
|
|
141
|
-
len);
|
|
142
|
-
|
|
143
|
-
// Copy handler outside of lock to avoid deadlock
|
|
144
|
-
// (handler may call unregisterHandler which needs unique_lock)
|
|
145
|
-
EventHandler handler;
|
|
146
|
-
{
|
|
147
|
-
std::shared_lock lock(_mutex);
|
|
148
|
-
auto it = _handlers.find(id);
|
|
149
|
-
if (it != _handlers.end()) {
|
|
150
|
-
handler = it->second;
|
|
151
|
-
}
|
|
103
|
+
// 1. Prepare data (copy if needed for async)
|
|
104
|
+
std::vector<uint8_t> buffer;
|
|
105
|
+
if (data && len > 0) {
|
|
106
|
+
buffer.assign(data, data + len);
|
|
152
107
|
}
|
|
153
108
|
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
109
|
+
// 2. Define the actual dispatch logic
|
|
110
|
+
auto doDispatch = [this, id, eventType, buffer = std::move(buffer)]() {
|
|
111
|
+
EventHandler handler;
|
|
112
|
+
{
|
|
113
|
+
std::shared_lock lock(_mutex);
|
|
114
|
+
auto it = _handlers.find(id);
|
|
115
|
+
if (it != _handlers.end()) {
|
|
116
|
+
handler = it->second;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (handler) {
|
|
121
|
+
handler(eventType, buffer.data(), buffer.size());
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// 3. Dispatch either via JS Dispatcher (Async) or immediately (Sync fallback)
|
|
126
|
+
if (_dispatcher) {
|
|
127
|
+
_dispatcher->runAsync(std::move(doDispatch));
|
|
157
128
|
} else {
|
|
158
|
-
|
|
129
|
+
doDispatch();
|
|
159
130
|
}
|
|
160
131
|
}
|
|
161
132
|
|
package/lib/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAI5C,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAU9B,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA0D/C,CAAC;AAEF,eAAO,MAAM,OAAO,UAMnB,CAAC;AAIF,qBAAa,eAAgB,SAAQ,QAAQ;IAClC,WAAW,EAAE,MAAM,CAAS;IAC5B,gBAAgB,EAAE,MAAM,CAAK;IAC7B,gBAAgB,EAAE,MAAM,CAAK;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAM;IAChD,UAAU,EAAE,MAAM,EAAE,CAAM;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAS;IACzB,QAAQ,EAAE,OAAO,CAAS;IAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;gBAEjC,MAAM,EAAE,MAAM;IAM1B,KAAK;
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/lib/http.js
CHANGED
|
@@ -91,7 +91,14 @@ export class IncomingMessage extends Readable {
|
|
|
91
91
|
this.socket = socket;
|
|
92
92
|
}
|
|
93
93
|
_read() {
|
|
94
|
-
|
|
94
|
+
// Server-side: socket is kept flowing by _setupHttpConnection.
|
|
95
|
+
// Calling socket.resume() here is the correct Node.js backpressure pattern
|
|
96
|
+
// but only when socket is the actual data source (client-side IncomingMessage).
|
|
97
|
+
// For server-side req, body bytes come via parser→push(), not socket directly.
|
|
98
|
+
// Still call resume() to unblock if paused by backpressure, but guard it.
|
|
99
|
+
if (this.socket && !this.socket._destroyed) {
|
|
100
|
+
this.socket.resume();
|
|
101
|
+
}
|
|
95
102
|
}
|
|
96
103
|
setTimeout(msecs, callback) {
|
|
97
104
|
this.socket.setTimeout(msecs, callback);
|
|
@@ -493,15 +500,55 @@ export class Server extends EventEmitter {
|
|
|
493
500
|
this.emit('request', req, res);
|
|
494
501
|
}
|
|
495
502
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
+
// Push body/EOF into IncomingMessage.
|
|
504
|
+
// CRITICAL: When headers and body arrive in the same TCP packet
|
|
505
|
+
// (parsed.is_headers && body present), the user's 'request' handler
|
|
506
|
+
// has just been called synchronously above. The readable-stream
|
|
507
|
+
// library schedules its internal resume/flow via process.nextTick.
|
|
508
|
+
// If we push() synchronously here, the data lands in the buffer
|
|
509
|
+
// *before* the Readable enters flowing mode, and since no further
|
|
510
|
+
// socket data events will arrive, the flow() loop never drains it.
|
|
511
|
+
// Solution: always defer body/EOF push via process.nextTick so the
|
|
512
|
+
// Readable has a chance to enter flowing mode first.
|
|
513
|
+
const _bodyToPush = req && parsed.body && parsed.body.length > 0
|
|
514
|
+
? Buffer.from(parsed.body) : null;
|
|
515
|
+
const _isComplete = !!(req && parsed.complete);
|
|
516
|
+
const _trailers = parsed.trailers;
|
|
517
|
+
const _reqRef = req;
|
|
518
|
+
// Diagnostic: log body delivery state (requires debug mode)
|
|
519
|
+
debugLog(`[Server] handleParsedResult: is_headers=${parsed.is_headers}, ` +
|
|
520
|
+
`bodyLen=${_bodyToPush?.length ?? 0}, complete=${_isComplete}, ` +
|
|
521
|
+
`req.readableFlowing=${_reqRef?._readableState?.flowing}`);
|
|
522
|
+
if (_bodyToPush !== null || _isComplete) {
|
|
523
|
+
if (parsed.is_headers) {
|
|
524
|
+
// Same-packet case: defer to give Readable time to enter flowing mode
|
|
525
|
+
debugLog(`[Server] Deferring body/EOF push via setImmediate (same-packet)`);
|
|
526
|
+
setImmediate(() => {
|
|
527
|
+
if (!_reqRef)
|
|
528
|
+
return;
|
|
529
|
+
debugLog(`[Server] setImmediate: pushing body=${_bodyToPush?.length ?? 0}, EOF=${_isComplete}`);
|
|
530
|
+
if (_bodyToPush)
|
|
531
|
+
_reqRef.push(_bodyToPush);
|
|
532
|
+
if (_isComplete) {
|
|
533
|
+
_reqRef.complete = true;
|
|
534
|
+
if (_trailers)
|
|
535
|
+
_reqRef.trailers = _trailers;
|
|
536
|
+
_reqRef.push(null);
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
// Subsequent-packet case: push immediately
|
|
542
|
+
debugLog(`[Server] Pushing body/EOF immediately (subsequent-packet)`);
|
|
543
|
+
if (_bodyToPush)
|
|
544
|
+
_reqRef.push(_bodyToPush);
|
|
545
|
+
if (_isComplete) {
|
|
546
|
+
_reqRef.complete = true;
|
|
547
|
+
if (_trailers)
|
|
548
|
+
_reqRef.trailers = _trailers;
|
|
549
|
+
_reqRef.push(null);
|
|
550
|
+
}
|
|
503
551
|
}
|
|
504
|
-
req.push(null);
|
|
505
552
|
}
|
|
506
553
|
// For Keep-Alive, try to parse remaining buffer in case of pipelining
|
|
507
554
|
if (parsed.complete && !req) {
|
|
@@ -890,9 +937,10 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
890
937
|
this.socket = socket;
|
|
891
938
|
this._connected = true;
|
|
892
939
|
this.emit('socket', this.socket);
|
|
893
|
-
//
|
|
894
|
-
|
|
940
|
+
// IMPORTANT: attach response listeners BEFORE flushing writes.
|
|
941
|
+
// If we flush first, the server may respond before we have a data listener.
|
|
895
942
|
this._attachSocketListeners();
|
|
943
|
+
this._flushPendingWrites();
|
|
896
944
|
}
|
|
897
945
|
else {
|
|
898
946
|
this._connect();
|
|
@@ -906,15 +954,18 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
906
954
|
this.emit('error', err);
|
|
907
955
|
return;
|
|
908
956
|
}
|
|
909
|
-
debugLog(`ClientRequest._connect: Socket connected
|
|
910
|
-
debugLog(`[HTTP] _connect: Socket connected!`);
|
|
957
|
+
debugLog(`ClientRequest._connect: Socket connected!`);
|
|
911
958
|
this.socket = socket;
|
|
912
959
|
this._connected = true;
|
|
913
960
|
this.emit('socket', this.socket);
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
this._flushPendingWrites();
|
|
961
|
+
// IMPORTANT: attach response listeners BEFORE flushing writes.
|
|
962
|
+
// If we flush first, the server may respond before we have a data listener.
|
|
917
963
|
this._attachSocketListeners();
|
|
964
|
+
// _flushPendingWrites() internally calls _sendRequest() if headers not sent yet.
|
|
965
|
+
// Do NOT call _sendRequest() separately here — _flushPendingWrites() needs to
|
|
966
|
+
// inspect headersSent and _pendingWrites together so it can set Content-Length
|
|
967
|
+
// before sending headers (to avoid chunked encoding when body is already known).
|
|
968
|
+
this._flushPendingWrites();
|
|
918
969
|
};
|
|
919
970
|
this.socket = agent.createConnection(this._options, connectCallback);
|
|
920
971
|
}
|
|
@@ -1050,8 +1101,28 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
1050
1101
|
return;
|
|
1051
1102
|
this._isFlushing = true;
|
|
1052
1103
|
try {
|
|
1053
|
-
if (!this.headersSent)
|
|
1104
|
+
if (!this.headersSent) {
|
|
1105
|
+
// KEY FIX: When all body data is already queued AND the request is ending,
|
|
1106
|
+
// we can calculate the exact Content-Length and avoid chunked encoding.
|
|
1107
|
+
//
|
|
1108
|
+
// Why this matters: without Content-Length, the request is sent with
|
|
1109
|
+
// Transfer-Encoding: chunked. The Rust HTTP parser on the server side
|
|
1110
|
+
// stores chunked body bytes in its internal buffer after parsing headers,
|
|
1111
|
+
// but calling parser.feed(empty_buffer) to drain those bytes does NOT work
|
|
1112
|
+
// — the drain call returns empty metadata and the body is permanently lost.
|
|
1113
|
+
//
|
|
1114
|
+
// By setting Content-Length here (when we have all the data), the body is
|
|
1115
|
+
// sent as raw bytes. The server parser simply reads N bytes and marks the
|
|
1116
|
+
// request complete — no chunked framing, no drain issues.
|
|
1117
|
+
if (this._ended
|
|
1118
|
+
&& !this.hasHeader('Content-Length')
|
|
1119
|
+
&& !this.hasHeader('Transfer-Encoding')
|
|
1120
|
+
&& this._pendingWrites.length > 0) {
|
|
1121
|
+
const totalLen = this._getPendingBodyLength();
|
|
1122
|
+
this.setHeader('Content-Length', totalLen);
|
|
1123
|
+
}
|
|
1054
1124
|
this._sendRequest();
|
|
1125
|
+
}
|
|
1055
1126
|
// If we are waiting for 100-continue, don't flush yet
|
|
1056
1127
|
if (this._expectContinue && !this._continueReceived) {
|
|
1057
1128
|
return;
|
package/lib/net.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"net.d.ts","sourceRoot":"","sources":["../src/net.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAI5C,OAAO,KAAK,EAAE,eAAe,EAAmB,SAAS,EAAE,MAAM,aAAa,CAAA;AAE9E,OAAO,EAAE,SAAS,EAAE,UAAU,EAA8B,MAAM,UAAU,CAAA;AAM5E,iBAAS,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOnC;AAED,iBAAS,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtC;AAED,iBAAS,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtC;AA0BD,iBAAS,0BAA0B,IAAI,MAAM,CAE5C;AAED,iBAAS,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAGxD;AAYD;;;;;;;;;;;;;;GAcG;AACH,iBAAS,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"net.d.ts","sourceRoot":"","sources":["../src/net.ts"],"names":[],"mappings":"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"}
|
package/lib/net.js
CHANGED
|
@@ -81,6 +81,10 @@ function initWithConfig(config) {
|
|
|
81
81
|
if (config.debug !== undefined) {
|
|
82
82
|
setVerbose(config.debug);
|
|
83
83
|
}
|
|
84
|
+
// Inject dispatcher for async events to avoid thread starvation/deadlocks
|
|
85
|
+
if (Driver.installDispatcher) {
|
|
86
|
+
Driver.installDispatcher();
|
|
87
|
+
}
|
|
84
88
|
Driver.initWithConfig(config);
|
|
85
89
|
}
|
|
86
90
|
export class SocketAddress {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-net",
|
|
3
3
|
"description": "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
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
7
7
|
"module": "./lib/index.js",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
],
|
|
19
19
|
"author": "iwater <iwater@gmail.com>",
|
|
20
20
|
"license": "ISC",
|
|
21
|
+
"homepage": "https://github.com/iwater/react-native-nitro-net",
|
|
21
22
|
"repository": {
|
|
22
23
|
"type": "git",
|
|
23
24
|
"url": "git+https://github.com/iwater/react-native-nitro-net.git"
|
|
@@ -6,12 +6,12 @@ Pod::Spec.new do |s|
|
|
|
6
6
|
s.name = "react-native-nitro-net"
|
|
7
7
|
s.version = package["version"]
|
|
8
8
|
s.summary = package["description"]
|
|
9
|
-
s.homepage = "
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
10
|
s.license = package["license"]
|
|
11
11
|
s.authors = package["author"]
|
|
12
12
|
|
|
13
13
|
s.platform = :ios, "13.0"
|
|
14
|
-
s.source = { :git => "
|
|
14
|
+
s.source = { :git => package["repository"]["url"].gsub("git+", ""), :tag => "v#{s.version}" }
|
|
15
15
|
|
|
16
16
|
# Module Name must match iosModuleName in nitro.json
|
|
17
17
|
s.module_name = "RustCNet"
|
package/src/http.ts
CHANGED
|
@@ -105,7 +105,14 @@ export class IncomingMessage extends Readable {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
_read() {
|
|
108
|
-
|
|
108
|
+
// Server-side: socket is kept flowing by _setupHttpConnection.
|
|
109
|
+
// Calling socket.resume() here is the correct Node.js backpressure pattern
|
|
110
|
+
// but only when socket is the actual data source (client-side IncomingMessage).
|
|
111
|
+
// For server-side req, body bytes come via parser→push(), not socket directly.
|
|
112
|
+
// Still call resume() to unblock if paused by backpressure, but guard it.
|
|
113
|
+
if (this.socket && !(this.socket as any)._destroyed) {
|
|
114
|
+
this.socket.resume();
|
|
115
|
+
}
|
|
109
116
|
}
|
|
110
117
|
|
|
111
118
|
public setTimeout(msecs: number, callback?: () => void): this {
|
|
@@ -573,16 +580,51 @@ export class Server extends EventEmitter {
|
|
|
573
580
|
}
|
|
574
581
|
}
|
|
575
582
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
583
|
+
// Push body/EOF into IncomingMessage.
|
|
584
|
+
// CRITICAL: When headers and body arrive in the same TCP packet
|
|
585
|
+
// (parsed.is_headers && body present), the user's 'request' handler
|
|
586
|
+
// has just been called synchronously above. The readable-stream
|
|
587
|
+
// library schedules its internal resume/flow via process.nextTick.
|
|
588
|
+
// If we push() synchronously here, the data lands in the buffer
|
|
589
|
+
// *before* the Readable enters flowing mode, and since no further
|
|
590
|
+
// socket data events will arrive, the flow() loop never drains it.
|
|
591
|
+
// Solution: always defer body/EOF push via process.nextTick so the
|
|
592
|
+
// Readable has a chance to enter flowing mode first.
|
|
593
|
+
const _bodyToPush = req && parsed.body && parsed.body.length > 0
|
|
594
|
+
? Buffer.from(parsed.body) : null;
|
|
595
|
+
const _isComplete = !!(req && parsed.complete);
|
|
596
|
+
const _trailers = parsed.trailers;
|
|
597
|
+
const _reqRef = req;
|
|
598
|
+
|
|
599
|
+
// Diagnostic: log body delivery state (requires debug mode)
|
|
600
|
+
debugLog(`[Server] handleParsedResult: is_headers=${parsed.is_headers}, ` +
|
|
601
|
+
`bodyLen=${_bodyToPush?.length ?? 0}, complete=${_isComplete}, ` +
|
|
602
|
+
`req.readableFlowing=${(_reqRef as any)?._readableState?.flowing}`);
|
|
603
|
+
|
|
604
|
+
if (_bodyToPush !== null || _isComplete) {
|
|
605
|
+
if (parsed.is_headers) {
|
|
606
|
+
// Same-packet case: defer to give Readable time to enter flowing mode
|
|
607
|
+
debugLog(`[Server] Deferring body/EOF push via setImmediate (same-packet)`);
|
|
608
|
+
setImmediate(() => {
|
|
609
|
+
if (!_reqRef) return;
|
|
610
|
+
debugLog(`[Server] setImmediate: pushing body=${_bodyToPush?.length ?? 0}, EOF=${_isComplete}`);
|
|
611
|
+
if (_bodyToPush) _reqRef.push(_bodyToPush);
|
|
612
|
+
if (_isComplete) {
|
|
613
|
+
_reqRef.complete = true;
|
|
614
|
+
if (_trailers) _reqRef.trailers = _trailers;
|
|
615
|
+
_reqRef.push(null);
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
} else {
|
|
619
|
+
// Subsequent-packet case: push immediately
|
|
620
|
+
debugLog(`[Server] Pushing body/EOF immediately (subsequent-packet)`);
|
|
621
|
+
if (_bodyToPush) _reqRef!.push(_bodyToPush);
|
|
622
|
+
if (_isComplete) {
|
|
623
|
+
_reqRef!.complete = true;
|
|
624
|
+
if (_trailers) _reqRef!.trailers = _trailers;
|
|
625
|
+
_reqRef!.push(null);
|
|
626
|
+
}
|
|
584
627
|
}
|
|
585
|
-
req.push(null);
|
|
586
628
|
}
|
|
587
629
|
|
|
588
630
|
// For Keep-Alive, try to parse remaining buffer in case of pipelining
|
|
@@ -1034,9 +1076,10 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
1034
1076
|
this.socket = socket;
|
|
1035
1077
|
this._connected = true;
|
|
1036
1078
|
this.emit('socket', this.socket);
|
|
1037
|
-
//
|
|
1038
|
-
|
|
1079
|
+
// IMPORTANT: attach response listeners BEFORE flushing writes.
|
|
1080
|
+
// If we flush first, the server may respond before we have a data listener.
|
|
1039
1081
|
this._attachSocketListeners();
|
|
1082
|
+
this._flushPendingWrites();
|
|
1040
1083
|
} else {
|
|
1041
1084
|
this._connect();
|
|
1042
1085
|
}
|
|
@@ -1051,15 +1094,18 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
1051
1094
|
this.emit('error', err);
|
|
1052
1095
|
return;
|
|
1053
1096
|
}
|
|
1054
|
-
debugLog(`ClientRequest._connect: Socket connected
|
|
1055
|
-
debugLog(`[HTTP] _connect: Socket connected!`);
|
|
1097
|
+
debugLog(`ClientRequest._connect: Socket connected!`);
|
|
1056
1098
|
this.socket = socket;
|
|
1057
1099
|
this._connected = true;
|
|
1058
1100
|
this.emit('socket', this.socket);
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
this._flushPendingWrites();
|
|
1101
|
+
// IMPORTANT: attach response listeners BEFORE flushing writes.
|
|
1102
|
+
// If we flush first, the server may respond before we have a data listener.
|
|
1062
1103
|
this._attachSocketListeners();
|
|
1104
|
+
// _flushPendingWrites() internally calls _sendRequest() if headers not sent yet.
|
|
1105
|
+
// Do NOT call _sendRequest() separately here — _flushPendingWrites() needs to
|
|
1106
|
+
// inspect headersSent and _pendingWrites together so it can set Content-Length
|
|
1107
|
+
// before sending headers (to avoid chunked encoding when body is already known).
|
|
1108
|
+
this._flushPendingWrites();
|
|
1063
1109
|
};
|
|
1064
1110
|
|
|
1065
1111
|
this.socket = agent.createConnection(this._options, connectCallback);
|
|
@@ -1208,10 +1254,31 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
1208
1254
|
private _isFlushing = false;
|
|
1209
1255
|
private _flushPendingWrites() {
|
|
1210
1256
|
if (!this.socket || this._isFlushing) return;
|
|
1211
|
-
|
|
1257
|
+
|
|
1212
1258
|
this._isFlushing = true;
|
|
1213
1259
|
try {
|
|
1214
|
-
if (!this.headersSent)
|
|
1260
|
+
if (!this.headersSent) {
|
|
1261
|
+
// KEY FIX: When all body data is already queued AND the request is ending,
|
|
1262
|
+
// we can calculate the exact Content-Length and avoid chunked encoding.
|
|
1263
|
+
//
|
|
1264
|
+
// Why this matters: without Content-Length, the request is sent with
|
|
1265
|
+
// Transfer-Encoding: chunked. The Rust HTTP parser on the server side
|
|
1266
|
+
// stores chunked body bytes in its internal buffer after parsing headers,
|
|
1267
|
+
// but calling parser.feed(empty_buffer) to drain those bytes does NOT work
|
|
1268
|
+
// — the drain call returns empty metadata and the body is permanently lost.
|
|
1269
|
+
//
|
|
1270
|
+
// By setting Content-Length here (when we have all the data), the body is
|
|
1271
|
+
// sent as raw bytes. The server parser simply reads N bytes and marks the
|
|
1272
|
+
// request complete — no chunked framing, no drain issues.
|
|
1273
|
+
if (this._ended
|
|
1274
|
+
&& !this.hasHeader('Content-Length')
|
|
1275
|
+
&& !this.hasHeader('Transfer-Encoding')
|
|
1276
|
+
&& this._pendingWrites.length > 0) {
|
|
1277
|
+
const totalLen = this._getPendingBodyLength();
|
|
1278
|
+
this.setHeader('Content-Length', totalLen);
|
|
1279
|
+
}
|
|
1280
|
+
this._sendRequest();
|
|
1281
|
+
}
|
|
1215
1282
|
|
|
1216
1283
|
// If we are waiting for 100-continue, don't flush yet
|
|
1217
1284
|
if (this._expectContinue && !this._continueReceived) {
|
|
@@ -1228,7 +1295,7 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
1228
1295
|
super._write(pending.chunk, pending.encoding, pending.callback);
|
|
1229
1296
|
}
|
|
1230
1297
|
}
|
|
1231
|
-
|
|
1298
|
+
|
|
1232
1299
|
if (this._ended) {
|
|
1233
1300
|
super.end();
|
|
1234
1301
|
}
|
|
@@ -1288,7 +1355,7 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
1288
1355
|
}
|
|
1289
1356
|
|
|
1290
1357
|
debugLog(`ClientRequest.end() called, connected=${this._connected}, chunk=${!!chunk}`);
|
|
1291
|
-
|
|
1358
|
+
|
|
1292
1359
|
if (chunk != null) {
|
|
1293
1360
|
this._hasBody = true;
|
|
1294
1361
|
if (!this.headersSent && !this.hasHeader('Content-Length')) {
|
|
@@ -1298,7 +1365,7 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
1298
1365
|
// Use this.write to handle pending queue if not connected
|
|
1299
1366
|
this.write(chunk, encoding);
|
|
1300
1367
|
}
|
|
1301
|
-
|
|
1368
|
+
|
|
1302
1369
|
this._ended = true;
|
|
1303
1370
|
|
|
1304
1371
|
if (this._connected) {
|
package/src/net.ts
CHANGED
|
@@ -91,6 +91,10 @@ function initWithConfig(config: NetConfig): void {
|
|
|
91
91
|
if (config.debug !== undefined) {
|
|
92
92
|
setVerbose(config.debug);
|
|
93
93
|
}
|
|
94
|
+
// Inject dispatcher for async events to avoid thread starvation/deadlocks
|
|
95
|
+
if ((Driver as any).installDispatcher) {
|
|
96
|
+
(Driver as any).installDispatcher();
|
|
97
|
+
}
|
|
94
98
|
Driver.initWithConfig(config);
|
|
95
99
|
}
|
|
96
100
|
|