#include "cell.h" #if defined(_WIN32) #include #include #include typedef unsigned char par_byte; static void par_easycurl_init(unsigned int flags) { (void)flags; } static int par_easycurl_to_memory(char const *url, par_byte **data, int *nbytes) { if (!url || !data || !nbytes) return 0; *data = NULL; *nbytes = 0; int success = 0; HINTERNET hSession = NULL, hConnect = NULL, hRequest = NULL; wchar_t *wUrl = NULL, *wHost = NULL, *wPath = NULL; char *buffer = NULL; int bufferSize = 0; int len = MultiByteToWideChar(CP_UTF8, 0, url, -1, NULL, 0); if (len <= 0) goto cleanup; wUrl = (wchar_t *)malloc(len * sizeof(wchar_t)); if (!wUrl) goto cleanup; MultiByteToWideChar(CP_UTF8, 0, url, -1, wUrl, len); URL_COMPONENTS urlComp; ZeroMemory(&urlComp, sizeof(urlComp)); urlComp.dwStructSize = sizeof(urlComp); urlComp.dwHostNameLength = (DWORD)-1; urlComp.dwUrlPathLength = (DWORD)-1; if (!WinHttpCrackUrl(wUrl, (DWORD)wcslen(wUrl), 0, &urlComp)) goto cleanup; wHost = (wchar_t *)malloc((urlComp.dwHostNameLength + 1) * sizeof(wchar_t)); if (!wHost) goto cleanup; wcsncpy(wHost, urlComp.lpszHostName, urlComp.dwHostNameLength); wHost[urlComp.dwHostNameLength] = L'\0'; wPath = (wchar_t *)malloc((urlComp.dwUrlPathLength + 1) * sizeof(wchar_t)); if (!wPath) goto cleanup; wcsncpy(wPath, urlComp.lpszUrlPath, urlComp.dwUrlPathLength); wPath[urlComp.dwUrlPathLength] = L'\0'; hSession = WinHttpOpen(L"cell/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (!hSession) goto cleanup; hConnect = WinHttpConnect(hSession, wHost, urlComp.nPort, 0); if (!hConnect) goto cleanup; DWORD dwFlags = (urlComp.nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0; hRequest = WinHttpOpenRequest(hConnect, L"GET", wPath, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, dwFlags); if (!hRequest) goto cleanup; if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0)) goto cleanup; if (!WinHttpReceiveResponse(hRequest, NULL)) goto cleanup; DWORD dwStatusCode = 0; DWORD dwSize = sizeof(dwStatusCode); if (!WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &dwStatusCode, &dwSize, WINHTTP_NO_HEADER_INDEX)) { goto cleanup; } if (dwStatusCode >= 400) goto cleanup; buffer = (char *)malloc(1); if (!buffer) goto cleanup; buffer[0] = 0; for (;;) { DWORD dwAvailable = 0; if (!WinHttpQueryDataAvailable(hRequest, &dwAvailable)) goto cleanup; if (dwAvailable == 0) break; char *newBuffer = (char *)realloc(buffer, bufferSize + dwAvailable + 1); if (!newBuffer) goto cleanup; buffer = newBuffer; DWORD dwDownloaded = 0; if (!WinHttpReadData(hRequest, buffer + bufferSize, dwAvailable, &dwDownloaded)) goto cleanup; bufferSize += dwDownloaded; buffer[bufferSize] = 0; } *data = (par_byte *)buffer; *nbytes = bufferSize; success = 1; buffer = NULL; cleanup: if (wUrl) free(wUrl); if (wHost) free(wHost); if (wPath) free(wPath); if (buffer) free(buffer); if (hRequest) WinHttpCloseHandle(hRequest); if (hConnect) WinHttpCloseHandle(hConnect); if (hSession) WinHttpCloseHandle(hSession); return success; } #elif defined(__EMSCRIPTEN__) #include #include #include typedef unsigned char par_byte; static void par_easycurl_init(unsigned int flags) { (void)flags; } typedef struct { par_byte *data; int nbytes; int complete; int success; } fetch_result; static void on_fetch_success(emscripten_fetch_t *fetch) { fetch_result *res = (fetch_result *)fetch->userData; res->nbytes = fetch->numBytes; res->data = (par_byte *)malloc((size_t)res->nbytes + 1); if (!res->data) { res->success = 0; } else { memcpy(res->data, fetch->data, (size_t)res->nbytes); res->data[res->nbytes] = 0; res->success = 1; } res->complete = 1; emscripten_fetch_close(fetch); } static void on_fetch_failure(emscripten_fetch_t *fetch) { fetch_result *res = (fetch_result *)fetch->userData; res->success = 0; res->complete = 1; emscripten_fetch_close(fetch); } static int par_easycurl_to_memory(char const *url, par_byte **data, int *nbytes) { if (!url || !data || !nbytes) return 0; *data = NULL; *nbytes = 0; fetch_result res = {0}; emscripten_fetch_attr_t attr; emscripten_fetch_attr_init(&attr); strcpy(attr.requestMethod, "GET"); attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; attr.userData = &res; attr.onsuccess = on_fetch_success; attr.onerror = on_fetch_failure; attr.attributes |= EMSCRIPTEN_FETCH_SYNCHRONOUS; emscripten_fetch_t *fetch = emscripten_fetch(&attr, url); if (!fetch) return 0; if (!res.complete || !res.success) return 0; *data = res.data; *nbytes = res.nbytes; return 1; } #elif defined(__APPLE__) #include #include #include #include typedef unsigned char par_byte; static void par_easycurl_init(unsigned int flags) { (void)flags; } typedef struct { CFMutableDataRef data; int success; int complete; } cf_result; static void cf_response_callback(CFReadStreamRef stream, CFStreamEventType type, void *clientCallBackInfo) { cf_result *res = (cf_result *)clientCallBackInfo; if (type == kCFStreamEventHasBytesAvailable) { UInt8 buffer[4096]; CFIndex bytesRead = CFReadStreamRead(stream, buffer, sizeof(buffer)); if (bytesRead > 0) { CFDataAppendBytes(res->data, buffer, bytesRead); } } else if (type == kCFStreamEventEndEncountered) { res->success = 1; res->complete = 1; CFReadStreamClose(stream); } else if (type == kCFStreamEventErrorOccurred) { res->success = 0; res->complete = 1; CFReadStreamClose(stream); } } static int par_easycurl_to_memory(char const *url, par_byte **data, int *nbytes) { if (!url || !data || !nbytes) return 0; *data = NULL; *nbytes = 0; CFStringRef cfurl = CFStringCreateWithCString(NULL, url, kCFStringEncodingUTF8); if (!cfurl) return 0; CFURLRef cfurlRef = CFURLCreateWithString(NULL, cfurl, NULL); CFRelease(cfurl); if (!cfurlRef) return 0; CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), cfurlRef, kCFHTTPVersion1_1); CFRelease(cfurlRef); if (!request) return 0; CFReadStreamRef stream = CFReadStreamCreateForHTTPRequest(NULL, request); CFRelease(request); if (!stream) return 0; cf_result res; res.data = CFDataCreateMutable(NULL, 0); res.success = 0; res.complete = 0; if (!res.data) { CFRelease(stream); return 0; } CFStreamClientContext ctx = {0, &res, NULL, NULL, NULL}; CFOptionFlags events = kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered; if (!CFReadStreamSetClient(stream, events, cf_response_callback, &ctx)) { CFRelease(stream); CFRelease(res.data); return 0; } CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); if (!CFReadStreamOpen(stream)) { CFReadStreamUnscheduleFromRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); CFRelease(stream); CFRelease(res.data); return 0; } while (!res.complete) { CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.01, false); } CFReadStreamUnscheduleFromRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); CFRelease(stream); if (!res.success) { CFRelease(res.data); return 0; } CFIndex len = CFDataGetLength(res.data); par_byte *bytes = (par_byte *)malloc((size_t)len + 1); if (!bytes) { CFRelease(res.data); return 0; } CFDataGetBytes(res.data, CFRangeMake(0, len), bytes); bytes[len] = 0; CFRelease(res.data); *data = bytes; *nbytes = (int)len; return 1; } #else #define PAR_EASYCURL_IMPLEMENTATION #include "par_easycurl.h" #endif #include #include #include // Performs a blocking HTTP GET and returns a QuickJS Blob of the body static JSValue js_fetch_picoparser(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { if (argc < 1 || !JS_IsString(argv[0])) return JS_ThrowTypeError(ctx, "fetch: URL string required"); const char *url = JS_ToCString(ctx, argv[0]); if (!url) return JS_ThrowTypeError(ctx, "fetch: invalid URL"); par_byte *data = NULL; int nbytes = 0; if (!par_easycurl_to_memory(url, &data, &nbytes)) { JS_FreeCString(ctx, url); return JS_ThrowTypeError(ctx, "fetch: failed to fetch URL"); } JS_FreeCString(ctx, url); // Return a Blob wrapping the data JSValue blob = js_new_blob_stoned_copy(ctx, data, (size_t)nbytes); free(data); // par_easycurl allocates with malloc, so we free it return blob; } static const JSCFunctionListEntry js_http_funcs[] = { JS_CFUNC_DEF("fetch", 2, js_fetch_picoparser), }; JSValue js_http_use(JSContext *js) { par_easycurl_init(0); // Initialize platform HTTP backend JSValue obj = JS_NewObject(js); JS_SetPropertyFunctionList(js, obj, js_http_funcs, sizeof(js_http_funcs)/sizeof(js_http_funcs[0])); return obj; }