nfets 0.0.58 → 0.0.60

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.
@@ -0,0 +1,436 @@
1
+ #include <node_api.h>
2
+
3
+ #include <windows.h>
4
+ #include <winhttp.h>
5
+ #include <wincrypt.h>
6
+ #include <string>
7
+ #include <vector>
8
+ #include <stdexcept>
9
+
10
+ #pragma comment(lib, "winhttp.lib")
11
+ #pragma comment(lib, "crypt32.lib")
12
+
13
+ std::wstring utf8_to_wstring(const std::string &str)
14
+ {
15
+ if (str.empty())
16
+ return std::wstring();
17
+ int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
18
+ std::wstring wstrTo(size_needed, 0);
19
+ MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
20
+ return wstrTo;
21
+ }
22
+
23
+ std::string wstring_to_utf8(const std::wstring &wstr)
24
+ {
25
+ if (wstr.empty())
26
+ return std::string();
27
+ int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
28
+ std::string strTo(size_needed, 0);
29
+ WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
30
+ return strTo;
31
+ }
32
+
33
+ napi_value MakeRequest(napi_env env, napi_callback_info info)
34
+ {
35
+ size_t argc = 4;
36
+ napi_value args[4];
37
+ napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
38
+
39
+ if (argc < 4)
40
+ {
41
+ napi_value error_msg;
42
+ napi_create_string_utf8(env, "Expected: subject (string), method (string), url (string), options (object)", NAPI_AUTO_LENGTH, &error_msg);
43
+ napi_value error;
44
+ napi_create_type_error(env, nullptr, error_msg, &error);
45
+ napi_throw(env, error);
46
+ return nullptr;
47
+ }
48
+
49
+ napi_valuetype arg0_type, arg1_type, arg2_type, arg3_type;
50
+ napi_typeof(env, args[0], &arg0_type);
51
+ napi_typeof(env, args[1], &arg1_type);
52
+ napi_typeof(env, args[2], &arg2_type);
53
+ napi_typeof(env, args[3], &arg3_type);
54
+
55
+ if (arg0_type != napi_string || arg1_type != napi_string || arg2_type != napi_string || arg3_type != napi_object)
56
+ {
57
+ napi_value error_msg;
58
+ napi_create_string_utf8(env, "Expected: subject (string), method (string), url (string), options (object)", NAPI_AUTO_LENGTH, &error_msg);
59
+ napi_value error;
60
+ napi_create_type_error(env, nullptr, error_msg, &error);
61
+ napi_throw(env, error);
62
+ return nullptr;
63
+ }
64
+
65
+ size_t subject_len = 0;
66
+ napi_get_value_string_utf8(env, args[0], nullptr, 0, &subject_len);
67
+ std::string subject_str(subject_len, '\0');
68
+ napi_get_value_string_utf8(env, args[0], &subject_str[0], subject_len + 1, nullptr);
69
+ std::wstring subject = utf8_to_wstring(subject_str);
70
+
71
+ size_t method_len = 0;
72
+ napi_get_value_string_utf8(env, args[1], nullptr, 0, &method_len);
73
+ std::string method_str(method_len, '\0');
74
+ napi_get_value_string_utf8(env, args[1], &method_str[0], method_len + 1, nullptr);
75
+ std::wstring method = utf8_to_wstring(method_str);
76
+
77
+ size_t url_len = 0;
78
+ napi_get_value_string_utf8(env, args[2], nullptr, 0, &url_len);
79
+ std::string url_str(url_len, '\0');
80
+ napi_get_value_string_utf8(env, args[2], &url_str[0], url_len + 1, nullptr);
81
+ std::wstring url = utf8_to_wstring(url_str);
82
+
83
+ napi_value optionsObj = args[3];
84
+
85
+ std::wstring headers;
86
+ std::string body;
87
+ int timeout = 30000;
88
+
89
+ napi_value headers_key;
90
+ napi_create_string_utf8(env, "headers", NAPI_AUTO_LENGTH, &headers_key);
91
+ napi_value headers_val;
92
+ napi_status status = napi_get_property(env, optionsObj, headers_key, &headers_val);
93
+ if (status == napi_ok)
94
+ {
95
+ napi_valuetype headers_type;
96
+ napi_typeof(env, headers_val, &headers_type);
97
+ if (headers_type == napi_string)
98
+ {
99
+ size_t headers_len = 0;
100
+ napi_get_value_string_utf8(env, headers_val, nullptr, 0, &headers_len);
101
+ std::string headers_str(headers_len, '\0');
102
+ napi_get_value_string_utf8(env, headers_val, &headers_str[0], headers_len + 1, nullptr);
103
+ headers = utf8_to_wstring(headers_str);
104
+ }
105
+ }
106
+
107
+ napi_value body_key;
108
+ napi_create_string_utf8(env, "body", NAPI_AUTO_LENGTH, &body_key);
109
+ napi_value body_val;
110
+ status = napi_get_property(env, optionsObj, body_key, &body_val);
111
+ if (status == napi_ok)
112
+ {
113
+ bool is_buffer;
114
+ napi_is_buffer(env, body_val, &is_buffer);
115
+ if (is_buffer)
116
+ {
117
+ BYTE *bodyData = nullptr;
118
+ size_t bodyLen = 0;
119
+ napi_get_buffer_info(env, body_val, (void **)&bodyData, &bodyLen);
120
+ body = std::string((char *)bodyData, bodyLen);
121
+ }
122
+ }
123
+
124
+ napi_value timeout_key;
125
+ napi_create_string_utf8(env, "timeout", NAPI_AUTO_LENGTH, &timeout_key);
126
+ napi_value timeout_val;
127
+ status = napi_get_property(env, optionsObj, timeout_key, &timeout_val);
128
+ if (status == napi_ok)
129
+ {
130
+ napi_valuetype timeout_type;
131
+ napi_typeof(env, timeout_val, &timeout_type);
132
+ if (timeout_type == napi_number)
133
+ {
134
+ double timeout_double;
135
+ napi_get_value_double(env, timeout_val, &timeout_double);
136
+ timeout = (int)timeout_double;
137
+ }
138
+ }
139
+
140
+ HCERTSTORE hStore = NULL;
141
+ PCCERT_CONTEXT pCertContext = NULL;
142
+ HINTERNET hSession = NULL;
143
+ HINTERNET hConnect = NULL;
144
+ HINTERNET hRequest = NULL;
145
+ napi_value result = nullptr;
146
+
147
+ try
148
+ {
149
+ hStore = CertOpenSystemStoreW(0, L"MY");
150
+ if (!hStore)
151
+ {
152
+ throw std::runtime_error("Failed to open certificate store");
153
+ }
154
+
155
+ pCertContext = CertFindCertificateInStore(
156
+ hStore,
157
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
158
+ 0,
159
+ CERT_FIND_SUBJECT_STR_W,
160
+ (LPVOID)subject.c_str(),
161
+ NULL);
162
+
163
+ if (!pCertContext)
164
+ {
165
+ throw std::runtime_error("Certificate not found in store");
166
+ }
167
+
168
+ URL_COMPONENTS urlComp;
169
+ ZeroMemory(&urlComp, sizeof(urlComp));
170
+ urlComp.dwStructSize = sizeof(urlComp);
171
+ urlComp.dwSchemeLength = (DWORD)-1;
172
+ urlComp.dwHostNameLength = (DWORD)-1;
173
+ urlComp.dwUrlPathLength = (DWORD)-1;
174
+ urlComp.dwExtraInfoLength = (DWORD)-1;
175
+
176
+ std::vector<wchar_t> schemeBuffer(32);
177
+ std::vector<wchar_t> hostNameBuffer(256);
178
+ std::vector<wchar_t> urlPathBuffer(2048);
179
+ std::vector<wchar_t> extraInfoBuffer(256);
180
+
181
+ urlComp.lpszScheme = schemeBuffer.data();
182
+ urlComp.dwSchemeLength = (DWORD)schemeBuffer.size();
183
+ urlComp.lpszHostName = hostNameBuffer.data();
184
+ urlComp.dwHostNameLength = (DWORD)hostNameBuffer.size();
185
+ urlComp.lpszUrlPath = urlPathBuffer.data();
186
+ urlComp.dwUrlPathLength = (DWORD)urlPathBuffer.size();
187
+ urlComp.lpszExtraInfo = extraInfoBuffer.data();
188
+ urlComp.dwExtraInfoLength = (DWORD)extraInfoBuffer.size();
189
+
190
+ std::vector<wchar_t> urlBuffer(url.length() + 1);
191
+ wcscpy_s(urlBuffer.data(), urlBuffer.size(), url.c_str());
192
+
193
+ if (!WinHttpCrackUrl(urlBuffer.data(), (DWORD)url.length(), 0, &urlComp))
194
+ {
195
+ throw std::runtime_error("Failed to parse URL. Error: " + std::to_string(GetLastError()));
196
+ }
197
+
198
+ std::wstring hostName(hostNameBuffer.data(), urlComp.dwHostNameLength);
199
+ std::wstring urlPath(urlPathBuffer.data(), urlComp.dwUrlPathLength);
200
+ if (urlPath.empty())
201
+ {
202
+ urlPath = L"/";
203
+ }
204
+
205
+ if (urlComp.dwExtraInfoLength > 0)
206
+ {
207
+ std::wstring extraInfo(extraInfoBuffer.data(), urlComp.dwExtraInfoLength);
208
+ urlPath += extraInfo;
209
+ }
210
+
211
+ INTERNET_PORT port = urlComp.nPort;
212
+ if (port == 0)
213
+ {
214
+ port = (urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
215
+ }
216
+
217
+ hSession = WinHttpOpen(
218
+ L"Node.js WinHTTP Client/1.0",
219
+ WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
220
+ WINHTTP_NO_PROXY_NAME,
221
+ WINHTTP_NO_PROXY_BYPASS,
222
+ 0);
223
+
224
+ if (!hSession)
225
+ {
226
+ throw std::runtime_error("Failed to create WinHTTP session");
227
+ }
228
+
229
+ DWORD timeoutMs = timeout;
230
+ WinHttpSetTimeouts(hSession, timeoutMs, timeoutMs, timeoutMs, timeoutMs);
231
+
232
+ hConnect = WinHttpConnect(hSession, hostName.c_str(), port, 0);
233
+ if (!hConnect)
234
+ {
235
+ throw std::runtime_error("Failed to connect to server");
236
+ }
237
+
238
+ DWORD dwFlags = WINHTTP_FLAG_SECURE;
239
+ if (urlComp.nScheme == INTERNET_SCHEME_HTTPS)
240
+ {
241
+ dwFlags |= WINHTTP_FLAG_SECURE;
242
+ }
243
+
244
+ hRequest = WinHttpOpenRequest(
245
+ hConnect,
246
+ method.c_str(),
247
+ urlPath.c_str(),
248
+ NULL,
249
+ WINHTTP_NO_REFERER,
250
+ WINHTTP_DEFAULT_ACCEPT_TYPES,
251
+ dwFlags);
252
+
253
+ if (!hRequest)
254
+ {
255
+ throw std::runtime_error("Failed to create request");
256
+ }
257
+
258
+ if (!WinHttpSetOption(
259
+ hRequest,
260
+ WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
261
+ (LPVOID)pCertContext,
262
+ sizeof(CERT_CONTEXT)))
263
+ {
264
+ throw std::runtime_error("Failed to set client certificate");
265
+ }
266
+
267
+ if (!headers.empty())
268
+ {
269
+ if (!WinHttpAddRequestHeaders(
270
+ hRequest,
271
+ headers.c_str(),
272
+ (DWORD)headers.length(),
273
+ WINHTTP_ADDREQ_FLAG_ADD))
274
+ {
275
+ throw std::runtime_error("Failed to add request headers");
276
+ }
277
+ }
278
+
279
+ BOOL bResults = FALSE;
280
+ if (body.empty())
281
+ {
282
+ bResults = WinHttpSendRequest(
283
+ hRequest,
284
+ WINHTTP_NO_ADDITIONAL_HEADERS,
285
+ 0,
286
+ WINHTTP_NO_REQUEST_DATA,
287
+ 0,
288
+ 0,
289
+ 0);
290
+ }
291
+ else
292
+ {
293
+ bResults = WinHttpSendRequest(
294
+ hRequest,
295
+ WINHTTP_NO_ADDITIONAL_HEADERS,
296
+ 0,
297
+ (LPVOID)body.c_str(),
298
+ (DWORD)body.length(),
299
+ (DWORD)body.length(),
300
+ 0);
301
+ }
302
+
303
+ if (!bResults)
304
+ {
305
+ throw std::runtime_error("Failed to send request. Error: " + std::to_string(GetLastError()));
306
+ }
307
+
308
+ if (!WinHttpReceiveResponse(hRequest, NULL))
309
+ {
310
+ throw std::runtime_error("Failed to receive response. Error: " + std::to_string(GetLastError()));
311
+ }
312
+
313
+ DWORD dwStatusCode = 0;
314
+ DWORD dwStatusCodeSize = sizeof(dwStatusCode);
315
+ if (!WinHttpQueryHeaders(
316
+ hRequest,
317
+ WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
318
+ WINHTTP_HEADER_NAME_BY_INDEX,
319
+ &dwStatusCode,
320
+ &dwStatusCodeSize,
321
+ WINHTTP_NO_HEADER_INDEX))
322
+ {
323
+ throw std::runtime_error("Failed to query status code");
324
+ }
325
+
326
+ DWORD dwHeaderSize = 0;
327
+ WinHttpQueryHeaders(
328
+ hRequest,
329
+ WINHTTP_QUERY_RAW_HEADERS_CRLF,
330
+ WINHTTP_HEADER_NAME_BY_INDEX,
331
+ WINHTTP_NO_OUTPUT_BUFFER,
332
+ &dwHeaderSize,
333
+ WINHTTP_NO_HEADER_INDEX);
334
+
335
+ std::vector<wchar_t> headerBuffer(dwHeaderSize / sizeof(wchar_t));
336
+ if (dwHeaderSize > 0)
337
+ {
338
+ WinHttpQueryHeaders(
339
+ hRequest,
340
+ WINHTTP_QUERY_RAW_HEADERS_CRLF,
341
+ WINHTTP_HEADER_NAME_BY_INDEX,
342
+ headerBuffer.data(),
343
+ &dwHeaderSize,
344
+ WINHTTP_NO_HEADER_INDEX);
345
+ }
346
+
347
+ std::vector<BYTE> responseData;
348
+ DWORD dwBytesRead = 0;
349
+ do
350
+ {
351
+ BYTE buffer[8192];
352
+ dwBytesRead = 0;
353
+ if (!WinHttpReadData(hRequest, buffer, sizeof(buffer), &dwBytesRead))
354
+ {
355
+ DWORD dwError = GetLastError();
356
+ if (dwError != ERROR_WINHTTP_CONNECTION_ERROR)
357
+ {
358
+ break;
359
+ }
360
+ }
361
+ if (dwBytesRead > 0)
362
+ {
363
+ responseData.insert(responseData.end(), buffer, buffer + dwBytesRead);
364
+ }
365
+ } while (dwBytesRead > 0);
366
+
367
+ napi_create_object(env, &result);
368
+
369
+ napi_value statusCode_key;
370
+ napi_create_string_utf8(env, "statusCode", NAPI_AUTO_LENGTH, &statusCode_key);
371
+ napi_value statusCode_val;
372
+ napi_create_int32(env, dwStatusCode, &statusCode_val);
373
+ napi_set_property(env, result, statusCode_key, statusCode_val);
374
+
375
+ if (dwHeaderSize > 0)
376
+ {
377
+ std::string headersStr = wstring_to_utf8(std::wstring(headerBuffer.data()));
378
+ napi_value headers_key;
379
+ napi_create_string_utf8(env, "headers", NAPI_AUTO_LENGTH, &headers_key);
380
+ napi_value headers_val;
381
+ napi_create_string_utf8(env, headersStr.c_str(), NAPI_AUTO_LENGTH, &headers_val);
382
+ napi_set_property(env, result, headers_key, headers_val);
383
+ }
384
+
385
+ napi_value body_key;
386
+ napi_create_string_utf8(env, "body", NAPI_AUTO_LENGTH, &body_key);
387
+ napi_value body_val;
388
+ if (!responseData.empty())
389
+ {
390
+ napi_create_buffer_copy(env, responseData.size(), responseData.data(), nullptr, &body_val);
391
+ }
392
+ else
393
+ {
394
+ napi_create_buffer(env, 0, nullptr, &body_val);
395
+ }
396
+ napi_set_property(env, result, body_key, body_val);
397
+ }
398
+ catch (const std::exception &e)
399
+ {
400
+ napi_throw_error(env, nullptr, e.what());
401
+ result = nullptr;
402
+ }
403
+
404
+ if (hRequest)
405
+ {
406
+ WinHttpCloseHandle(hRequest);
407
+ }
408
+ if (hConnect)
409
+ {
410
+ WinHttpCloseHandle(hConnect);
411
+ }
412
+ if (hSession)
413
+ {
414
+ WinHttpCloseHandle(hSession);
415
+ }
416
+ if (pCertContext)
417
+ {
418
+ CertFreeCertificateContext(pCertContext);
419
+ }
420
+ if (hStore)
421
+ {
422
+ CertCloseStore(hStore, 0);
423
+ }
424
+
425
+ return result;
426
+ }
427
+
428
+ napi_value Init(napi_env env, napi_value exports)
429
+ {
430
+ napi_value fn;
431
+ napi_create_function(env, nullptr, 0, MakeRequest, nullptr, &fn);
432
+ napi_set_named_property(env, exports, "request", fn);
433
+ return exports;
434
+ }
435
+
436
+ NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)