-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathZiti_https_request_data.c
250 lines (202 loc) · 7.75 KB
/
Ziti_https_request_data.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/*
Copyright NetFoundry Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "ziti-nodejs.h"
#include <ziti/ziti_src.h>
/**
* This function is responsible for calling the JavaScript on_resp_data callback function
* that was specified when the Ziti_https_request_data(...) was called from JavaScript.
*/
static void CallJs_on_req_body(napi_env env, napi_value js_cb, void* context, void* data) {
ZITI_NODEJS_LOG(DEBUG, "entered");
// This parameter is not used.
(void) context;
// Retrieve the HttpsRespBodyItem created by the worker thread.
HttpsReqBodyItem* item = (HttpsReqBodyItem*)data;
// env and js_cb may both be NULL if Node.js is in its cleanup phase, and
// items are left over from earlier thread-safe calls from the worker thread.
// When env is NULL, we simply skip over the call into Javascript
if (env != NULL) {
napi_value undefined;
// Retrieve the JavaScript `undefined` value so we can use it as the `this`
// value of the JavaScript function call.
napi_get_undefined(env, &undefined);
// const obj = {}
napi_value js_http_item, js_req, js_status, js_body;
int rc = napi_create_object(env, &js_http_item);
if (rc != napi_ok) {
napi_throw_error(env, "EINVAL", "failure to create object");
}
// obj.req = req
napi_create_int64(env, (int64_t)item->req, &js_req);
if (rc != napi_ok) {
napi_throw_error(env, "EINVAL", "failure to create resp.req");
}
rc = napi_set_named_property(env, js_http_item, "req", js_req);
if (rc != napi_ok) {
napi_throw_error(env, "EINVAL", "failure to set named property req");
}
ZITI_NODEJS_LOG(DEBUG, "js_req: %p", item->req);
// obj.code = status
rc = napi_create_int32(env, item->status, &js_status);
if (rc != napi_ok) {
napi_throw_error(env, "EINVAL", "failure to create resp.status");
}
rc = napi_set_named_property(env, js_http_item, "status", js_status);
if (rc != napi_ok) {
napi_throw_error(env, "EINVAL", "failure to set named property status");
}
ZITI_NODEJS_LOG(DEBUG, "status: %zd", item->status);
// obj.body = body
rc = napi_create_int32(env, (int64_t)item->body, &js_body);
if (rc != napi_ok) {
napi_throw_error(env, "EINVAL", "failure to create resp.body");
}
rc = napi_set_named_property(env, js_http_item, "body", js_body);
if (rc != napi_ok) {
napi_throw_error(env, "EINVAL", "failure to set named property body");
}
// Call the JavaScript function and pass it the HttpsRespItem
rc = napi_call_function(
env,
undefined,
js_cb,
1,
&js_http_item,
NULL
);
if (rc != napi_ok) {
napi_throw_error(env, "EINVAL", "failure to invoke JS callback");
}
}
}
/**
*
*/
void on_req_body(tlsuv_http_req_t *req, char *body, ssize_t status) {
ZITI_NODEJS_LOG(DEBUG, "status: %zd, body: %p", status, body);
HttpsAddonData* addon_data = (HttpsAddonData*) req->data;
ZITI_NODEJS_LOG(DEBUG, "addon_data is: %p", addon_data);
HttpsReqBodyItem* item = calloc(1, sizeof(*item));
ZITI_NODEJS_LOG(DEBUG, "new HttpsReqBodyItem is: %p", item);
// Grab everything off the tlsuv_http_resp_t that we need to eventually pass on to the JS on_resp_body callback.
// If we wait until CallJs_on_resp_body is invoked to do that work, the tlsuv_http_resp_t may have already been free'd by the C-SDK
item->req = req;
item->body = (void*)body;
item->status = status;
ZITI_NODEJS_LOG(DEBUG, "calling tsfn_on_req_body: %p", addon_data->tsfn_on_req_body);
// Initiate the call into the JavaScript callback.
int rc = napi_call_threadsafe_function(
addon_data->tsfn_on_req_body,
item,
napi_tsfn_blocking);
if (rc != napi_ok) {
napi_throw_error(addon_data->env, "EINVAL", "failure to invoke JS callback");
}
}
/**
* Send Body data over active HTTPS request
*
* @param {string} [0] req
* @param {string} [1] data (we expect a Buffer)
* @param {func} [2] JS on_write callback; This is invoked from 'on_req_body' function above
*
* @returns NULL
*/
napi_value _Ziti_http_request_data(napi_env env, const napi_callback_info info) {
napi_status status;
int rc;
size_t argc = 3;
napi_value args[3];
status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Failed to parse arguments");
}
if (argc < 3) {
napi_throw_error(env, "EINVAL", "Too few arguments");
return NULL;
}
// Obtain tlsuv_http_req_t
int64_t js_req;
status = napi_get_value_int64(env, args[0], &js_req);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Failed to get Req");
}
// tlsuv_http_req_t *r = (tlsuv_http_req_t*)js_req;
HttpsReq* httpsReq = (HttpsReq*)js_req;
tlsuv_http_req_t *r = httpsReq->req;
ZITI_NODEJS_LOG(DEBUG, "req: %p", r);
HttpsAddonData* addon_data = httpsReq->addon_data;
ZITI_NODEJS_LOG(DEBUG, "addon_data is: %p", addon_data);
// If some kind of Ziti error previously occured on this request, then short-circuit now
if (httpsReq->on_resp_has_fired && (httpsReq->respCode < 0)) {
ZITI_NODEJS_LOG(DEBUG, "aborting due to previous error: %d", httpsReq->respCode);
return NULL;
}
// Obtain data to write (we expect a Buffer)
void* buffer;
size_t bufferLength;
status = napi_get_buffer_info(env, args[1], &buffer, &bufferLength);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Failed to get Buffer info");
}
ZITI_NODEJS_LOG(DEBUG, "bufferLength: %zd", bufferLength);
// Since the underlying Buffer's lifetime is not guaranteed if it's managed by the VM, we will copy the chunk into our heap
void* chunk = calloc(1, bufferLength + 1);
memcpy(chunk, buffer, bufferLength);
// Obtain ptr to JS 'on_write' callback function
napi_value js_write_cb = args[2];
napi_value work_name;
// Create a string to describe this asynchronous operation.
status = napi_create_string_utf8(
env,
"N-API on_write",
NAPI_AUTO_LENGTH,
&work_name);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Failed to napi_create_string_utf8");
}
// Convert the callback retrieved from JavaScript into a thread-safe function (tsfn)
// which we can call from a worker thread.
rc = napi_create_threadsafe_function(
env,
js_write_cb,
NULL,
work_name,
0,
1,
NULL,
NULL,
NULL,
CallJs_on_req_body,
&(addon_data->tsfn_on_req_body)
);
if (rc != napi_ok) {
napi_throw_error(env, "EINVAL", "Failed to create threadsafe_function");
}
ZITI_NODEJS_LOG(DEBUG, "napi_create_threadsafe_function addon_data->tsfn_on_req_body() : %p", addon_data->tsfn_on_req_body);
// Now, call the C-SDK to actually write the data over to the service
tlsuv_http_req_data(r, chunk, bufferLength, on_req_body );
return NULL;
}
void expose_ziti_https_request_data(napi_env env, napi_value exports) {
napi_status status;
napi_value fn;
status = napi_create_function(env, NULL, 0, _Ziti_http_request_data, NULL, &fn);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Unable to wrap native function '_Ziti_http_request_data");
}
status = napi_set_named_property(env, exports, "Ziti_http_request_data", fn);
if (status != napi_ok) {
napi_throw_error(env, NULL, "Unable to populate exports for 'Ziti_http_request_data");
}
}