uwebsockets 2.0.0 → 2.2.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 (95) hide show
  1. package/Makefile +11 -0
  2. package/README.md +38 -44
  3. package/build.c +185 -0
  4. package/docs/Makefile +2 -0
  5. package/docs/generated/.nojekyll +1 -0
  6. package/docs/generated/assets/highlight.css +22 -0
  7. package/docs/generated/assets/main.js +4 -0
  8. package/docs/generated/assets/search.js +1 -0
  9. package/docs/generated/assets/style.css +1280 -0
  10. package/docs/generated/enums/ListenOptions.html +70 -0
  11. package/docs/generated/functions/App.html +94 -0
  12. package/docs/generated/functions/SSLApp.html +93 -0
  13. package/docs/generated/functions/getParts.html +95 -0
  14. package/docs/generated/functions/us_listen_socket_close.html +93 -0
  15. package/docs/generated/functions/us_socket_local_port.html +93 -0
  16. package/docs/generated/index.html +131 -0
  17. package/docs/generated/interfaces/AppOptions.html +115 -0
  18. package/docs/generated/interfaces/HttpRequest.html +204 -0
  19. package/docs/generated/interfaces/HttpResponse.html +400 -0
  20. package/docs/generated/interfaces/MultipartField.html +88 -0
  21. package/docs/generated/interfaces/TemplatedApp.html +595 -0
  22. package/docs/generated/interfaces/WebSocket.html +303 -0
  23. package/docs/generated/interfaces/WebSocketBehavior.html +331 -0
  24. package/docs/generated/interfaces/us_listen_socket.html +89 -0
  25. package/docs/generated/interfaces/us_socket.html +89 -0
  26. package/docs/generated/interfaces/us_socket_context_t.html +89 -0
  27. package/docs/generated/types/CompressOptions.html +84 -0
  28. package/docs/generated/types/RecognizedString.html +89 -0
  29. package/docs/generated/variables/DEDICATED_COMPRESSOR_128KB.html +84 -0
  30. package/docs/generated/variables/DEDICATED_COMPRESSOR_16KB.html +84 -0
  31. package/docs/generated/variables/DEDICATED_COMPRESSOR_256KB.html +84 -0
  32. package/docs/generated/variables/DEDICATED_COMPRESSOR_32KB.html +84 -0
  33. package/docs/generated/variables/DEDICATED_COMPRESSOR_3KB.html +84 -0
  34. package/docs/generated/variables/DEDICATED_COMPRESSOR_4KB.html +84 -0
  35. package/docs/generated/variables/DEDICATED_COMPRESSOR_64KB.html +84 -0
  36. package/docs/generated/variables/DEDICATED_COMPRESSOR_8KB.html +84 -0
  37. package/docs/generated/variables/DEDICATED_DECOMPRESSOR.html +84 -0
  38. package/docs/generated/variables/DEDICATED_DECOMPRESSOR_16KB.html +84 -0
  39. package/docs/generated/variables/DEDICATED_DECOMPRESSOR_1KB.html +84 -0
  40. package/docs/generated/variables/DEDICATED_DECOMPRESSOR_2KB.html +84 -0
  41. package/docs/generated/variables/DEDICATED_DECOMPRESSOR_32KB.html +84 -0
  42. package/docs/generated/variables/DEDICATED_DECOMPRESSOR_4KB.html +84 -0
  43. package/docs/generated/variables/DEDICATED_DECOMPRESSOR_512B.html +84 -0
  44. package/docs/generated/variables/DEDICATED_DECOMPRESSOR_8KB.html +84 -0
  45. package/docs/generated/variables/DISABLED.html +84 -0
  46. package/docs/generated/variables/SHARED_COMPRESSOR.html +84 -0
  47. package/docs/generated/variables/SHARED_DECOMPRESSOR.html +84 -0
  48. package/docs/index.d.ts +395 -0
  49. package/docs/report.pdf +0 -0
  50. package/docs/tsconfig.json +11 -0
  51. package/examples/AsyncFunction.js +43 -0
  52. package/examples/AutomaticPortSelection.js +20 -0
  53. package/examples/Backpressure.js +75 -0
  54. package/examples/Broadcast.js +39 -0
  55. package/examples/GracefulShutdown.js +39 -0
  56. package/examples/H3lloWorld.js +23 -0
  57. package/examples/Headers.js +31 -0
  58. package/examples/HelloWorld.js +18 -0
  59. package/examples/JsonPost.js +72 -0
  60. package/examples/ProxyProtocol.js +25 -0
  61. package/examples/PubSub.js +57 -0
  62. package/examples/RateLimit.js +69 -0
  63. package/examples/Router.js +30 -0
  64. package/examples/ServerName.js +34 -0
  65. package/examples/ServerSentEvents.js +48 -0
  66. package/examples/SlowReceiver.js +53 -0
  67. package/examples/Upgrade.js +51 -0
  68. package/examples/UpgradeAsync.js +80 -0
  69. package/examples/Upload.js +36 -0
  70. package/examples/VideoStreamer.js +115 -0
  71. package/examples/VideoStreamerSync.js +42 -0
  72. package/examples/WebSockets.js +37 -0
  73. package/examples/WorkerThreads.js +33 -0
  74. package/misc/Manycast latency comparison [lower is better].png +0 -0
  75. package/misc/addon.pro +24 -0
  76. package/misc/cert.pem +21 -0
  77. package/misc/chart.png +0 -0
  78. package/misc/features_strip.png +0 -0
  79. package/misc/host.pro +31 -0
  80. package/misc/key.pem +28 -0
  81. package/misc/logo.svg +1 -0
  82. package/misc/streaming.png +0 -0
  83. package/package.json +8 -4
  84. package/src/AppWrapper.h +734 -0
  85. package/src/HttpRequestWrapper.h +196 -0
  86. package/src/HttpResponseWrapper.h +463 -0
  87. package/src/Utilities.h +172 -0
  88. package/src/WebSocketWrapper.h +348 -0
  89. package/src/addon.cpp +437 -0
  90. package/src/host.cpp +115 -0
  91. package/src/uws.js +24 -0
  92. package/tests/Autobahn.js +102 -0
  93. package/tests/Hammer.js +203 -0
  94. package/tests/Makefile +2 -0
  95. package/tests/fuzzingclient.json +28 -0
