328 lines
9.2 KiB
C
328 lines
9.2 KiB
C
#include "cell.h"
|
|
|
|
|
|
#if defined(_WIN32)
|
|
#include <windows.h>
|
|
#include <winhttp.h>
|
|
#include <stdio.h>
|
|
|
|
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 <emscripten/fetch.h>
|
|
#include <emscripten/emscripten.h>
|
|
#include <stdio.h>
|
|
|
|
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 <TargetConditionals.h>
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
#include <CFNetwork/CFNetwork.h>
|
|
#include <stdio.h>
|
|
|
|
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 <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
// 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_IsText(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;
|
|
}
|