@reboot-dev/reboot 0.33.1 → 0.35.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.
- package/index.d.ts +58 -94
- package/index.js +105 -98
- package/package.json +5 -3
- package/reboot_native.cc +233 -326
- package/reboot_native.cjs +0 -8
- package/version.d.ts +1 -1
- package/version.js +1 -1
package/reboot_native.cc
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
#include <pybind11/pybind11.h>
|
|
3
3
|
#include <pybind11/stl.h>
|
|
4
4
|
|
|
5
|
+
#if __linux__
|
|
6
|
+
#include <sys/eventfd.h>
|
|
7
|
+
#endif
|
|
8
|
+
|
|
5
9
|
#include <atomic>
|
|
6
10
|
#include <future>
|
|
7
11
|
#include <iostream>
|
|
@@ -21,8 +25,53 @@ using namespace pybind11::literals;
|
|
|
21
25
|
namespace py = pybind11;
|
|
22
26
|
|
|
23
27
|
|
|
28
|
+
// Helper when debugging, left for future optimization work.
|
|
29
|
+
struct Timer {
|
|
30
|
+
Timer(const char* name)
|
|
31
|
+
: name_(name) {
|
|
32
|
+
start_ = std::chrono::high_resolution_clock::now();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
void elapsed(const char* step = nullptr) const {
|
|
36
|
+
auto end = std::chrono::high_resolution_clock::now();
|
|
37
|
+
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
38
|
+
end - start_);
|
|
39
|
+
std::stringstream ss;
|
|
40
|
+
ss << name_
|
|
41
|
+
<< (step != nullptr ? " at " + std::string(step) : std::string(""))
|
|
42
|
+
<< " took " << duration.count() << "ms"
|
|
43
|
+
<< std::endl;
|
|
44
|
+
std::cout << ss.str();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
std::string name_;
|
|
48
|
+
std::chrono::time_point<std::chrono::high_resolution_clock> start_;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
void _RunNodeFunctions(
|
|
53
|
+
Napi::Env env,
|
|
54
|
+
Napi::Function /* callback */,
|
|
55
|
+
void* /* context */,
|
|
56
|
+
void* /* data */);
|
|
57
|
+
|
|
58
|
+
|
|
24
59
|
struct PythonNodeAdaptor {
|
|
25
|
-
PythonNodeAdaptor() {
|
|
60
|
+
PythonNodeAdaptor() {
|
|
61
|
+
#if __linux__
|
|
62
|
+
read_fd_ = write_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
|
|
63
|
+
#else
|
|
64
|
+
int pipe_fds[2];
|
|
65
|
+
if (pipe(pipe_fds) == -1) {
|
|
66
|
+
perror("pipe(...)");
|
|
67
|
+
abort();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// TODO: make nonblocking and close on exec.
|
|
71
|
+
read_fd_ = pipe_fds[0];
|
|
72
|
+
write_fd_ = pipe_fds[1];
|
|
73
|
+
#endif
|
|
74
|
+
}
|
|
26
75
|
|
|
27
76
|
~PythonNodeAdaptor() {
|
|
28
77
|
// We must join because thread captures `this`.
|
|
@@ -68,20 +117,41 @@ struct PythonNodeAdaptor {
|
|
|
68
117
|
}
|
|
69
118
|
|
|
70
119
|
template <typename F>
|
|
71
|
-
void ScheduleCallbackOnPythonEventLoop(F&& f) {
|
|
72
|
-
|
|
73
|
-
f();
|
|
74
|
-
};
|
|
120
|
+
void ScheduleCallbackOnPythonEventLoop(F&& f, bool high_priority = true) {
|
|
121
|
+
bool signal_fd = false;
|
|
75
122
|
|
|
76
123
|
{
|
|
77
124
|
std::lock_guard<std::mutex> lock(mutex);
|
|
78
|
-
python_functions.emplace_back(std::move(
|
|
125
|
+
python_functions.emplace_back(std::move(f));
|
|
126
|
+
if (high_priority && should_signal_fd_) {
|
|
127
|
+
signal_fd = true;
|
|
128
|
+
should_signal_fd_ = false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (signal_fd) {
|
|
133
|
+
uint64_t one = 1;
|
|
134
|
+
// Writing to this file descriptor communicates to Python
|
|
135
|
+
// (see `public/rebootdev/nodejs/python.py`) that
|
|
136
|
+
// `run_functions()` should be called (while holding the Python
|
|
137
|
+
// GIL). In `Initialize()` we've set up `run_functions()` to run
|
|
138
|
+
// the `python_functions` we've just added our callback to.
|
|
139
|
+
if (write(write_fd_, &one, sizeof(one)) < 0) {
|
|
140
|
+
perror("write(...)");
|
|
141
|
+
abort();
|
|
142
|
+
}
|
|
79
143
|
}
|
|
80
|
-
python_functions_not_empty.notify_one();
|
|
81
144
|
}
|
|
82
145
|
|
|
83
146
|
template <typename F>
|
|
84
|
-
void
|
|
147
|
+
void ScheduleCallbackOnPythonEventLoopLowPriority(F&& f) {
|
|
148
|
+
return ScheduleCallbackOnPythonEventLoop(
|
|
149
|
+
std::move(f),
|
|
150
|
+
/* high_priority = */ false);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
template <typename F>
|
|
154
|
+
void ScheduleCallbackOnNodeEventLoop(F&& f, bool high_priority = true) {
|
|
85
155
|
// It is possible that Node.js is shutting down and has already
|
|
86
156
|
// called the `thread_safe_function` finalizer and trying to use
|
|
87
157
|
// `thread_safe_function` will raise SIGABRT or SIGSEGV.
|
|
@@ -99,36 +169,67 @@ struct PythonNodeAdaptor {
|
|
|
99
169
|
return;
|
|
100
170
|
}
|
|
101
171
|
|
|
102
|
-
|
|
103
|
-
[f = std::forward<F>(f)](
|
|
104
|
-
Napi::Env env,
|
|
105
|
-
Napi::Function /* js_callback */) mutable {
|
|
106
|
-
try {
|
|
107
|
-
f(env);
|
|
108
|
-
} catch (const std::exception& e) {
|
|
109
|
-
PythonNodeAdaptor::HandleException(e);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
172
|
+
bool call_thread_safe_function = false;
|
|
112
173
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
174
|
+
{
|
|
175
|
+
std::lock_guard<std::mutex> lock(node_functions_mutex_);
|
|
176
|
+
node_functions_.emplace_back(std::move(f));
|
|
177
|
+
if (high_priority && should_call_thread_safe_function_) {
|
|
178
|
+
call_thread_safe_function = true;
|
|
179
|
+
should_call_thread_safe_function_ = false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (call_thread_safe_function) {
|
|
184
|
+
napi_status status = thread_safe_function.BlockingCall();
|
|
185
|
+
|
|
186
|
+
// TODO: handle each of the possible values for `status`:
|
|
187
|
+
//
|
|
188
|
+
// napi_ok: the call was successfully added to the queue.
|
|
189
|
+
//
|
|
190
|
+
// napi_queue_full: the queue was full when trying to call in a
|
|
191
|
+
// non-blocking method.
|
|
192
|
+
//
|
|
193
|
+
// napi_closing: the thread-safe function is aborted and cannot
|
|
194
|
+
// accept more calls.
|
|
195
|
+
//
|
|
196
|
+
// napi_invalid_arg: the thread-safe function is closed.
|
|
197
|
+
//
|
|
198
|
+
// napi_generic_failure: a generic error occurred when attempting
|
|
199
|
+
//
|
|
200
|
+
|
|
201
|
+
if (status != napi_ok) {
|
|
202
|
+
Napi::Error::Fatal(
|
|
203
|
+
"ThreadEntry",
|
|
204
|
+
"Napi::ThreadSafeNapi::Function.BlockingCall() failed");
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
template <typename F>
|
|
210
|
+
void ScheduleCallbackOnNodeEventLoopLowPriority(F&& f) {
|
|
211
|
+
return ScheduleCallbackOnNodeEventLoop(
|
|
212
|
+
std::move(f),
|
|
213
|
+
/* high_priority = */ false);
|
|
214
|
+
}
|
|
127
215
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
216
|
+
void RunNodeFunctions(Napi::Env& env) {
|
|
217
|
+
std::list<std::function<void(Napi::Env&)>> functions;
|
|
218
|
+
{
|
|
219
|
+
std::lock_guard<std::mutex> lock(node_functions_mutex_);
|
|
220
|
+
functions = std::move(node_functions_);
|
|
221
|
+
should_call_thread_safe_function_ = true;
|
|
222
|
+
}
|
|
223
|
+
for (auto&& function : functions) {
|
|
224
|
+
try {
|
|
225
|
+
function(env);
|
|
226
|
+
} catch (const std::exception& e) {
|
|
227
|
+
std::cerr
|
|
228
|
+
<< "Unexpected exception: " << e.what()
|
|
229
|
+
<< "\n"
|
|
230
|
+
<< "Please report this bug to the maintainers!"
|
|
231
|
+
<< std::endl;
|
|
232
|
+
}
|
|
132
233
|
}
|
|
133
234
|
}
|
|
134
235
|
|
|
@@ -162,23 +263,29 @@ struct PythonNodeAdaptor {
|
|
|
162
263
|
// into Python or expecting calls from Python.
|
|
163
264
|
std::atomic<int> references{0};
|
|
164
265
|
|
|
165
|
-
Napi::
|
|
266
|
+
Napi::TypedThreadSafeFunction<
|
|
267
|
+
void,
|
|
268
|
+
void,
|
|
269
|
+
_RunNodeFunctions>
|
|
270
|
+
thread_safe_function;
|
|
271
|
+
std::mutex node_functions_mutex_;
|
|
272
|
+
std::list<std::function<void(Napi::Env&)>> node_functions_;
|
|
273
|
+
bool should_call_thread_safe_function_ = true;
|
|
166
274
|
|
|
167
275
|
bool thread_safe_function_finalized = false;
|
|
168
276
|
|
|
169
277
|
std::thread thread;
|
|
170
278
|
std::mutex mutex;
|
|
171
|
-
std::condition_variable python_functions_not_empty;
|
|
172
279
|
std::list<std::function<void()>> python_functions;
|
|
280
|
+
int read_fd_ = -1;
|
|
281
|
+
int write_fd_ = -1;
|
|
282
|
+
bool should_signal_fd_ = true;
|
|
173
283
|
};
|
|
174
284
|
|
|
175
285
|
|
|
176
286
|
static PythonNodeAdaptor* adaptor = new PythonNodeAdaptor();
|
|
177
287
|
|
|
178
288
|
// References to nodejs functions so we don't have to look them up.
|
|
179
|
-
static Napi::FunctionReference* js_Context_fromNativeExternal =
|
|
180
|
-
new Napi::FunctionReference();
|
|
181
|
-
|
|
182
289
|
static Napi::FunctionReference* js_launchSubprocessConsensus =
|
|
183
290
|
new Napi::FunctionReference();
|
|
184
291
|
|
|
@@ -186,7 +293,7 @@ static Napi::FunctionReference* js_launchSubprocessConsensus =
|
|
|
186
293
|
struct NapiReferenceDeleter {
|
|
187
294
|
template <typename T>
|
|
188
295
|
void operator()(Napi::Reference<T>* reference) {
|
|
189
|
-
adaptor->
|
|
296
|
+
adaptor->ScheduleCallbackOnNodeEventLoopLowPriority(
|
|
190
297
|
[reference](Napi::Env) {
|
|
191
298
|
delete reference;
|
|
192
299
|
});
|
|
@@ -246,7 +353,7 @@ Napi::External<py::object> make_napi_external(
|
|
|
246
353
|
env,
|
|
247
354
|
py_object,
|
|
248
355
|
[](Napi::Env, py::object* py_object) {
|
|
249
|
-
adaptor->
|
|
356
|
+
adaptor->ScheduleCallbackOnPythonEventLoopLowPriority(
|
|
250
357
|
[py_object]() {
|
|
251
358
|
delete py_object;
|
|
252
359
|
});
|
|
@@ -260,18 +367,30 @@ Napi::External<py::object> make_napi_external(
|
|
|
260
367
|
}
|
|
261
368
|
|
|
262
369
|
|
|
370
|
+
void _RunNodeFunctions(
|
|
371
|
+
Napi::Env env,
|
|
372
|
+
Napi::Function /* callback */,
|
|
373
|
+
void* /* context */,
|
|
374
|
+
void* /* data */) {
|
|
375
|
+
adaptor->RunNodeFunctions(env);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
|
|
263
379
|
void PythonNodeAdaptor::Initialize(
|
|
264
380
|
Napi::Env& env,
|
|
265
381
|
const Napi::Function& js_callback) {
|
|
266
|
-
thread_safe_function = Napi::
|
|
382
|
+
thread_safe_function = Napi::TypedThreadSafeFunction<
|
|
383
|
+
void,
|
|
384
|
+
void,
|
|
385
|
+
_RunNodeFunctions>::New(
|
|
267
386
|
/* Environment: */ env,
|
|
268
387
|
/* JS callback: */ js_callback,
|
|
269
388
|
/* Resource name: */ "reboot_native",
|
|
270
389
|
/* Max queue size (0 = unlimited): */ 0,
|
|
271
390
|
/* Initial thread count: */ 1,
|
|
272
|
-
/* Context: */
|
|
391
|
+
/* Context: */ (void*) nullptr,
|
|
273
392
|
// Finalizer:
|
|
274
|
-
[this](Napi::Env env, void*,
|
|
393
|
+
[this](Napi::Env env, void*, void*) {
|
|
275
394
|
// Set that we've been finalized so that we don't use
|
|
276
395
|
// `thread_safe_function` again, see comment in
|
|
277
396
|
// `ScheduleCallbackOnNodeEventLoop`.
|
|
@@ -345,35 +464,35 @@ void PythonNodeAdaptor::Initialize(
|
|
|
345
464
|
return py_future;
|
|
346
465
|
});
|
|
347
466
|
|
|
348
|
-
|
|
467
|
+
module.attr("run_functions") = py::cpp_function(
|
|
468
|
+
[this]() {
|
|
469
|
+
std::list<std::function<void()>> functions;
|
|
470
|
+
std::vector<std::pair<py::object*, std::string>> futures;
|
|
471
|
+
|
|
472
|
+
{
|
|
473
|
+
std::unique_lock<std::mutex> lock(mutex);
|
|
474
|
+
functions = std::move(python_functions);
|
|
475
|
+
should_signal_fd_ = true;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
for (auto&& function : functions) {
|
|
479
|
+
try {
|
|
480
|
+
function();
|
|
481
|
+
} catch (std::exception& e) {
|
|
482
|
+
PythonNodeAdaptor::HandleException(e);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
py::object event_loop_thread = module.attr("EventLoopThread")(
|
|
488
|
+
read_fd_);
|
|
349
489
|
|
|
350
490
|
py::gil_scoped_release release;
|
|
351
491
|
|
|
352
492
|
while (true) {
|
|
353
|
-
std::
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
std::unique_lock<std::mutex> lock(mutex);
|
|
357
|
-
python_functions_not_empty.wait(
|
|
358
|
-
lock,
|
|
359
|
-
[this] { return !python_functions.empty(); });
|
|
360
|
-
|
|
361
|
-
function = std::move(python_functions.front());
|
|
362
|
-
python_functions.pop_front();
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
{
|
|
366
|
-
py::gil_scoped_acquire acquire;
|
|
367
|
-
|
|
368
|
-
event_loop_thread.attr("run_callback_on_event_loop")(
|
|
369
|
-
py::cpp_function([function = std::move(function)]() {
|
|
370
|
-
try {
|
|
371
|
-
function();
|
|
372
|
-
} catch (const std::exception& e) {
|
|
373
|
-
PythonNodeAdaptor::HandleException(e);
|
|
374
|
-
}
|
|
375
|
-
}));
|
|
376
|
-
}
|
|
493
|
+
std::this_thread::sleep_until(
|
|
494
|
+
std::chrono::time_point<std::chrono::system_clock>::max());
|
|
495
|
+
continue;
|
|
377
496
|
}
|
|
378
497
|
} catch (const std::exception& e) {
|
|
379
498
|
std::cout << e.what() << std::endl;
|
|
@@ -968,12 +1087,8 @@ void Initialize(const Napi::CallbackInfo& info) {
|
|
|
968
1087
|
std::call_once(initialize_once, [&]() {
|
|
969
1088
|
Napi::Env env = info.Env();
|
|
970
1089
|
|
|
971
|
-
js_Context_fromNativeExternal->Reset(
|
|
972
|
-
info[1].As<Napi::Function>(),
|
|
973
|
-
/* refcount = */ 1);
|
|
974
|
-
|
|
975
1090
|
js_launchSubprocessConsensus->Reset(
|
|
976
|
-
info[
|
|
1091
|
+
info[1].As<Napi::Function>(),
|
|
977
1092
|
/* refcount = */ 1);
|
|
978
1093
|
|
|
979
1094
|
// NOTE: must initialize after storing above nodejs functions.
|
|
@@ -1133,15 +1248,10 @@ Napi::Value Reboot_createExternalContext(const Napi::CallbackInfo& info) {
|
|
|
1133
1248
|
|
|
1134
1249
|
|
|
1135
1250
|
// NOTE: must be called within _Node_.
|
|
1136
|
-
Napi::
|
|
1251
|
+
Napi::Promise make_js_cancelled(
|
|
1137
1252
|
Napi::Env& env,
|
|
1138
|
-
py::object*
|
|
1139
|
-
|
|
1140
|
-
const std::string& kind) {
|
|
1141
|
-
Napi::External<py::object> js_external_context =
|
|
1142
|
-
make_napi_external(env, py_context);
|
|
1143
|
-
|
|
1144
|
-
Napi::Promise js_cancelled = NodePromiseFromPythonFuture(
|
|
1253
|
+
py::object* py_cancelled) {
|
|
1254
|
+
return NodePromiseFromPythonFuture(
|
|
1145
1255
|
env,
|
|
1146
1256
|
[py_cancelled]() {
|
|
1147
1257
|
// After returning we won't need `py_cancelled` anymore, so we
|
|
@@ -1161,13 +1271,6 @@ Napi::Object make_js_context(
|
|
|
1161
1271
|
// this lambda or the one above.
|
|
1162
1272
|
return env.Undefined();
|
|
1163
1273
|
});
|
|
1164
|
-
|
|
1165
|
-
return js_Context_fromNativeExternal
|
|
1166
|
-
->Call(
|
|
1167
|
-
{js_external_context,
|
|
1168
|
-
Napi::String::New(env, kind),
|
|
1169
|
-
js_cancelled})
|
|
1170
|
-
.As<Napi::Object>();
|
|
1171
1274
|
}
|
|
1172
1275
|
|
|
1173
1276
|
|
|
@@ -1191,11 +1294,9 @@ py::object make_py_authorizer(NapiSafeObjectReference js_authorizer) {
|
|
|
1191
1294
|
// Trampolines us from Python through C++ into Node.
|
|
1192
1295
|
attributes["_authorize"] = py::cpp_function(
|
|
1193
1296
|
[](py::object self,
|
|
1194
|
-
const std::string& method_name,
|
|
1195
1297
|
py::object py_reader_context,
|
|
1196
1298
|
py::object py_cancelled,
|
|
1197
|
-
std::
|
|
1198
|
-
std::optional<std::string> bytes_request) {
|
|
1299
|
+
std::string bytes_call) {
|
|
1199
1300
|
NapiSafeObjectReference* js_authorizer_reference =
|
|
1200
1301
|
self.attr("_js_authorizer")
|
|
1201
1302
|
.cast<NapiSafeObjectReference*>();
|
|
@@ -1204,30 +1305,21 @@ py::object make_py_authorizer(NapiSafeObjectReference js_authorizer) {
|
|
|
1204
1305
|
[js_authorizer_reference,
|
|
1205
1306
|
py_reader_context = new py::object(py_reader_context),
|
|
1206
1307
|
py_cancelled = new py::object(py_cancelled),
|
|
1207
|
-
|
|
1208
|
-
bytes_state = std::move(bytes_state),
|
|
1209
|
-
bytes_request = std::move(bytes_request)](Napi::Env env) {
|
|
1308
|
+
bytes_call = std::move(bytes_call)](Napi::Env env) {
|
|
1210
1309
|
std::vector<Napi::Value> js_args;
|
|
1211
1310
|
|
|
1212
|
-
Napi::
|
|
1213
|
-
env,
|
|
1214
|
-
py_reader_context,
|
|
1215
|
-
py_cancelled,
|
|
1216
|
-
"reader");
|
|
1311
|
+
Napi::External<py::object> js_external_context =
|
|
1312
|
+
make_napi_external(env, py_reader_context);
|
|
1217
1313
|
|
|
1218
|
-
|
|
1314
|
+
js_args.push_back(js_external_context);
|
|
1315
|
+
|
|
1316
|
+
Napi::Promise js_cancelled = make_js_cancelled(env, py_cancelled);
|
|
1219
1317
|
|
|
1220
|
-
|
|
1221
|
-
? str_to_uint8array(env, *bytes_request)
|
|
1222
|
-
: env.Undefined();
|
|
1223
|
-
Napi::Value js_bytes_state = bytes_state.has_value()
|
|
1224
|
-
? str_to_uint8array(env, *bytes_state)
|
|
1225
|
-
: env.Undefined();
|
|
1318
|
+
js_args.push_back(js_cancelled);
|
|
1226
1319
|
|
|
1227
|
-
js_args.push_back(
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
js_args.push_back(js_bytes_request);
|
|
1320
|
+
js_args.push_back(str_to_uint8array(env, bytes_call));
|
|
1321
|
+
|
|
1322
|
+
Napi::Object js_authorizer = js_authorizer_reference->Value(env);
|
|
1231
1323
|
|
|
1232
1324
|
return js_authorizer.Get("_authorize")
|
|
1233
1325
|
.As<Napi::Function>()
|
|
@@ -1243,11 +1335,9 @@ py::object make_py_authorizer(NapiSafeObjectReference js_authorizer) {
|
|
|
1243
1335
|
});
|
|
1244
1336
|
},
|
|
1245
1337
|
py::name("_authorize"),
|
|
1246
|
-
py::arg("method_name"),
|
|
1247
1338
|
py::arg("context"),
|
|
1248
1339
|
py::arg("cancelled"),
|
|
1249
|
-
py::arg("
|
|
1250
|
-
py::arg("request"),
|
|
1340
|
+
py::arg("bytes_call"),
|
|
1251
1341
|
py::is_method(py::none()));
|
|
1252
1342
|
|
|
1253
1343
|
// Now define our subclass.
|
|
@@ -1412,41 +1502,41 @@ py::object make_py_user_servicer(
|
|
|
1412
1502
|
// calls.
|
|
1413
1503
|
attributes["_trampoline"] = py::cpp_function(
|
|
1414
1504
|
[](NapiSafeObjectReference& js_servicer_reference,
|
|
1415
|
-
const std::string& kind,
|
|
1416
|
-
const std::string& method,
|
|
1417
1505
|
py::object py_context,
|
|
1418
1506
|
py::object py_cancelled,
|
|
1419
|
-
|
|
1420
|
-
const std::string& json_request) {
|
|
1507
|
+
std::string bytes_call) {
|
|
1421
1508
|
return PythonFutureFromNodePromise(
|
|
1422
1509
|
[&js_servicer_reference,
|
|
1423
|
-
kind,
|
|
1424
|
-
method,
|
|
1425
1510
|
py_context = new py::object(py_context),
|
|
1426
1511
|
py_cancelled = new py::object(py_cancelled),
|
|
1427
|
-
|
|
1428
|
-
json_request](Napi::Env env) {
|
|
1512
|
+
bytes_call = std::move(bytes_call)](Napi::Env env) {
|
|
1429
1513
|
std::vector<Napi::Value> js_args;
|
|
1430
1514
|
|
|
1431
|
-
Napi::
|
|
1432
|
-
|
|
1515
|
+
Napi::External<py::object> js_external_context =
|
|
1516
|
+
make_napi_external(env, py_context);
|
|
1517
|
+
|
|
1518
|
+
js_args.push_back(js_external_context);
|
|
1433
1519
|
|
|
1434
|
-
|
|
1520
|
+
Napi::Promise js_cancelled = make_js_cancelled(env, py_cancelled);
|
|
1435
1521
|
|
|
1436
|
-
js_args.push_back(
|
|
1437
|
-
|
|
1522
|
+
js_args.push_back(js_cancelled);
|
|
1523
|
+
|
|
1524
|
+
js_args.push_back(str_to_uint8array(env, bytes_call));
|
|
1438
1525
|
|
|
1439
1526
|
Napi::Object js_servicer =
|
|
1440
1527
|
js_servicer_reference.Value(env);
|
|
1441
1528
|
|
|
1442
1529
|
return js_servicer
|
|
1443
|
-
.Get("
|
|
1530
|
+
.Get("__dispatch")
|
|
1444
1531
|
.As<Napi::Function>()
|
|
1445
1532
|
.Call(js_servicer, js_args)
|
|
1446
1533
|
.As<Napi::Object>();
|
|
1447
1534
|
},
|
|
1448
1535
|
[](Napi::Env env, Napi::Value value) {
|
|
1449
|
-
return
|
|
1536
|
+
return uint8array_to_str(value.As<Napi::Uint8Array>());
|
|
1537
|
+
},
|
|
1538
|
+
[](std::string&& bytes_result) -> py::object {
|
|
1539
|
+
return py::bytes(bytes_result);
|
|
1450
1540
|
});
|
|
1451
1541
|
});
|
|
1452
1542
|
|
|
@@ -1562,19 +1652,11 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
|
|
|
1562
1652
|
[](py::object self,
|
|
1563
1653
|
py::object py_reader_context,
|
|
1564
1654
|
py::object py_cancelled,
|
|
1565
|
-
|
|
1655
|
+
std::string bytes_call) {
|
|
1566
1656
|
NapiSafeObjectReference* js_token_verifier_reference =
|
|
1567
1657
|
self.attr("_js_token_verifier")
|
|
1568
1658
|
.cast<NapiSafeObjectReference*>();
|
|
1569
1659
|
|
|
1570
|
-
// Converting to 'py::str' involves Python runtime access.
|
|
1571
|
-
// Perform this conversion here, on the Python thread, before entering
|
|
1572
|
-
// Node.
|
|
1573
|
-
std::optional<std::string> token;
|
|
1574
|
-
if (!py_token.is_none()) {
|
|
1575
|
-
token = py::str(py_token);
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
1660
|
return PythonFutureFromNodePromise(
|
|
1579
1661
|
[js_token_verifier_reference,
|
|
1580
1662
|
// We allocate 'py_cancelled' and 'py_reader_context' with 'new
|
|
@@ -1583,21 +1665,19 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
|
|
|
1583
1665
|
// thread, see 'make_js_context'.
|
|
1584
1666
|
py_reader_context = new py::object(py_reader_context),
|
|
1585
1667
|
py_cancelled = new py::object(py_cancelled),
|
|
1586
|
-
|
|
1668
|
+
bytes_call = std::move(bytes_call)](Napi::Env env) {
|
|
1587
1669
|
std::vector<Napi::Value> js_args;
|
|
1588
1670
|
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
env,
|
|
1592
|
-
py_reader_context,
|
|
1593
|
-
py_cancelled,
|
|
1594
|
-
"reader"));
|
|
1671
|
+
Napi::External<py::object> js_external_context =
|
|
1672
|
+
make_napi_external(env, py_reader_context);
|
|
1595
1673
|
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1674
|
+
js_args.push_back(js_external_context);
|
|
1675
|
+
|
|
1676
|
+
Napi::Promise js_cancelled = make_js_cancelled(env, py_cancelled);
|
|
1677
|
+
|
|
1678
|
+
js_args.push_back(js_cancelled);
|
|
1679
|
+
|
|
1680
|
+
js_args.push_back(str_to_uint8array(env, bytes_call));
|
|
1601
1681
|
|
|
1602
1682
|
Napi::Object js_token_verifier =
|
|
1603
1683
|
js_token_verifier_reference->Value(env);
|
|
@@ -1626,7 +1706,7 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
|
|
|
1626
1706
|
py::name("_verify_token"),
|
|
1627
1707
|
py::arg("context"),
|
|
1628
1708
|
py::arg("cancelled"),
|
|
1629
|
-
py::arg("
|
|
1709
|
+
py::arg("bytes_call"),
|
|
1630
1710
|
py::is_method(py::none()));
|
|
1631
1711
|
|
|
1632
1712
|
py::object py_parent_class =
|
|
@@ -2331,151 +2411,6 @@ Napi::Value Application_run(const Napi::CallbackInfo& info) {
|
|
|
2331
2411
|
}
|
|
2332
2412
|
|
|
2333
2413
|
|
|
2334
|
-
Napi::Value Context_auth(const Napi::CallbackInfo& info) {
|
|
2335
|
-
Napi::External<py::object> js_external_context =
|
|
2336
|
-
info[0].As<Napi::External<py::object>>();
|
|
2337
|
-
|
|
2338
|
-
// CHECK(...CheckTypeTag(...));
|
|
2339
|
-
|
|
2340
|
-
py::object* py_context = js_external_context.Data();
|
|
2341
|
-
|
|
2342
|
-
std::optional<std::string> auth_bytes = RunCallbackOnPythonEventLoop(
|
|
2343
|
-
[py_context]() -> std::optional<std::string> {
|
|
2344
|
-
py::object py_auth = py_context->attr("auth");
|
|
2345
|
-
|
|
2346
|
-
if (py_auth.is_none()) {
|
|
2347
|
-
return std::nullopt;
|
|
2348
|
-
} else {
|
|
2349
|
-
std::string auth_bytes = py::bytes(py_auth.attr("to_proto_bytes")());
|
|
2350
|
-
return auth_bytes;
|
|
2351
|
-
}
|
|
2352
|
-
});
|
|
2353
|
-
|
|
2354
|
-
if (auth_bytes.has_value()) {
|
|
2355
|
-
Napi::Env env = info.Env();
|
|
2356
|
-
return str_to_uint8array(env, *auth_bytes);
|
|
2357
|
-
} else {
|
|
2358
|
-
return info.Env().Null();
|
|
2359
|
-
}
|
|
2360
|
-
}
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
Napi::Value Context_stateId(const Napi::CallbackInfo& info) {
|
|
2364
|
-
Napi::External<py::object> js_external_context =
|
|
2365
|
-
info[0].As<Napi::External<py::object>>();
|
|
2366
|
-
|
|
2367
|
-
// CHECK(...CheckTypeTag(...));
|
|
2368
|
-
|
|
2369
|
-
py::object* py_context = js_external_context.Data();
|
|
2370
|
-
|
|
2371
|
-
std::string state_id = RunCallbackOnPythonEventLoop(
|
|
2372
|
-
[py_context]() {
|
|
2373
|
-
py::str state_id = py_context->attr("state_id");
|
|
2374
|
-
return std::string(state_id);
|
|
2375
|
-
});
|
|
2376
|
-
|
|
2377
|
-
return Napi::String::New(info.Env(), state_id);
|
|
2378
|
-
}
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
Napi::Value Context_stateTypeName(const Napi::CallbackInfo& info) {
|
|
2382
|
-
Napi::External<py::object> js_external_context =
|
|
2383
|
-
info[0].As<Napi::External<py::object>>();
|
|
2384
|
-
|
|
2385
|
-
// CHECK(...CheckTypeTag(...));
|
|
2386
|
-
|
|
2387
|
-
py::object* py_context = js_external_context.Data();
|
|
2388
|
-
|
|
2389
|
-
std::string state_type_name = RunCallbackOnPythonEventLoop(
|
|
2390
|
-
[py_context]() {
|
|
2391
|
-
py::str state_type_name = py_context->attr("state_type_name");
|
|
2392
|
-
return std::string(state_type_name);
|
|
2393
|
-
});
|
|
2394
|
-
|
|
2395
|
-
return Napi::String::New(info.Env(), state_type_name);
|
|
2396
|
-
}
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
Napi::Value Context_method(const Napi::CallbackInfo& info) {
|
|
2400
|
-
Napi::External<py::object> js_external_context =
|
|
2401
|
-
info[0].As<Napi::External<py::object>>();
|
|
2402
|
-
|
|
2403
|
-
// CHECK(...CheckTypeTag(...));
|
|
2404
|
-
|
|
2405
|
-
py::object* py_context = js_external_context.Data();
|
|
2406
|
-
|
|
2407
|
-
std::string method = RunCallbackOnPythonEventLoop(
|
|
2408
|
-
[py_context]() {
|
|
2409
|
-
py::str method = py_context->attr("method");
|
|
2410
|
-
return std::string(method);
|
|
2411
|
-
});
|
|
2412
|
-
|
|
2413
|
-
return Napi::String::New(info.Env(), method);
|
|
2414
|
-
}
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
Napi::Value Context_callerBearerToken(
|
|
2418
|
-
const Napi::CallbackInfo& info) {
|
|
2419
|
-
Napi::External<py::object> js_external_context =
|
|
2420
|
-
info[0].As<Napi::External<py::object>>();
|
|
2421
|
-
|
|
2422
|
-
// CHECK(...CheckTypeTag(...));
|
|
2423
|
-
|
|
2424
|
-
py::object* py_context = js_external_context.Data();
|
|
2425
|
-
|
|
2426
|
-
std::optional<std::string> caller_bearer_token = RunCallbackOnPythonEventLoop(
|
|
2427
|
-
[py_context]() -> std::optional<std::string> {
|
|
2428
|
-
py::object caller_bearer_token = py_context->attr(
|
|
2429
|
-
"caller_bearer_token");
|
|
2430
|
-
if (caller_bearer_token.is_none()) {
|
|
2431
|
-
return std::nullopt;
|
|
2432
|
-
} else {
|
|
2433
|
-
return std::string(py::str(caller_bearer_token));
|
|
2434
|
-
}
|
|
2435
|
-
});
|
|
2436
|
-
if (caller_bearer_token.has_value()) {
|
|
2437
|
-
return Napi::String::New(info.Env(), *caller_bearer_token);
|
|
2438
|
-
} else {
|
|
2439
|
-
return info.Env().Null();
|
|
2440
|
-
}
|
|
2441
|
-
}
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
Napi::Value Context_cookie(const Napi::CallbackInfo& info) {
|
|
2445
|
-
Napi::External<py::object> js_external_context =
|
|
2446
|
-
info[0].As<Napi::External<py::object>>();
|
|
2447
|
-
|
|
2448
|
-
// CHECK(...CheckTypeTag(...));
|
|
2449
|
-
|
|
2450
|
-
py::object* py_context = js_external_context.Data();
|
|
2451
|
-
|
|
2452
|
-
std::string cookie = RunCallbackOnPythonEventLoop(
|
|
2453
|
-
[py_context]() {
|
|
2454
|
-
py::str cookie = py_context->attr("cookie");
|
|
2455
|
-
return std::string(cookie);
|
|
2456
|
-
});
|
|
2457
|
-
|
|
2458
|
-
return Napi::String::New(info.Env(), cookie);
|
|
2459
|
-
}
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
Napi::Value Context_appInternal(const Napi::CallbackInfo& info) {
|
|
2463
|
-
Napi::External<py::object> js_external_context =
|
|
2464
|
-
info[0].As<Napi::External<py::object>>();
|
|
2465
|
-
|
|
2466
|
-
// CHECK(...CheckTypeTag(...));
|
|
2467
|
-
|
|
2468
|
-
py::object* py_context = js_external_context.Data();
|
|
2469
|
-
|
|
2470
|
-
bool app_internal = RunCallbackOnPythonEventLoop(
|
|
2471
|
-
[py_context]() {
|
|
2472
|
-
return py_context->attr("app_internal").cast<bool>();
|
|
2473
|
-
});
|
|
2474
|
-
|
|
2475
|
-
return Napi::Boolean::New(info.Env(), app_internal);
|
|
2476
|
-
}
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
2414
|
Napi::Value Context_generateIdempotentStateId(const Napi::CallbackInfo& info) {
|
|
2480
2415
|
// NOTE: we immediately get a safe reference to the `Napi::External`
|
|
2481
2416
|
// so that Node will not garbage collect it and the `py::object*` we
|
|
@@ -2911,34 +2846,6 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
|
2911
2846
|
Napi::String::New(env, "Service_call"),
|
|
2912
2847
|
Napi::Function::New<Service_call>(env));
|
|
2913
2848
|
|
|
2914
|
-
exports.Set(
|
|
2915
|
-
Napi::String::New(env, "Context_auth"),
|
|
2916
|
-
Napi::Function::New<Context_auth>(env));
|
|
2917
|
-
|
|
2918
|
-
exports.Set(
|
|
2919
|
-
Napi::String::New(env, "Context_stateId"),
|
|
2920
|
-
Napi::Function::New<Context_stateId>(env));
|
|
2921
|
-
|
|
2922
|
-
exports.Set(
|
|
2923
|
-
Napi::String::New(env, "Context_stateTypeName"),
|
|
2924
|
-
Napi::Function::New<Context_stateTypeName>(env));
|
|
2925
|
-
|
|
2926
|
-
exports.Set(
|
|
2927
|
-
Napi::String::New(env, "Context_method"),
|
|
2928
|
-
Napi::Function::New<Context_method>(env));
|
|
2929
|
-
|
|
2930
|
-
exports.Set(
|
|
2931
|
-
Napi::String::New(env, "Context_callerBearerToken"),
|
|
2932
|
-
Napi::Function::New<Context_callerBearerToken>(env));
|
|
2933
|
-
|
|
2934
|
-
exports.Set(
|
|
2935
|
-
Napi::String::New(env, "Context_cookie"),
|
|
2936
|
-
Napi::Function::New<Context_cookie>(env));
|
|
2937
|
-
|
|
2938
|
-
exports.Set(
|
|
2939
|
-
Napi::String::New(env, "Context_appInternal"),
|
|
2940
|
-
Napi::Function::New<Context_appInternal>(env));
|
|
2941
|
-
|
|
2942
2849
|
exports.Set(
|
|
2943
2850
|
Napi::String::New(env, "Context_generateIdempotentStateId"),
|
|
2944
2851
|
Napi::Function::New<Context_generateIdempotentStateId>(env));
|