@@ -0,0 +1,196 @@
1
+ /*
2
+ * Authored by Alex Hultman, 2018-2020.
3
+ * Intellectual property of third-party.
4
+
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "App.h"
19
+ #include "Utilities.h"
20
+
21
+ #include <v8.h>
22
+ using namespace v8;
23
+
24
+ /* This one is the same for SSL and non-SSL */
25
+ struct HttpRequestWrapper {
26
+
27
+ /* Unwraps the HttpRequest from V8 object */
28
+ template <int QUIC>
29
+ static inline constexpr decltype(auto) getHttpRequest(const FunctionCallbackInfo<Value> &args) {
30
+ Isolate *isolate = args.GetIsolate();
31
+ /* Thow on deleted request */
32
+ auto *req = (uWS::HttpRequest *) args.Holder()->GetAlignedPointerFromInternalField(0);
33
+ if (!req) {
34
+ args.GetReturnValue().Set(isolate->ThrowException(v8::Exception::Error(String::NewFromUtf8(isolate, "uWS.HttpRequest must not be accessed after await or route handler return. See documentation for uWS.HttpRequest and consult the user manual.", NewStringType::kNormal).ToLocalChecked())));
35
+ }
36
+
37
+ if constexpr (QUIC) {
38
+ return (uWS::Http3Request *) req;
39
+ } else {
40
+ return req;
41
+ }
42
+ }
43
+
44
+ /* Takes function of string, string. Returns this (doesn't really but should) */
45
+ template <int QUIC>
46
+ static void req_forEach(const FunctionCallbackInfo<Value> &args) {
47
+ Isolate *isolate = args.GetIsolate();
48
+ auto *req = getHttpRequest<QUIC>(args);
49
+ if (req) {
50
+ Local<Function> cb = Local<Function>::Cast(args[0]);
51
+
52
+ for (auto p : *req) {
53
+ Local<Value> argv[] = {String::NewFromUtf8(isolate, p.first.data(), NewStringType::kNormal, p.first.length()).ToLocalChecked(),
54
+ String::NewFromUtf8(isolate, p.second.data(), NewStringType::kNormal, p.second.length()).ToLocalChecked()};
55
+ /* This one is also called from JS so no need for CallJS */
56
+ cb->Call(isolate->GetCurrentContext(), isolate->GetCurrentContext()->Global(), 2, argv).IsEmpty();
57
+ }
58
+ }
59
+ }
60
+
61
+ /* Takes int, returns string (must be in bounds) */
62
+ template <int QUIC>
63
+ static void req_getParameter(const FunctionCallbackInfo<Value> &args) {
64
+ Isolate *isolate = args.GetIsolate();
65
+ auto *req = getHttpRequest<QUIC>(args);
66
+ if (req) {
67
+ int index = args[0]->Uint32Value(isolate->GetCurrentContext()).ToChecked();
68
+ std::string_view parameter = req->getParameter(index);
69
+
70
+ args.GetReturnValue().Set(String::NewFromUtf8(isolate, parameter.data(), NewStringType::kNormal, parameter.length()).ToLocalChecked());
71
+ }
72
+ }
73
+
74
+ /* Takes nothing, returns string */
75
+ template <int QUIC>
76
+ static void req_getUrl(const FunctionCallbackInfo<Value> &args) {
77
+ Isolate *isolate = args.GetIsolate();
78
+ auto *req = getHttpRequest<QUIC>(args);
79
+ if (req) {
80
+ std::string_view url = req->getUrl();
81
+
82
+ args.GetReturnValue().Set(String::NewFromUtf8(isolate, url.data(), NewStringType::kNormal, url.length()).ToLocalChecked());
83
+ }
84
+ }
85
+
86
+ /* Takes String, returns String */
87
+ template <int QUIC>
88
+ static void req_getHeader(const FunctionCallbackInfo<Value> &args) {
89
+ Isolate *isolate = args.GetIsolate();
90
+ auto *req = getHttpRequest<QUIC>(args);
91
+ if (req) {
92
+ NativeString data(args.GetIsolate(), args[0]);
93
+ if (data.isInvalid(args)) {
94
+ return;
95
+ }
96
+
97
+ std::string_view header = req->getHeader(data.getString());
98
+
99
+ /* We want latin1 here */
100
+ args.GetReturnValue().Set(String::NewFromOneByte(isolate, (const uint8_t *)header.data(), NewStringType::kNormal, header.length()).ToLocalChecked());
101
+ }
102
+ }
103
+
104
+ /* Takes boolean, returns this */
105
+ template <int QUIC>
106
+ static void req_setYield(const FunctionCallbackInfo<Value> &args) {
107
+ Isolate *isolate = args.GetIsolate();
108
+ auto *req = getHttpRequest<QUIC>(args);
109
+ if (req) {
110
+ bool yield = args[0]->BooleanValue(isolate);
111
+ req->setYield(yield);
112
+
113
+ args.GetReturnValue().Set(args.Holder());
114
+ }
115
+ }
116
+
117
+ /* Takes nothing, returns string */
118
+ template <int QUIC>
119
+ static void req_getMethod(const FunctionCallbackInfo<Value> &args) {
120
+ Isolate *isolate = args.GetIsolate();
121
+ auto *req = getHttpRequest<QUIC>(args);
122
+ if (req) {
123
+ std::string_view method = req->getMethod();
124
+
125
+ args.GetReturnValue().Set(String::NewFromUtf8(isolate, method.data(), NewStringType::kNormal, method.length()).ToLocalChecked());
126
+ }
127
+ }
128
+
129
+ /* Takes nothing, returns string */
130
+ template <int QUIC>
131
+ static void req_getCaseSensitiveMethod(const FunctionCallbackInfo<Value> &args) {
132
+ Isolate *isolate = args.GetIsolate();
133
+ auto *req = getHttpRequest<QUIC>(args);
134
+ if (req) {
135
+ std::string_view method = req->getCaseSensitiveMethod();
136
+
137
+ args.GetReturnValue().Set(String::NewFromUtf8(isolate, method.data(), NewStringType::kNormal, method.length()).ToLocalChecked());
138
+ }
139
+ }
140
+
141
+ template <int QUIC>
142
+ static void req_getQuery(const FunctionCallbackInfo<Value> &args) {
143
+ Isolate *isolate = args.GetIsolate();
144
+ auto *req = getHttpRequest<QUIC>(args);
145
+ if (req) {
146
+ std::string_view query;
147
+
148
+ /* Do we have a key argument? */
149
+ if (args.Length() == 1) {
150
+ NativeString keyString(isolate, args[0]);
151
+ if (keyString.isInvalid(args)) {
152
+ return;
153
+ }
154
+
155
+ query = req->getQuery(keyString.getString());
156
+ } else {
157
+ query = req->getQuery();
158
+ }
159
+
160
+ /* If we have nullptr as data, it's not simply an empty string */
161
+ if (query.data() == nullptr) {
162
+ return;
163
+ }
164
+
165
+ args.GetReturnValue().Set(String::NewFromUtf8(isolate, query.data(), NewStringType::kNormal, query.length()).ToLocalChecked());
166
+ }
167
+ }
168
+
169
+ /* Returns a clonable object wrapping an HttpRequest */
170
+ template <int QUIC>
171
+ static Local<Object> init(Isolate *isolate) {
172
+ /* We do clone every request object, we could share them, they are illegal to use outside the function anyways */
173
+ Local<FunctionTemplate> reqTemplateLocal = FunctionTemplate::New(isolate);
174
+ reqTemplateLocal->SetClassName(String::NewFromUtf8(isolate, QUIC ? "uWS.Http3Request" : "uWS.HttpRequest", NewStringType::kNormal).ToLocalChecked());
175
+ reqTemplateLocal->InstanceTemplate()->SetInternalFieldCount(1);
176
+
177
+ /* Register our functions */
178
+ reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getHeader", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, req_getHeader<QUIC>));
179
+
180
+ if constexpr (!QUIC) {
181
+ reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getParameter", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, req_getParameter<QUIC>));
182
+ reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getUrl", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, req_getUrl<QUIC>));
183
+ reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getMethod", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, req_getMethod<QUIC>));
184
+ reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getCaseSensitiveMethod", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, req_getCaseSensitiveMethod<QUIC>));
185
+ reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getQuery", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, req_getQuery<QUIC>));
186
+ reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "forEach", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, req_forEach<QUIC>));
187
+ reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "setYield", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, req_setYield<QUIC>));
188
+ }
189
+
190
+
191
+ /* Create the template */
192
+ Local<Object> reqObjectLocal = reqTemplateLocal->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
193
+
194
+ return reqObjectLocal;
195
+ }
196
+ };
@@ -0,0 +1,463 @@
1
+ /*
2
+ * Authored by Alex Hultman, 2018-2020.
3
+ * Intellectual property of third-party.
4
+
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "App.h"
19
+ #include "Utilities.h"
20
+
21
+ #include <v8.h>
22
+ using namespace v8;
23
+
24
+ thread_local int insideCorkCallback = 0;
25
+
26
+ struct HttpResponseWrapper {
27
+
28
+ static void assumeCorked() {
29
+ if (!insideCorkCallback) {
30
+ std::cerr << "Warning: uWS.HttpResponse writes must be made from within a corked callback. See documentation for uWS.HttpResponse.cork and consult the user manual." << std::endl;
31
+ }
32
+ }
33
+
34
+ template <int PROTOCOL>
35
+ static inline constexpr decltype(auto) getHttpResponse(const FunctionCallbackInfo<Value> &args) {
36
+ Isolate *isolate = args.GetIsolate();
37
+ auto *res = (uWS::HttpResponse<PROTOCOL != 0> *) args.Holder()->GetAlignedPointerFromInternalField(0);
38
+ if (!res) {
39
+ args.GetReturnValue().Set(isolate->ThrowException(v8::Exception::Error(String::NewFromUtf8(isolate, "uWS.HttpResponse must not be accessed after uWS.HttpResponse.onAborted callback, or after a successful response. See documentation for uWS.HttpResponse and consult the user manual.", NewStringType::kNormal).ToLocalChecked())));
40
+ }
41
+
42
+ if constexpr (PROTOCOL == 2) {
43
+ return (uWS::Http3Response *) res;
44
+ } else {
45
+ return res;
46
+ }
47
+ }
48
+
49
+ /* Marks this JS object invalid */
50
+ static inline void invalidateResObject(const FunctionCallbackInfo<Value> &args) {
51
+ args.Holder()->SetAlignedPointerInInternalField(0, nullptr);
52
+ }
53
+
54
+ /* Takes nothing, returns this */
55
+ template <int SSL>
56
+ static void res_pause(const FunctionCallbackInfo<Value> &args) {
57
+ auto *res = getHttpResponse<SSL>(args);
58
+ if (res) {
59
+ res->pause();
60
+ args.GetReturnValue().Set(args.Holder());
61
+ }
62
+ }
63
+
64
+ /* Takes nothing, returns this */
65
+ template <int SSL>
66
+ static void res_resume(const FunctionCallbackInfo<Value> &args) {
67
+ auto *res = getHttpResponse<SSL>(args);
68
+ if (res) {
69
+ res->resume();
70
+ args.GetReturnValue().Set(args.Holder());
71
+ }
72
+ }
73
+
74
+ /* Takes nothing, kills the connection */
75
+ template <int SSL>
76
+ static void res_close(const FunctionCallbackInfo<Value> &args) {
77
+ auto *res = getHttpResponse<SSL>(args);
78
+ if (res) {
79
+ invalidateResObject(args);
80
+ res->close();
81
+ args.GetReturnValue().Set(args.Holder());
82
+ }
83
+ }
84
+
85
+ /* Takes function of data and isLast. Expects nothing from callback, returns this */
86
+ template <int SSL>
87
+ static void res_onData(const FunctionCallbackInfo<Value> &args) {
88
+ Isolate *isolate = args.GetIsolate();
89
+ auto *res = getHttpResponse<SSL>(args);
90
+ if (res) {
91
+ /* This thing perfectly fits in with unique_function, and will Reset on destructor */
92
+ UniquePersistent<Function> p(isolate, Local<Function>::Cast(args[0]));
93
+
94
+ res->onData([p = std::move(p), isolate](std::string_view data, bool last) {
95
+ HandleScope hs(isolate);
96
+
97
+ Local<ArrayBuffer> dataArrayBuffer = ArrayBuffer_New(isolate, (void *) data.data(), data.length());
98
+
99
+ Local<Value> argv[] = {dataArrayBuffer, Boolean::New(isolate, last)};
100
+ CallJS(isolate, Local<Function>::New(isolate, p), 2, argv);
101
+
102
+ dataArrayBuffer->Detach();
103
+ });
104
+
105
+ args.GetReturnValue().Set(args.Holder());
106
+ }
107
+ }
108
+
109
+ /* Takes nothing, returns nothing. Cb wants nothing returned. */
110
+ template <int SSL>
111
+ static void res_onAborted(const FunctionCallbackInfo<Value> &args) {
112
+ Isolate *isolate = args.GetIsolate();
113
+ auto *res = getHttpResponse<SSL>(args);
114
+ if (res) {
115
+ /* This thing perfectly fits in with unique_function, and will Reset on destructor */
116
+ UniquePersistent<Function> p(isolate, Local<Function>::Cast(args[0]));
117
+
118
+ /* This is how we capture res (C++ this in invocation of this function) */
119
+ UniquePersistent<Object> resObject(isolate, args.Holder());
120
+
121
+ res->onAborted([p = std::move(p), resObject = std::move(resObject), isolate]() {
122
+ HandleScope hs(isolate);
123
+
124
+ /* Mark this resObject invalid */
125
+ Local<Object>::New(isolate, resObject)->SetAlignedPointerInInternalField(0, nullptr);
126
+
127
+ CallJS(isolate, Local<Function>::New(isolate, p), 0, nullptr);
128
+ });
129
+
130
+ args.GetReturnValue().Set(args.Holder());
131
+ }
132
+ }
133
+
134
+ /* Takes nothing, returns arraybuffer */
135
+ template <int SSL>
136
+ static void res_getRemoteAddress(const FunctionCallbackInfo<Value> &args) {
137
+ Isolate *isolate = args.GetIsolate();
138
+ auto *res = getHttpResponse<SSL>(args);
139
+ if (res) {
140
+ std::string_view ip = res->getRemoteAddress();
141
+
142
+ args.GetReturnValue().Set(ArrayBuffer_NewCopy(isolate, (void *) ip.data(), ip.length()));
143
+ }
144
+ }
145
+
146
+ /* Takes nothing, returns arraybuffer */
147
+ template <int SSL>
148
+ static void res_getRemoteAddressAsText(const FunctionCallbackInfo<Value> &args) {
149
+ Isolate *isolate = args.GetIsolate();
150
+ auto *res = getHttpResponse<SSL>(args);
151
+ if (res) {
152
+ std::string_view ip = res->getRemoteAddressAsText();
153
+
154
+ args.GetReturnValue().Set(ArrayBuffer_NewCopy(isolate, (void *) ip.data(), ip.length()));
155
+ }
156
+ }
157
+
158
+ /* Takes nothing, returns arraybuffer */
159
+ template <int SSL>
160
+ static void res_getProxiedRemoteAddress(const FunctionCallbackInfo<Value> &args) {
161
+ Isolate *isolate = args.GetIsolate();
162
+ auto *res = getHttpResponse<SSL>(args);
163
+ if (res) {
164
+ std::string_view ip = res->getProxiedRemoteAddress();
165
+
166
+ args.GetReturnValue().Set(ArrayBuffer_NewCopy(isolate, (void *) ip.data(), ip.length()));
167
+ }
168
+ }
169
+
170
+ /* Takes nothing, returns arraybuffer */
171
+ template <int SSL>
172
+ static void res_getProxiedRemoteAddressAsText(const FunctionCallbackInfo<Value> &args) {
173
+ Isolate *isolate = args.GetIsolate();
174
+ auto *res = getHttpResponse<SSL>(args);
175
+ if (res) {
176
+ std::string_view ip = res->getProxiedRemoteAddressAsText();
177
+
178
+ args.GetReturnValue().Set(ArrayBuffer_NewCopy(isolate, (void *) ip.data(), ip.length()));
179
+ }
180
+ }
181
+
182
+ /* Returns the current write offset */
183
+ template <int SSL>
184
+ static void res_getWriteOffset(const FunctionCallbackInfo<Value> &args) {
185
+ Isolate *isolate = args.GetIsolate();
186
+ auto *res = getHttpResponse<SSL>(args);
187
+ if (res) {
188
+ args.GetReturnValue().Set(Number::New(isolate, getHttpResponse<SSL>(args)->getWriteOffset()));
189
+ }
190
+ }
191
+
192
+ /* Takes function of bool(int), returns this */
193
+ template <int SSL>
194
+ static void res_onWritable(const FunctionCallbackInfo<Value> &args) {
195
+ Isolate *isolate = args.GetIsolate();
196
+ auto *res = getHttpResponse<SSL>(args);
197
+ if (res) {
198
+ /* This thing perfectly fits in with unique_function, and will Reset on destructor */
199
+ UniquePersistent<Function> p(isolate, Local<Function>::Cast(args[0]));
200
+
201
+ res->onWritable([p = std::move(p), isolate](size_t offset) -> bool {
202
+ HandleScope hs(isolate);
203
+
204
+ Local<Value> argv[] = {Number::New(isolate, offset)};
205
+
206
+ /* We should check if this is really here! */
207
+ MaybeLocal<Value> maybeBoolean = CallJS(isolate, Local<Function>::New(isolate, p), 1, argv);
208
+ if (maybeBoolean.IsEmpty()) {
209
+ std::cerr << "Warning: uWS.HttpResponse.onWritable callback should return Boolean. See documentation for uWS.HttpResponse.onWritable and consult the user manual." << std::endl;
210
+ /* The default should be true, as it only adds a potential extra send, rather than erroneously avoid it */
211
+ return true;
212
+ }
213
+
214
+ return maybeBoolean.ToLocalChecked()->BooleanValue(isolate);
215
+ /* How important is this return? */
216
+ });
217
+
218
+ args.GetReturnValue().Set(args.Holder());
219
+ }
220
+ }
221
+
222
+ /* Takes string or arraybuffer, returns this */
223
+ template <int SSL>
224
+ static void res_writeStatus(const FunctionCallbackInfo<Value> &args) {
225
+ auto *res = getHttpResponse<SSL>(args);
226
+ if (res) {
227
+ NativeString data(args.GetIsolate(), args[0]);
228
+ if (data.isInvalid(args)) {
229
+ return;
230
+ }
231
+
232
+ assumeCorked();
233
+ res->writeStatus(data.getString());
234
+
235
+ args.GetReturnValue().Set(args.Holder());
236
+ }
237
+ }
238
+
239
+ /* Takes number, bool */
240
+ template <int SSL>
241
+ static void res_endWithoutBody(const FunctionCallbackInfo<Value> &args) {
242
+ auto *res = getHttpResponse<SSL>(args);
243
+ if (res) {
244
+ std::optional<size_t> reportedContentLength;
245
+ if (args.Length() >= 1) {
246
+ reportedContentLength = (size_t) args[0]->NumberValue(args.GetIsolate()->GetCurrentContext()).ToChecked();
247
+ }
248
+
249
+ bool closeConnection = false;
250
+ if (args.Length() >= 2) {
251
+ closeConnection = args[1]->BooleanValue(args.GetIsolate());
252
+ }
253
+
254
+ invalidateResObject(args);
255
+ assumeCorked();
256
+ res->endWithoutBody(reportedContentLength, closeConnection);
257
+
258
+ args.GetReturnValue().Set(args.Holder());
259
+ }
260
+ }
261
+
262
+ /* Takes string or arraybuffer, returns this */
263
+ template <int PROTOCOL>
264
+ static void res_end(const FunctionCallbackInfo<Value> &args) {
265
+ auto *res = getHttpResponse<PROTOCOL>(args);
266
+ if (res) {
267
+ NativeString data(args.GetIsolate(), args[0]);
268
+ if (data.isInvalid(args)) {
269
+ return;
270
+ }
271
+
272
+ bool closeConnection = false;
273
+ if (args.Length() >= 2) {
274
+ closeConnection = args[1]->BooleanValue(args.GetIsolate());
275
+ }
276
+
277
+ invalidateResObject(args);
278
+
279
+ assumeCorked();
280
+ res->end(data.getString(), closeConnection);
281
+
282
+ args.GetReturnValue().Set(args.Holder());
283
+ }
284
+ }
285
+
286
+ /* Takes data and optionally totalLength, returns true for success, false for backpressure */
287
+ template <int PROTOCOL>
288
+ static void res_tryEnd(const FunctionCallbackInfo<Value> &args) {
289
+ Isolate *isolate = args.GetIsolate();
290
+ auto *res = getHttpResponse<PROTOCOL>(args);
291
+ if (res) {
292
+ NativeString data(args.GetIsolate(), args[0]);
293
+ if (data.isInvalid(args)) {
294
+ return;
295
+ }
296
+
297
+ size_t totalSize = 0;
298
+ if (args.Length() > 1) {
299
+ totalSize = (size_t) args[1]->NumberValue(isolate->GetCurrentContext()).ToChecked();
300
+ }
301
+
302
+ assumeCorked();
303
+ auto [ok, hasResponded] = res->tryEnd(data.getString(), totalSize);
304
+
305
+ /* Invalidate this object if we responded completely */
306
+ if (hasResponded) {
307
+ invalidateResObject(args);
308
+ }
309
+
310
+ /* This is a quick fix, it will need updating in µWS later on */
311
+ Local<Array> array = Array::New(isolate, 2);
312
+ array->Set(isolate->GetCurrentContext(), 0, Boolean::New(isolate, ok)).ToChecked();
313
+ array->Set(isolate->GetCurrentContext(), 1, Boolean::New(isolate, hasResponded)).ToChecked();
314
+
315
+ args.GetReturnValue().Set(array);
316
+ }
317
+ }
318
+
319
+ /* Takes data, returns true for success, false for backpressure */
320
+ template <int PROTOCOL>
321
+ static void res_write(const FunctionCallbackInfo<Value> &args) {
322
+ Isolate *isolate = args.GetIsolate();
323
+ auto *res = getHttpResponse<PROTOCOL>(args);
324
+ if (res) {
325
+ NativeString data(args.GetIsolate(), args[0]);
326
+ if (data.isInvalid(args)) {
327
+ return;
328
+ }
329
+ assumeCorked();
330
+ bool ok = res->write(data.getString());
331
+
332
+ args.GetReturnValue().Set(Boolean::New(isolate, ok));
333
+ }
334
+ }
335
+
336
+ /* Takes key, value. Returns this */
337
+ template <int PROTOCOL>
338
+ static void res_writeHeader(const FunctionCallbackInfo<Value> &args) {
339
+ Isolate *isolate = args.GetIsolate();
340
+ auto *res = getHttpResponse<PROTOCOL>(args);
341
+ if (res) {
342
+ NativeString header(args.GetIsolate(), args[0]);
343
+ if (header.isInvalid(args)) {
344
+ return;
345
+ }
346
+ NativeString value(args.GetIsolate(), args[1]);
347
+ if (value.isInvalid(args)) {
348
+ return;
349
+ }
350
+ assumeCorked();
351
+ res->writeHeader(header.getString(), value.getString());
352
+
353
+ args.GetReturnValue().Set(args.Holder());
354
+ }
355
+ }
356
+
357
+ /* Takes function, returns this */
358
+ template <int SSL>
359
+ static void res_cork(const FunctionCallbackInfo<Value> &args) {
360
+ Isolate *isolate = args.GetIsolate();
361
+ auto *res = getHttpResponse<SSL>(args);
362
+ if (res) {
363
+
364
+ res->cork([cb = Local<Function>::Cast(args[0]), isolate]() {
365
+ insideCorkCallback++;
366
+ /* This one is called from JS so we don't need CallJS */
367
+ cb->Call(isolate->GetCurrentContext(), isolate->GetCurrentContext()->Global(), 0, nullptr).IsEmpty();
368
+ insideCorkCallback--;
369
+ });
370
+
371
+ args.GetReturnValue().Set(args.Holder());
372
+ }
373
+ }
374
+
375
+ /* Takes UserData, secKey, secProtocol, secExtensions, context. Returns nothing */
376
+ template <int SSL>
377
+ static void res_upgrade(const FunctionCallbackInfo<Value> &args) {
378
+ Isolate *isolate = args.GetIsolate();
379
+ auto *res = getHttpResponse<SSL>(args);
380
+ if (res) {
381
+ /* We require exactly 5 arguments */
382
+ if (args.Length() != 5) {
383
+ return;
384
+ }
385
+
386
+ NativeString secWebSocketKey(args.GetIsolate(), args[1]);
387
+ if (secWebSocketKey.isInvalid(args)) {
388
+ return;
389
+ }
390
+
391
+ NativeString secWebSocketProtocol(args.GetIsolate(), args[2]);
392
+ if (secWebSocketProtocol.isInvalid(args)) {
393
+ return;
394
+ }
395
+
396
+ NativeString secWebSocketExtensions(args.GetIsolate(), args[3]);
397
+ if (secWebSocketExtensions.isInvalid(args)) {
398
+ return;
399
+ }
400
+
401
+ auto *context = (struct us_socket_context_t *) Local<External>::Cast(args[4])->Value();
402
+
403
+ invalidateResObject(args);
404
+
405
+ /* This releases on return */
406
+ UniquePersistent<Object> userData;
407
+ userData.Reset(isolate, Local<Object>::Cast(args[0]));
408
+
409
+ /* Immediately calls open handler */
410
+ assumeCorked();
411
+ res->template upgrade<PerSocketData>({
412
+ std::move(userData)
413
+ }, secWebSocketKey.getString(), secWebSocketProtocol.getString(),
414
+ secWebSocketExtensions.getString(), context);
415
+
416
+ /* Nothing is returned */
417
+ }
418
+ }
419
+
420
+ /* 0 = TCP, 1 = TLS, 2 = QUIC */
421
+ template <int SSL>
422
+ static Local<Object> init(Isolate *isolate) {
423
+ Local<FunctionTemplate> resTemplateLocal = FunctionTemplate::New(isolate);
424
+ if (SSL == 1) {
425
+ resTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.SSLHttpResponse", NewStringType::kNormal).ToLocalChecked());
426
+ } else if (SSL == 0) {
427
+ resTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.HttpResponse", NewStringType::kNormal).ToLocalChecked());
428
+ } else if (SSL == 2) {
429
+ resTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.Http3Response", NewStringType::kNormal).ToLocalChecked());
430
+ }
431
+ resTemplateLocal->InstanceTemplate()->SetInternalFieldCount(1);
432
+
433
+ /* Register our functions */
434
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "writeStatus", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_writeStatus<SSL>));
435
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "end", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_end<SSL>));
436
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "endWithoutBody", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_endWithoutBody<SSL>));
437
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "tryEnd", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_tryEnd<SSL>));
438
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "write", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_write<SSL>));
439
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "writeHeader", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_writeHeader<SSL>));
440
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "close", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_close<SSL>));
441
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "onWritable", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_onWritable<SSL>));
442
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "onAborted", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_onAborted<SSL>));
443
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "onData", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_onData<SSL>));
444
+
445
+ if constexpr (SSL != 2) {
446
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getWriteOffset", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getWriteOffset<SSL>));
447
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemoteAddress", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getRemoteAddress<SSL>));
448
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "cork", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_cork<SSL>));
449
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "collect", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_cork<SSL>));
450
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "upgrade", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_upgrade<SSL>));
451
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemoteAddressAsText", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getRemoteAddressAsText<SSL>));
452
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getProxiedRemoteAddress", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getProxiedRemoteAddress<SSL>));
453
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getProxiedRemoteAddressAsText", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getProxiedRemoteAddressAsText<SSL>));
454
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "pause", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_pause<SSL>));
455
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "resume", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_resume<SSL>));
456
+ }
457
+
458
+ /* Create our template */
459
+ Local<Object> resObjectLocal = resTemplateLocal->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
460
+
461
+ return resObjectLocal;
462
+ }
463
+ };