Skip to content

Commit cb9390e

Browse files
committed
feat: http es module support for HMR improvements
1 parent 5b41f8e commit cb9390e

File tree

1 file changed

+52
-8
lines changed

1 file changed

+52
-8
lines changed

test-app/runtime/src/main/cpp/HMRSupport.cpp

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <jni.h>
77
#include <unordered_map>
88
#include <cstring>
9+
#include <string>
910

1011
namespace tns {
1112

@@ -183,36 +184,75 @@ bool HttpFetchText(const std::string& url, std::string& out, std::string& conten
183184
try {
184185
JEnv env;
185186

187+
// Allow network operations on the current thread (dev-only HMR path)
188+
// Some Android environments enforce StrictMode which throws NetworkOnMainThreadException
189+
// when performing network I/O on the main thread. Since this fetch runs on the JS/V8 thread
190+
// during development, explicitly relax the policy here.
191+
{
192+
jclass clsStrict = env.FindClass("android/os/StrictMode");
193+
jclass clsPolicyBuilder = env.FindClass("android/os/StrictMode$ThreadPolicy$Builder");
194+
if (clsStrict && clsPolicyBuilder) {
195+
jmethodID builderCtor = env.GetMethodID(clsPolicyBuilder, "<init>", "()V");
196+
jobject builder = env.NewObject(clsPolicyBuilder, builderCtor);
197+
if (builder) {
198+
jmethodID permitAll = env.GetMethodID(clsPolicyBuilder, "permitAll", "()Landroid/os/StrictMode$ThreadPolicy$Builder;");
199+
jobject builder2 = permitAll ? env.CallObjectMethod(builder, permitAll) : builder;
200+
jmethodID build = env.GetMethodID(clsPolicyBuilder, "build", "()Landroid/os/StrictMode$ThreadPolicy;");
201+
jobject policy = build ? env.CallObjectMethod(builder2 ? builder2 : builder, build) : nullptr;
202+
if (policy) {
203+
jmethodID setThreadPolicy = env.GetStaticMethodID(clsStrict, "setThreadPolicy", "(Landroid/os/StrictMode$ThreadPolicy;)V");
204+
if (setThreadPolicy) {
205+
env.CallStaticVoidMethod(clsStrict, setThreadPolicy, policy);
206+
}
207+
}
208+
}
209+
}
210+
}
211+
186212
jclass clsURL = env.FindClass("java/net/URL");
187213
if (!clsURL) return false;
188214
jmethodID urlCtor = env.GetMethodID(clsURL, "<init>", "(Ljava/lang/String;)V");
189215
jmethodID openConnection = env.GetMethodID(clsURL, "openConnection", "()Ljava/net/URLConnection;");
190216
jstring jUrlStr = env.NewStringUTF(url.c_str());
191217
jobject urlObj = env.NewObject(clsURL, urlCtor, jUrlStr);
192218

193-
jobject conn = env.CallObjectMethod(urlObj, openConnection);
219+
jobject conn = env.CallObjectMethod(urlObj, openConnection);
194220
if (!conn) return false;
195221

196222
jclass clsConn = env.GetObjectClass(conn);
197223
jmethodID setConnectTimeout = env.GetMethodID(clsConn, "setConnectTimeout", "(I)V");
198224
jmethodID setReadTimeout = env.GetMethodID(clsConn, "setReadTimeout", "(I)V");
225+
jmethodID setDoInput = env.GetMethodID(clsConn, "setDoInput", "(Z)V");
226+
jmethodID setUseCaches = env.GetMethodID(clsConn, "setUseCaches", "(Z)V");
199227
jmethodID setReqProp = env.GetMethodID(clsConn, "setRequestProperty", "(Ljava/lang/String;Ljava/lang/String;)V");
200-
env.CallVoidMethod(conn, setConnectTimeout, 5000);
201-
env.CallVoidMethod(conn, setReadTimeout, 5000);
202-
env.CallVoidMethod(conn, setReqProp, env.NewStringUTF("Accept"), env.NewStringUTF("application/javascript, text/javascript, */*;q=0.1"));
203-
env.CallVoidMethod(conn, setReqProp, env.NewStringUTF("Accept-Encoding"), env.NewStringUTF("identity"));
228+
env.CallVoidMethod(conn, setConnectTimeout, 15000);
229+
env.CallVoidMethod(conn, setReadTimeout, 15000);
230+
if (setDoInput) { env.CallVoidMethod(conn, setDoInput, JNI_TRUE); }
231+
if (setUseCaches) { env.CallVoidMethod(conn, setUseCaches, JNI_FALSE); }
232+
env.CallVoidMethod(conn, setReqProp, env.NewStringUTF("Accept"), env.NewStringUTF("application/javascript, text/javascript, */*;q=0.1"));
233+
env.CallVoidMethod(conn, setReqProp, env.NewStringUTF("Accept-Encoding"), env.NewStringUTF("identity"));
234+
env.CallVoidMethod(conn, setReqProp, env.NewStringUTF("Cache-Control"), env.NewStringUTF("no-cache"));
235+
env.CallVoidMethod(conn, setReqProp, env.NewStringUTF("Connection"), env.NewStringUTF("close"));
236+
env.CallVoidMethod(conn, setReqProp, env.NewStringUTF("User-Agent"), env.NewStringUTF("NativeScript-HTTP-ESM"));
204237

205238
// Try to get status via HttpURLConnection if possible
206239
jclass clsHttp = env.FindClass("java/net/HttpURLConnection");
207240
bool isHttp = clsHttp && env.IsInstanceOf(conn, clsHttp);
208241
jmethodID getResponseCode = isHttp ? env.GetMethodID(clsHttp, "getResponseCode", "()I") : nullptr;
242+
jmethodID getErrorStream = isHttp ? env.GetMethodID(clsHttp, "getErrorStream", "()Ljava/io/InputStream;") : nullptr;
209243
if (isHttp && getResponseCode) {
210244
status = env.CallIntMethod(conn, getResponseCode);
211245
}
212246

213-
// Read InputStream
247+
// Read InputStream (prefer error stream on HTTP error codes)
214248
jmethodID getInputStream = env.GetMethodID(clsConn, "getInputStream", "()Ljava/io/InputStream;");
215-
jobject inStream = env.CallObjectMethod(conn, getInputStream);
249+
jobject inStream = nullptr;
250+
if (isHttp && status >= 400 && getErrorStream) {
251+
inStream = env.CallObjectMethod(conn, getErrorStream);
252+
}
253+
if (!inStream) {
254+
inStream = env.CallObjectMethod(conn, getInputStream);
255+
}
216256
if (!inStream) return false;
217257

218258
jclass clsIS = env.GetObjectClass(inStream);
@@ -229,7 +269,11 @@ bool HttpFetchText(const std::string& url, std::string& out, std::string& conten
229269
jbyteArray buffer = env.NewByteArray(8192);
230270
while (true) {
231271
jint n = env.CallIntMethod(inStream, readMethod, buffer);
232-
if (n <= 0) break;
272+
if (n < 0) break; // -1 indicates EOF
273+
if (n == 0) {
274+
// Defensive: continue reading if zero bytes returned
275+
continue;
276+
}
233277
env.CallVoidMethod(baos, baosWrite, buffer, 0, n);
234278
}
235279

0 commit comments

Comments
 (0)