uws-react-native 0.0.1 → 0.0.2
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/README.md +14 -2
- package/cpp/app/HttpRequestObject.h +3 -3
- package/cpp/app/HttpResponseObject.h +81 -121
- package/cpp/app/TemplatedAppObject.h +58 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ This repository does also contain the [example](https://github.com/RakaDoank/uws
|
|
|
33
33
|
|
|
34
34
|
## Compatibility
|
|
35
35
|
|
|
36
|
-
`uws-react-native` is
|
|
36
|
+
`uws-react-native` is only compatible for new architecture of React Native.
|
|
37
37
|
|
|
38
38
|
### Platform
|
|
39
39
|
|
|
@@ -53,6 +53,8 @@ There are some new APIs in uws-react-native. The most reason is because of the d
|
|
|
53
53
|
- `onFullData`. This is an equivalent of `res.collectBody` from uWebSockets.js, but to set the max size, provide it through route method handler options
|
|
54
54
|
- `onFullDataText`. Same as the `onFullData`, but provides the body data in raw text instead of ArrayBuffer. This is useful if you are using React Native 0.84 version or older because TextDecoder is natively supported only in React Native 0.85 version
|
|
55
55
|
|
|
56
|
+
---
|
|
57
|
+
|
|
56
58
|
## Development & Research
|
|
57
59
|
|
|
58
60
|
uws-react-native is still in heavy development and research. All the uWebSockets instances are not fully implemented yet.
|
|
@@ -73,7 +75,11 @@ Intentionally, we make the uWebSockets runs in another thread, therefore we have
|
|
|
73
75
|
|
|
74
76
|
We have another issue because of the uWebSockets runs in another thread. From the JSI C++ side, we have to assume any JS function as a callback especially the route method handler is asynchronous. We cannot make a sync call to the JS function from an arbitrary thread to the JS thread, and it makes JS call to the uWebSockets runner is also late.
|
|
75
77
|
|
|
76
|
-
|
|
78
|
+
There some topics you may to read regarding this threading research
|
|
79
|
+
|
|
80
|
+
#### Predefined Instances
|
|
81
|
+
|
|
82
|
+
For the threading case especially, we have to predefined two instances in the C++ uWebSockets route method handler because of the late call, and the late call would not be tolerated by the uWebSockets internally for some cases
|
|
77
83
|
|
|
78
84
|
- `res.onAborted`
|
|
79
85
|
|
|
@@ -82,3 +88,9 @@ We have to predefined the `onAborted` callback in C++ side and prevent `res.end`
|
|
|
82
88
|
- `res.onDataV2`
|
|
83
89
|
|
|
84
90
|
For the `onData` and `onDataV2`, we have predefined it with a single `onDataV2` callback in C++ side. If this method is not predefined, uWebSockets will not collect any incoming body data. If a route method handler is sure that it doesn't expect any body data at all, a route method handler can disable the body reading through the third argument in `any`, `get`, `post`, and other route methods.
|
|
91
|
+
|
|
92
|
+
#### About Worker Thread
|
|
93
|
+
|
|
94
|
+
In theory, we can create another JavaScript runtime with [react-native-worklets](https://docs.swmansion.com/react-native-worklets/) and tie with our uWebSockets runner. It would solve a lot of late communication problem between uWebSockets runner and JavaScript thread that we embrace right now, but it also introduces new major issue, which is developer experience. It sounds like not a big problem, but it may bigger than you think.
|
|
95
|
+
|
|
96
|
+
Think of this sample case, you want to use uws-react-native server for a simple CRUD with a local database in an app. You probably know SQLite can be used in Android, iOS, macOS, even Windows app, with your own adapter or a known library that support SQLite integration in React Native such as [op-sqlite](https://github.com/OP-Engineering/op-sqlite), and [Expo SQLite](https://docs.expo.dev/versions/latest/sdk/sqlite). If you want to use that known library, this case would not works at all, because op-sqlite and/or Expo SQLite is tied to the default JavaScript runtime. Even, you cannot use any React Native non-JS-only libraries in arbitrary JavaScript thread that has created by react-native-worklets if the library you want to use is using the default JavaScript runtime. If you really want to achieve the goal of this case, you have to create your own library for the SQLite database by yourself and tie-up with the JavaScript runtime. Probably, in the future we would still provide that worker thread with react-native-worklets.
|
|
@@ -33,9 +33,9 @@ public:
|
|
|
33
33
|
std::for_each(req->begin(),
|
|
34
34
|
req->end(),
|
|
35
35
|
[&rt_1, callback_ = std::move(callback)](const auto &item) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
callback_.call(rt_1,
|
|
37
|
+
facebook::jsi::String::createFromAscii(rt_1, std::string(item.first)),
|
|
38
|
+
facebook::jsi::String::createFromAscii(rt_1, std::string(item.second)));
|
|
39
39
|
});
|
|
40
40
|
return facebook::jsi::Value::undefined();
|
|
41
41
|
}));
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
namespace uws_react_native {
|
|
10
10
|
|
|
11
|
-
struct HttpResponseObjectOptions {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
};
|
|
11
|
+
// struct HttpResponseObjectOptions {
|
|
12
|
+
// bool disableBodyRead;
|
|
13
|
+
// unsigned long maxBodySize;
|
|
14
|
+
// };
|
|
15
15
|
|
|
16
16
|
class HttpResponseObject : public facebook::jsi::Object {
|
|
17
17
|
|
|
@@ -52,63 +52,6 @@ private:
|
|
|
52
52
|
bool isStopCollecting = false;
|
|
53
53
|
} OnDataV2Assignee;
|
|
54
54
|
|
|
55
|
-
void setChunk(std::string_view chunk,
|
|
56
|
-
unsigned long maxRemainingBodyLength) {
|
|
57
|
-
if(!this->OnDataV2Assignee.buffer) {
|
|
58
|
-
this->OnDataV2Assignee.buffer = std::make_shared<std::vector<char>>();
|
|
59
|
-
this->OnDataV2Assignee.buffer->reserve(maxRemainingBodyLength + chunk.size()); // preallocate with hint
|
|
60
|
-
}
|
|
61
|
-
this->OnDataV2Assignee.buffer->insert(this->OnDataV2Assignee.buffer->end(), chunk.begin(), chunk.end());
|
|
62
|
-
// this->OnDataV2Assignee.chunk.append(chunk.data(), chunk.size());
|
|
63
|
-
this->OnDataV2Assignee.maxRemainingBodyLength = maxRemainingBodyLength;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Probably bad name,
|
|
68
|
-
* it's used either for "onData", "onDataV2", or "onFullData", and combined for "onFullDataText"
|
|
69
|
-
*/
|
|
70
|
-
void invokeOnDataHandler() {
|
|
71
|
-
if(
|
|
72
|
-
!this->OnDataV2Assignee.isCallbackForFullChunk ||
|
|
73
|
-
this->OnDataV2Assignee.maxRemainingBodyLength == 0
|
|
74
|
-
) {
|
|
75
|
-
/// HELP me the better way to pass JSI ArrayBuffer here.
|
|
76
|
-
/// with faster buffer or anything
|
|
77
|
-
|
|
78
|
-
/// I have tested that,
|
|
79
|
-
/// when we captured the buffer by reference into the lambda,
|
|
80
|
-
/// in the middle of stream, it is often giving inaccuracy of
|
|
81
|
-
/// JS ArrayBuffer.byteLength in the `onDataV2` argument
|
|
82
|
-
/// when it's compared to the maxRemainingBodyLength differentiation that captured by value.
|
|
83
|
-
/// It's still accurate when it's finished.
|
|
84
|
-
|
|
85
|
-
/// While capturing the buffer by value is often accurate.
|
|
86
|
-
/// but is it slower?
|
|
87
|
-
|
|
88
|
-
if(this->OnDataV2Assignee.callback) {
|
|
89
|
-
this->OnDataV2Assignee.callback
|
|
90
|
-
->callWithPriority(facebook::react::SchedulerPriority::ImmediatePriority,
|
|
91
|
-
[buffer = this->OnDataV2Assignee.buffer, maxRemainingBodyLength = this->OnDataV2Assignee.maxRemainingBodyLength](facebook::jsi::Runtime &rt, facebook::jsi::Function &cb) {
|
|
92
|
-
auto mutableBuffer = CharsMutableBuffer(buffer);
|
|
93
|
-
|
|
94
|
-
cb.call(rt,
|
|
95
|
-
facebook::jsi::ArrayBuffer(rt, std::make_shared<CharsMutableBuffer>(std::move(mutableBuffer))),
|
|
96
|
-
facebook::jsi::BigInt::fromUint64(rt, maxRemainingBodyLength));
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if(this->OnDataV2Assignee.callbackStr) {
|
|
101
|
-
this->OnDataV2Assignee.callbackStr
|
|
102
|
-
->callWithPriority(facebook::react::SchedulerPriority::ImmediatePriority,
|
|
103
|
-
[buffer = this->OnDataV2Assignee.buffer, maxRemainingBodyLength = this->OnDataV2Assignee.maxRemainingBodyLength](facebook::jsi::Runtime &rt, facebook::jsi::Function &cb) {
|
|
104
|
-
cb.call(rt,
|
|
105
|
-
std::string(buffer->begin(), buffer->end()),
|
|
106
|
-
facebook::jsi::BigInt::fromUint64(rt, maxRemainingBodyLength));
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
55
|
// void preEnd(facebook::jsi::Runtime &rt) const {
|
|
113
56
|
// if(this->OnAbortedAssignee.alreadyAborted) {
|
|
114
57
|
// /// Stated from uWebSockets
|
|
@@ -124,8 +67,8 @@ private:
|
|
|
124
67
|
public:
|
|
125
68
|
HttpResponseObject(facebook::jsi::Runtime &rt,
|
|
126
69
|
uWS::HttpResponse<false> *res,
|
|
127
|
-
std::shared_ptr<facebook::react::CallInvoker> &jsInvoker
|
|
128
|
-
std::optional<HttpResponseObjectOptions> &&options = HttpResponseObjectOptions{ .disableBodyRead = false, .maxBodySize = 0 }) : facebook::jsi::Object(rt) {
|
|
70
|
+
std::shared_ptr<facebook::react::CallInvoker> &jsInvoker
|
|
71
|
+
/* std::optional<HttpResponseObjectOptions> &&options = HttpResponseObjectOptions{ .disableBodyRead = false, .maxBodySize = 0 } */) : facebook::jsi::Object(rt) {
|
|
129
72
|
|
|
130
73
|
this->setProperty(rt,
|
|
131
74
|
"close",
|
|
@@ -377,7 +320,6 @@ public:
|
|
|
377
320
|
this->OnDataV2Assignee.callback
|
|
378
321
|
->callWithPriority(facebook::react::SchedulerPriority::ImmediatePriority,
|
|
379
322
|
[this](facebook::jsi::Runtime &rt, facebook::jsi::Function &cb) {
|
|
380
|
-
// auto stringMutableBuffer = facebook::jsi::StringMutableBuffer(&this->OnDataV2Assignee.chunk);
|
|
381
323
|
auto mutableBuffer = CharsMutableBuffer(this->OnDataV2Assignee.buffer.get());
|
|
382
324
|
cb.call(rt,
|
|
383
325
|
facebook::jsi::ArrayBuffer(rt, std::make_shared<CharsMutableBuffer>(std::move(mutableBuffer))));
|
|
@@ -558,65 +500,83 @@ public:
|
|
|
558
500
|
return {rt_1, thisValue};
|
|
559
501
|
}));
|
|
560
502
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
///
|
|
569
|
-
/// I thought `onAborted` is just a callback or event listener.
|
|
570
|
-
res->onAborted([this]() {
|
|
571
|
-
this->OnAbortedAssignee.alreadyAborted = true;
|
|
572
|
-
|
|
573
|
-
if(this->OnAbortedAssignee.callback) {
|
|
574
|
-
this->OnAbortedAssignee.callback->call(facebook::jsi::Value::undefined());
|
|
575
|
-
}
|
|
576
|
-
});
|
|
577
|
-
|
|
578
|
-
/// Sadly, we can't do late assignment to the onDataV2 and onData.
|
|
579
|
-
/// uWebSockets will do nothing to our handler if we assign the lambda so late.
|
|
580
|
-
/// So we have to predefined onDataV2 handler here, and save the chunk.
|
|
581
|
-
if(!options->disableBodyRead) {
|
|
582
|
-
res->onDataV2([this, maxBodySize = options->maxBodySize](auto chunk, auto maxRemainingBodyLength) {
|
|
583
|
-
if(this->OnDataV2Assignee.isStopCollecting) {
|
|
584
|
-
return;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
if(maxBodySize > 0) {
|
|
588
|
-
auto chunkSize = chunk.size();
|
|
589
|
-
auto currentChunkSize = this->OnDataV2Assignee.buffer ? this->OnDataV2Assignee.buffer->size() : 0;
|
|
590
|
-
|
|
591
|
-
/// First and possibly only chunk
|
|
592
|
-
if(currentChunkSize == 0 && chunkSize > maxBodySize) {
|
|
593
|
-
this->OnDataV2Assignee.isStopCollecting = true;
|
|
594
|
-
/// set the first chunk
|
|
595
|
-
this->setChunk(chunk, maxRemainingBodyLength);
|
|
596
|
-
this->invokeOnDataHandler();
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
/// subsequent chunks overflow
|
|
601
|
-
if(currentChunkSize > 0 && chunkSize > maxBodySize - currentChunkSize) {
|
|
602
|
-
/// tell to JS that we already stop collecting chunk
|
|
603
|
-
/// and invoke the JSI onData / onDataText / onDataV2 / onFullData / onFullDataText handler immediately
|
|
604
|
-
this->OnDataV2Assignee.isStopCollecting = true;
|
|
605
|
-
this->invokeOnDataHandler();
|
|
606
|
-
return;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
this->setChunk(chunk, maxRemainingBodyLength);
|
|
611
|
-
this->invokeOnDataHandler();
|
|
612
|
-
|
|
613
|
-
/// About the invokeOnDataHandler,
|
|
614
|
-
/// JS call may late
|
|
615
|
-
/// it will invokes the handler once when user pass the handler.
|
|
616
|
-
});
|
|
503
|
+
} // HttpResponseObject
|
|
504
|
+
|
|
505
|
+
void invokeOnAbortedHandler() {
|
|
506
|
+
this->OnAbortedAssignee.alreadyAborted = true;
|
|
507
|
+
|
|
508
|
+
if(this->OnAbortedAssignee.callback) {
|
|
509
|
+
this->OnAbortedAssignee.callback->call(facebook::jsi::Value::undefined());
|
|
617
510
|
}
|
|
511
|
+
}
|
|
618
512
|
|
|
619
|
-
|
|
513
|
+
bool isStopCollectingData() const {
|
|
514
|
+
return this->OnDataV2Assignee.isStopCollecting;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
void stopCollectingData() {
|
|
518
|
+
this->OnDataV2Assignee.isStopCollecting = true;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
void updateBuffer(std::string_view chunk,
|
|
522
|
+
unsigned long maxRemainingBodyLength) {
|
|
523
|
+
if(!this->OnDataV2Assignee.buffer) {
|
|
524
|
+
this->OnDataV2Assignee.buffer = std::make_shared<std::vector<char>>();
|
|
525
|
+
this->OnDataV2Assignee.buffer->reserve(maxRemainingBodyLength + chunk.size()); // preallocate with hint
|
|
526
|
+
}
|
|
527
|
+
this->OnDataV2Assignee.buffer->insert(this->OnDataV2Assignee.buffer->end(), chunk.begin(), chunk.end());
|
|
528
|
+
this->OnDataV2Assignee.maxRemainingBodyLength = maxRemainingBodyLength;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
size_t getBufferSize() const {
|
|
532
|
+
return this->OnDataV2Assignee.buffer->size();
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Probably bad name,
|
|
537
|
+
* it's used either for "onData", "onDataV2", or "onFullData", and combined for "onFullDataText"
|
|
538
|
+
*/
|
|
539
|
+
void invokeOnDataHandler() {
|
|
540
|
+
if(
|
|
541
|
+
!this->OnDataV2Assignee.isCallbackForFullChunk ||
|
|
542
|
+
this->OnDataV2Assignee.maxRemainingBodyLength == 0
|
|
543
|
+
) {
|
|
544
|
+
/// HELP me the better way to pass JSI ArrayBuffer here.
|
|
545
|
+
/// with faster buffer or anything
|
|
546
|
+
|
|
547
|
+
/// I have tested that,
|
|
548
|
+
/// when we captured the buffer by reference into the lambda,
|
|
549
|
+
/// in the middle of stream, it is often giving inaccuracy of
|
|
550
|
+
/// JS ArrayBuffer.byteLength in the `onDataV2` argument
|
|
551
|
+
/// when it's compared to the maxRemainingBodyLength differentiation that captured by value.
|
|
552
|
+
/// It's still accurate when it's finished.
|
|
553
|
+
|
|
554
|
+
/// While capturing the buffer by value is often accurate.
|
|
555
|
+
/// but is it slower?
|
|
556
|
+
|
|
557
|
+
if(this->OnDataV2Assignee.callback) {
|
|
558
|
+
this->OnDataV2Assignee.callback
|
|
559
|
+
->callWithPriority(facebook::react::SchedulerPriority::ImmediatePriority,
|
|
560
|
+
[buffer = this->OnDataV2Assignee.buffer, maxRemainingBodyLength = this->OnDataV2Assignee.maxRemainingBodyLength](facebook::jsi::Runtime &rt, facebook::jsi::Function &cb) {
|
|
561
|
+
auto mutableBuffer = CharsMutableBuffer(buffer);
|
|
562
|
+
|
|
563
|
+
cb.call(rt,
|
|
564
|
+
facebook::jsi::ArrayBuffer(rt, std::make_shared<CharsMutableBuffer>(std::move(mutableBuffer))),
|
|
565
|
+
facebook::jsi::BigInt::fromUint64(rt, maxRemainingBodyLength));
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if(this->OnDataV2Assignee.callbackStr) {
|
|
570
|
+
this->OnDataV2Assignee.callbackStr
|
|
571
|
+
->callWithPriority(facebook::react::SchedulerPriority::ImmediatePriority,
|
|
572
|
+
[buffer = this->OnDataV2Assignee.buffer, maxRemainingBodyLength = this->OnDataV2Assignee.maxRemainingBodyLength](facebook::jsi::Runtime &rt, facebook::jsi::Function &cb) {
|
|
573
|
+
cb.call(rt,
|
|
574
|
+
std::string(buffer->begin(), buffer->end()),
|
|
575
|
+
facebook::jsi::BigInt::fromUint64(rt, maxRemainingBodyLength));
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
620
580
|
|
|
621
581
|
};
|
|
622
582
|
|
|
@@ -90,11 +90,10 @@ private:
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
std::function<void (uWS::HttpResponse<false> *res, uWS::HttpRequest *req)> uwsRouteHandler = [
|
|
93
|
+
std::function<void (uWS::HttpResponse<false> *res, uWS::HttpRequest *req)> uwsRouteHandler = [disableBodyRead, maxBodySize, &rt, &jsInvoker, asyncCallback = facebook::react::AsyncCallback(rt, std::move(callback), jsInvoker)](uWS::HttpResponse<false> *res, uWS::HttpRequest *req) {
|
|
94
94
|
auto httpResponseObject = std::make_shared<HttpResponseObject>(rt,
|
|
95
95
|
res,
|
|
96
|
-
jsInvoker
|
|
97
|
-
HttpResponseObjectOptions{ .disableBodyRead = disableBodyRead, .maxBodySize = maxBodySize });
|
|
96
|
+
jsInvoker);
|
|
98
97
|
|
|
99
98
|
auto httpRequestObject = std::make_shared<HttpRequestObject>(rt, req);
|
|
100
99
|
|
|
@@ -103,6 +102,62 @@ private:
|
|
|
103
102
|
*httpResponseObject,
|
|
104
103
|
*httpRequestObject);
|
|
105
104
|
});
|
|
105
|
+
|
|
106
|
+
/// We have to make JS call asynchronously because the uWebSockets app run at different thread.
|
|
107
|
+
/// See the `react_native_uws::AppRunner`, and `facebook::react::AsyncCallback`.
|
|
108
|
+
/// So this predefined `onAborted` assignment below is to tell that
|
|
109
|
+
/// uWebSockets has to wait until JS call finished with the `res.end() or res.tryEnd()`.
|
|
110
|
+
///
|
|
111
|
+
/// Stated from uWebSockets
|
|
112
|
+
/// `Returning from a request handler without responding or attaching an onAborted handler is ill-use`
|
|
113
|
+
///
|
|
114
|
+
/// I thought `onAborted` is just a callback or event listener.
|
|
115
|
+
res->onAborted([httpResponseObject]() {
|
|
116
|
+
httpResponseObject->invokeOnAbortedHandler();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
/// Sadly, we can't do late assignment to the onDataV2 and onData.
|
|
120
|
+
/// uWebSockets will do nothing to our handler if we assign the lambda so late.
|
|
121
|
+
/// So we have to predefined onDataV2 handler here, and save the chunk.
|
|
122
|
+
if(!disableBodyRead) {
|
|
123
|
+
res->onDataV2([httpResponseObject, maxBodySize](auto chunk, auto maxRemainingBodyLength) {
|
|
124
|
+
if(httpResponseObject->isStopCollectingData()) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if(maxBodySize > 0) {
|
|
129
|
+
auto chunkSize = chunk.size();
|
|
130
|
+
auto currentChunkSize = httpResponseObject->getBufferSize();
|
|
131
|
+
|
|
132
|
+
/// First and possibly only chunk
|
|
133
|
+
if(currentChunkSize == 0 && chunkSize > maxBodySize) {
|
|
134
|
+
httpResponseObject->stopCollectingData();
|
|
135
|
+
/// set the first chunk
|
|
136
|
+
httpResponseObject->updateBuffer(chunk, maxRemainingBodyLength);
|
|
137
|
+
httpResponseObject->invokeOnDataHandler();
|
|
138
|
+
/// Don't worry,
|
|
139
|
+
/// JS call may late
|
|
140
|
+
/// it will invokes the handler once when user pass the handler.
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// subsequent chunks overflow
|
|
145
|
+
if(currentChunkSize > 0 && chunkSize > maxBodySize - currentChunkSize) {
|
|
146
|
+
/// tell to JS that we already stop collecting chunk
|
|
147
|
+
/// and invoke the JSI onData / onDataText / onDataV2 / onFullData / onFullDataText handler immediately
|
|
148
|
+
httpResponseObject->stopCollectingData();
|
|
149
|
+
httpResponseObject->invokeOnDataHandler();
|
|
150
|
+
/// Don't worry,
|
|
151
|
+
/// JS call may late
|
|
152
|
+
/// it will invokes the handler once when user pass the handler.
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
httpResponseObject->updateBuffer(chunk, maxRemainingBodyLength);
|
|
158
|
+
httpResponseObject->invokeOnDataHandler();
|
|
159
|
+
});
|
|
160
|
+
}
|
|
106
161
|
};
|
|
107
162
|
|
|
108
163
|
if(method == UwsRouteMethod::ANY) {
|
package/package.json
CHANGED