|
8 | 8 | #include <atomic> |
9 | 9 | #include <chrono> |
10 | 10 | #include <cstring> |
| 11 | +#include <memory> |
11 | 12 | #include <mutex> |
12 | 13 | #include <string> |
13 | 14 | #include <thread> |
|
20 | 21 | #include "NativeScriptException.h" |
21 | 22 | #include "Runtime.h" |
22 | 23 | #include "robin_hood.h" |
| 24 | +#include "v8-json.h" |
23 | 25 |
|
24 | 26 | namespace tns { |
25 | 27 |
|
@@ -232,25 +234,33 @@ struct CanonicalizationConfig { |
232 | 234 | std::vector<std::string> devPathPrefixes; |
233 | 235 | std::vector<std::string> preserveQueryPrefixes; |
234 | 236 | }; |
235 | | -static CanonicalizationConfig g_canonConfig; |
236 | | -static bool g_canonConfigured = false; |
| 237 | +static std::mutex g_canonConfigMutex; |
| 238 | +static std::shared_ptr<const CanonicalizationConfig> g_canonConfig; |
| 239 | + |
| 240 | +static std::shared_ptr<const CanonicalizationConfig> CurrentCanonicalizationConfig() { |
| 241 | + std::lock_guard<std::mutex> lock(g_canonConfigMutex); |
| 242 | + return g_canonConfig; |
| 243 | +} |
237 | 244 |
|
238 | 245 | static void SetCanonicalizationConfig(CanonicalizationConfig config) { |
239 | | - g_canonConfig = std::move(config); |
240 | | - g_canonConfigured = true; |
| 246 | + auto snapshot = std::make_shared<const CanonicalizationConfig>(std::move(config)); |
| 247 | + { |
| 248 | + std::lock_guard<std::mutex> lock(g_canonConfigMutex); |
| 249 | + g_canonConfig = snapshot; |
| 250 | + } |
241 | 251 | if (IsScriptLoadingLogEnabled()) { |
242 | 252 | DEBUG_WRITE_FORCE( |
243 | 253 | "[ns:module configureLoader] canonicalization set (strip=%lu devPrefixes=%lu " |
244 | 254 | "preserve=%lu)", |
245 | | - (unsigned long)g_canonConfig.stripParams.size(), |
246 | | - (unsigned long)g_canonConfig.devPathPrefixes.size(), |
247 | | - (unsigned long)g_canonConfig.preserveQueryPrefixes.size()); |
| 255 | + (unsigned long)snapshot->stripParams.size(), |
| 256 | + (unsigned long)snapshot->devPathPrefixes.size(), |
| 257 | + (unsigned long)snapshot->preserveQueryPrefixes.size()); |
248 | 258 | } |
249 | 259 | } |
250 | 260 |
|
251 | 261 | static void ResetCanonicalizationConfig() { |
252 | | - g_canonConfig = CanonicalizationConfig{}; |
253 | | - g_canonConfigured = false; |
| 262 | + std::lock_guard<std::mutex> lock(g_canonConfigMutex); |
| 263 | + g_canonConfig.reset(); |
254 | 264 | } |
255 | 265 |
|
256 | 266 | std::string CanonicalizeHttpUrlKey(const std::string& url) { |
@@ -278,16 +288,17 @@ std::string CanonicalizeHttpUrlKey(const std::string& url) { |
278 | 288 | std::string originAndPath = (qPos == std::string::npos) ? noHash : noHash.substr(0, qPos); |
279 | 289 | std::string query = (qPos == std::string::npos) ? std::string() : noHash.substr(qPos + 1); |
280 | 290 |
|
| 291 | + auto canon = CurrentCanonicalizationConfig(); |
281 | 292 | { |
282 | 293 | std::string pathOnly = originAndPath.substr(pathStart); |
283 | | - if (g_canonConfigured) { |
284 | | - for (const auto& p : g_canonConfig.preserveQueryPrefixes) { |
| 294 | + if (canon) { |
| 295 | + for (const auto& p : canon->preserveQueryPrefixes) { |
285 | 296 | if (!p.empty() && pathOnly.find(p) != std::string::npos) { |
286 | 297 | return noHash; |
287 | 298 | } |
288 | 299 | } |
289 | 300 | bool isDevEndpoint = false; |
290 | | - for (const auto& p : g_canonConfig.devPathPrefixes) { |
| 301 | + for (const auto& p : canon->devPathPrefixes) { |
291 | 302 | if (!p.empty() && StartsWith(pathOnly, p.c_str())) { |
292 | 303 | isDevEndpoint = true; |
293 | 304 | break; |
@@ -322,9 +333,9 @@ std::string CanonicalizeHttpUrlKey(const std::string& url) { |
322 | 333 | size_t eq = pair.find('='); |
323 | 334 | std::string name = (eq == std::string::npos) ? pair : pair.substr(0, eq); |
324 | 335 | bool drop; |
325 | | - if (g_canonConfigured) { |
326 | | - drop = std::find(g_canonConfig.stripParams.begin(), g_canonConfig.stripParams.end(), |
327 | | - name) != g_canonConfig.stripParams.end(); |
| 336 | + if (canon) { |
| 337 | + drop = std::find(canon->stripParams.begin(), canon->stripParams.end(), |
| 338 | + name) != canon->stripParams.end(); |
328 | 339 | } else { |
329 | 340 | drop = (name == "import" || name == "t" || name == "v"); |
330 | 341 | } |
@@ -698,14 +709,29 @@ static bool PerformHttpFetchOnceSync(const std::string& url, std::string& out, |
698 | 709 | jobject baos = env.NewObject(clsBAOS, baosCtor); |
699 | 710 |
|
700 | 711 | jbyteArray buffer = env.NewByteArray(8192); |
| 712 | + bool readFailed = false; |
701 | 713 | while (true) { |
702 | 714 | jint n = env.CallIntMethod(inStream, readMethod, buffer); |
| 715 | + std::string excClass, excMsg; |
| 716 | + if (DrainPendingJniException(env, excClass, excMsg)) { |
| 717 | + RecordLastHttpFetchError("read-body", excClass, excMsg); |
| 718 | + if (IsScriptLoadingLogEnabled()) { |
| 719 | + DEBUG_WRITE_FORCE( |
| 720 | + "[http-esm][fetch][exception] stage=read-body url=%s class=%s msg=%s", |
| 721 | + url.c_str(), excClass.c_str(), excMsg.c_str()); |
| 722 | + } |
| 723 | + readFailed = true; |
| 724 | + break; |
| 725 | + } |
703 | 726 | if (n < 0) break; |
704 | 727 | if (n == 0) continue; |
705 | 728 | env.CallVoidMethod(baos, baosWrite, buffer, 0, n); |
706 | 729 | } |
707 | 730 |
|
708 | 731 | env.CallVoidMethod(inStream, closeIS); |
| 732 | + if (readFailed) { |
| 733 | + return false; |
| 734 | + } |
709 | 735 | jbyteArray bytes = static_cast<jbyteArray>(env.CallObjectMethod(baos, baosToByteArray)); |
710 | 736 | env.CallVoidMethod(baos, baosClose); |
711 | 737 |
|
@@ -773,6 +799,26 @@ void FetchModuleBodyAsync(const std::string& url, |
773 | 799 | } |
774 | 800 |
|
775 | 801 | std::thread([url, completion = std::move(completion)]() mutable { |
| 802 | + JavaVM* jvm = Runtime::GetJVM(); |
| 803 | + bool attachedHere = false; |
| 804 | + if (jvm != nullptr) { |
| 805 | + JNIEnv* raw = nullptr; |
| 806 | + if (jvm->GetEnv(reinterpret_cast<void**>(&raw), JNI_VERSION_1_6) != JNI_OK) { |
| 807 | + if (jvm->AttachCurrentThread(&raw, nullptr) == JNI_OK) { |
| 808 | + attachedHere = true; |
| 809 | + } |
| 810 | + } |
| 811 | + } |
| 812 | + struct DetachIfAttached { |
| 813 | + JavaVM* jvm; |
| 814 | + bool attached; |
| 815 | + ~DetachIfAttached() { |
| 816 | + if (attached && jvm != nullptr) { |
| 817 | + jvm->DetachCurrentThread(); |
| 818 | + } |
| 819 | + } |
| 820 | + } detachGuard{jvm, attachedHere}; |
| 821 | + |
776 | 822 | std::string out; |
777 | 823 | std::string contentType; |
778 | 824 | int status = 0; |
@@ -870,19 +916,9 @@ void ConfigureLoaderCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
870 | 916 | v8::String::Utf8Value utf8(isolate, importMapVal); |
871 | 917 | if (*utf8) jsonStr = *utf8; |
872 | 918 | } else if (importMapVal->IsObject()) { |
873 | | - v8::Local<v8::Object> jsonObj = |
874 | | - ctx->Global() |
875 | | - ->Get(ctx, ToV8String(isolate, "JSON")) |
876 | | - .ToLocalChecked() |
877 | | - .As<v8::Object>(); |
878 | | - v8::Local<v8::Function> stringify = |
879 | | - jsonObj->Get(ctx, ToV8String(isolate, "stringify")) |
880 | | - .ToLocalChecked() |
881 | | - .As<v8::Function>(); |
882 | | - v8::Local<v8::Value> args[] = {importMapVal}; |
883 | | - v8::Local<v8::Value> result; |
884 | | - if (stringify->Call(ctx, jsonObj, 1, args).ToLocal(&result) && result->IsString()) { |
885 | | - v8::String::Utf8Value utf8(isolate, result); |
| 919 | + v8::Local<v8::String> stringified; |
| 920 | + if (v8::JSON::Stringify(ctx, importMapVal).ToLocal(&stringified)) { |
| 921 | + v8::String::Utf8Value utf8(isolate, stringified); |
886 | 922 | if (*utf8) jsonStr = *utf8; |
887 | 923 | } |
888 | 924 | } |
|
0 commit comments