summaryrefslogtreecommitdiff
path: root/quickjs/quickjs-libc.c
diff options
context:
space:
mode:
Diffstat (limited to 'quickjs/quickjs-libc.c')
-rw-r--r--quickjs/quickjs-libc.c753
1 files changed, 424 insertions, 329 deletions
diff --git a/quickjs/quickjs-libc.c b/quickjs/quickjs-libc.c
index 75114a6..acecd8b 100644
--- a/quickjs/quickjs-libc.c
+++ b/quickjs/quickjs-libc.c
@@ -79,10 +79,11 @@ typedef sig_t sighandler_t;
*/
#ifndef NO_HTTP
-#include <curl/curl.h>
#include <arpa/inet.h>
+#include "quickjs-http.h"
#endif
+
typedef struct {
struct list_head link;
int fd;
@@ -135,6 +136,23 @@ typedef struct {
typedef struct {
struct list_head link;
+ int request_id;
+ int status;
+ char *errmsg;
+ char **response_headers;
+ void *body;
+ size_t body_len;
+} JSHttpMessage;
+
+typedef struct {
+ pthread_mutex_t mutex;
+ struct list_head msg_queue; /* list of JSHttpMessage.link */
+ int read_fd;
+ int write_fd;
+} JSHttpMessagePipe;
+
+typedef struct {
+ struct list_head link;
JSWorkerMessagePipe *recv_pipe;
JSValue on_message_func;
} JSWorkerMessageHandler;
@@ -151,6 +169,9 @@ typedef struct JSThreadState {
// send/receive message to/from the host in the main thread
JSHostMessagePipe *host_pipe;
+ // receive messages from the HTTP client thread
+ JSHttpMessagePipe *http_pipe;
+
JSValue on_host_message_func;
JSHostMessageHandlerFn host_message_handler_f;
@@ -158,11 +179,12 @@ typedef struct JSThreadState {
int is_worker_thread;
+ struct list_head http_requests;
+
#ifndef NO_HTTP
- CURLM *curlm;
- CURLSH *curlsh;
- struct list_head curl_requests;
+ struct JSHttpClientImplementation *http_client_impl;
#endif
+
} JSThreadState;
static uint64_t os_pending_signals;
@@ -2106,233 +2128,246 @@ static void js_os_timer_mark(JSRuntime *rt, JSValueConst val,
typedef struct {
// linked list of all requests
struct list_head link;
- DynBuf response_data;
- // curl request headers, must be kept around during the request
- struct curl_slist *headers;
- // Response headers
- JSValue headers_list;
- JSValue resolve_func;
- JSValue reject_func;
- JSContext* ctx;
- uint8_t *readbuf;
- size_t readpos;
- size_t readlen;
- CURL *curl;
- BOOL client_has_accept_header;
- BOOL client_has_content_type_header;
-} CurlRequestContext;
-
-static size_t curl_header_callback(char *buffer, size_t size,
- size_t nitems, void *userdata)
-{
- CurlRequestContext *rctx = userdata;
- size_t sz = size * nitems;
-
- qjs_array_append_new(rctx->ctx, rctx->headers_list, JS_NewStringLen(rctx->ctx, buffer, sz));
- return sz;
-}
+ int request_id;
-static size_t curl_write_cb(void *data, size_t size, size_t nmemb, void *userp)
-{
- size_t realsize = size * nmemb;
- CurlRequestContext *rctx = userp;
+ JSValue resolve_func;
+ JSValue reject_func;
- if (0 != dbuf_put(&rctx->response_data, data, realsize)) {
- return 0;
- }
+ JSContext* ctx;
+} HttpRequestContext;
- return realsize;
-}
-size_t read_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
+void js_os_set_http_impl(JSRuntime *rt, struct JSHttpClientImplementation *impl)
{
- CurlRequestContext *rctx = userdata;
- ssize_t src_available = rctx->readlen - rctx->readpos;
- size_t dst_available = size * nmemb;
- size_t n;
+ JSThreadState *ts = JS_GetRuntimeOpaque(rt);
- if (!rctx->readbuf) {
- return 0;
- }
- if (src_available <= 0) {
- return 0;
- }
- n = src_available;
- if (dst_available < src_available) {
- n = dst_available;
- }
- memcpy(ptr, rctx->readbuf + rctx->readpos, n);
- rctx->readpos += n;
- return n;
+ ts->http_client_impl = impl;
}
int expect_property_str_bool(JSContext *ctx, JSValueConst this_val, const char *prop_name)
{
- JSValue prop_val;
- BOOL bool_val;
-
- prop_val = JS_GetPropertyStr(ctx, this_val, prop_name);
- if (JS_IsException(prop_val)) {
- return -1;
- }
- bool_val = JS_ToBool(ctx, prop_val);
- JS_FreeValue(ctx, prop_val);
- return bool_val;
-}
+ JSValue prop_val;
+ BOOL bool_val;
-BOOL starts_with_ignorecase(const char *str, const char *prefix)
-{
- for (int i = 0; i < strlen(prefix); i++) {
- if (!str[i]) {
- return FALSE;
- }
- if (tolower(str[i]) != tolower(prefix[i])) {
- return FALSE;
+ prop_val = JS_GetPropertyStr(ctx, this_val, prop_name);
+ if (JS_IsException(prop_val)) {
+ return -1;
}
- }
- return TRUE;
+ bool_val = JS_ToBool(ctx, prop_val);
+ JS_FreeValue(ctx, prop_val);
+ return bool_val;
}
-static int gather_headers(JSContext *ctx, JSValueConst js_headers, CurlRequestContext *req_context)
-{
- JSValue length_prop;
- uint32_t length;
-
- length_prop = JS_GetPropertyStr(ctx, js_headers, "length");
- if (JS_IsException(length_prop)) {
- return -1;
- }
- if (0 != JS_ToUint32(ctx, &length, length_prop)) {
- return -1;
- }
- JS_FreeValue(ctx, length_prop);
-
- for (uint32_t i = 0; i < length; i++) {
- JSValue item = JS_GetPropertyUint32(ctx, js_headers, i);
- if (JS_IsException(item)) {
- goto exception;
- }
- const char *cstr = JS_ToCString(ctx, item);
- if (starts_with_ignorecase(cstr, "accept:")) {
- req_context->client_has_accept_header = TRUE;
- } else if (starts_with_ignorecase(cstr, "content-type:")) {
- req_context->client_has_content_type_header = TRUE;
- }
- if (!cstr) {
- JS_FreeValue(ctx, item);
- goto exception;
- }
- req_context->headers = curl_slist_append (req_context->headers, cstr);
- JS_FreeCString(ctx, cstr);
- JS_FreeValue(ctx, item);
- }
- return 0;
-exception:
- return -1;
-}
-
-static void free_fetch_request_context(CurlRequestContext *req_context)
+static void free_http_request_context(HttpRequestContext *req_context)
{
JSContext *ctx;
+ JSThreadState *ts;
if (!req_context) {
return;
}
ctx = req_context->ctx;
+ ts = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
+ ts->http_client_impl->req_cancel(ts->http_client_impl->cls, req_context->request_id);
req_context->ctx = NULL;
- if (req_context->curl) {
- curl_easy_cleanup(req_context->curl);
- req_context->curl = NULL;
- }
- if (NULL != req_context->readbuf) {
- free(req_context->readbuf);
- }
- dbuf_free(&req_context->response_data);
- JS_FreeValue(ctx, req_context->headers_list);
JS_FreeValue(ctx, req_context->resolve_func);
JS_FreeValue(ctx, req_context->reject_func);
- curl_slist_free_all(req_context->headers);
if (NULL != req_context->link.prev) {
- list_del(&req_context->link);
+ list_del(&req_context->link);
}
js_free(ctx, req_context);
}
-static void
-finish_fetch_http(CurlRequestContext *req_context, CURLcode result)
+static void js_free_http_message(JSHttpMessage *msg)
{
+ if (msg->body) {
+ free(msg->body);
+ msg->body = NULL;
+ }
+ if (msg->errmsg) {
+ free(msg->errmsg);
+ msg->errmsg = NULL;
+ }
+ if (msg->response_headers) {
+ char **h;
+ for (h = msg->response_headers; *h; h++) {
+ free(*h);
+ }
+ free(msg->response_headers);
+ msg->response_headers = NULL;
+ }
+ free(msg);
+}
+
+static void handle_http_resp(void *cls, struct JSHttpResponseInfo *resp_info)
+{
+ // Called from a different thread.
+ // We must enqueue something that the message loop will process
+ //
+ HttpRequestContext *req_context = cls;
JSContext *ctx = req_context->ctx;
- long resp_code;
- JSValue ret_val;
- JSValue cb_ret;
-
- if (CURLE_OK != result) {
- const char *errmsg;
- JSAtom atom_message;
-
- atom_message = JS_NewAtom(ctx, "message");
- errmsg = curl_easy_strerror(result);
- ret_val = JS_NewError(ctx);
- JS_DefinePropertyValue(ctx, ret_val, atom_message,
- JS_NewString(ctx, errmsg),
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
- cb_ret = JS_Call(ctx, req_context->reject_func, JS_UNDEFINED, 1, &ret_val);
- JS_FreeAtom(ctx, atom_message);
- JS_FreeValue(ctx, cb_ret);
- JS_FreeValue(ctx, ret_val);
- free_fetch_request_context(req_context);
- return;
+ JSThreadState *ts = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
+ JSHttpMessage *msg;
+ JSHttpMessagePipe *hp;
+
+ msg = malloc(sizeof (*msg));
+ if (!msg) {
+ goto fail;
+ }
+ memset(msg, 0, sizeof (*msg));
+
+ msg->status = resp_info->status;
+ msg->request_id = resp_info->request_id;
+
+ if (resp_info->response_headers) {
+ int num_headers = 0;
+ char **h;
+
+ h = resp_info->response_headers;
+ while (*h) {
+ num_headers++;
+ h++;
+ }
+
+ msg->response_headers = malloc((num_headers + 1) * sizeof (char *));
+ if (!msg->response_headers) {
+ goto fail;
+ }
+ memset(msg->response_headers, 0, (num_headers + 1) * sizeof (char *));
+ for (int i = 0; i < num_headers; i++) {
+ msg->response_headers[i] = strdup(resp_info->response_headers[i]);
+ if (!msg->response_headers[i]) {
+ goto fail;
+ }
+ }
+ } else {
+ msg->response_headers = NULL;
}
- ret_val = JS_NewObject(ctx);
- if (JS_IsException(ret_val)) {
- // Huh, we're out of memory in the request handler?
- fprintf(stderr, "fatal: can't allocate object in finish_fetch_http\n");
+ if (resp_info->errmsg != NULL) {
+ msg->errmsg = strdup(resp_info->errmsg);
+ if (!msg->errmsg) {
+ goto fail;
+ }
+ }
+
+ if (resp_info->body_len > 0) {
+ msg->body = malloc(resp_info->body_len);
+ if (!msg->body) {
+ goto fail;
+ }
+ msg->body_len = resp_info->body_len;
+ memcpy(msg->body, resp_info->body, resp_info->body_len);
+ }
+
+ hp = ts->http_pipe;
+ pthread_mutex_lock(&hp->mutex);
+ /* indicate that data is present */
+ if (list_empty(&hp->msg_queue)) {
+ uint8_t ch = '\0';
+ int ret;
+ for(;;) {
+ ret = write(hp->write_fd, &ch, 1);
+ if (ret == 1)
+ break;
+ if (ret < 0 && (errno != EAGAIN || errno != EINTR))
+ break;
+ }
+ }
+ list_add_tail(&msg->link, &hp->msg_queue);
+ pthread_mutex_unlock(&hp->mutex);
+ return;
+ fail:
+ js_free_http_message(msg);
+ return;
+}
+
+static void
+free_http_headers(JSContext *ctx, char **headers)
+{
+ if (!headers) {
return;
}
+ for (char **h = headers; *h != NULL; h++) {
+ js_free(ctx, *h);
+ }
+ js_free(ctx, headers);
+}
+
+static char **gather_http_headers(JSContext *ctx, JSValueConst js_headers)
+{
+ JSValue length_prop;
+ uint32_t length;
+ char **headers = NULL;
+
+ length_prop = JS_GetPropertyStr(ctx, js_headers, "length");
+ if (JS_IsException(length_prop)) {
+ return NULL;
+ }
+ if (0 != JS_ToUint32(ctx, &length, length_prop)) {
+ return NULL;
+ }
+ JS_FreeValue(ctx, length_prop);
- curl_easy_getinfo(req_context->curl, CURLINFO_RESPONSE_CODE, &resp_code);
- JS_SetPropertyStr(ctx, ret_val, "status", JS_NewInt32(ctx, resp_code));
- JS_SetPropertyStr(ctx, ret_val, "headers", req_context->headers_list);
- req_context->headers_list = JS_UNINITIALIZED;
- // FIXME: Don't copy, own buffer
- JS_SetPropertyStr(ctx,
- ret_val,
- "data",
- JS_NewArrayBufferCopy(ctx,
- req_context->response_data.buf,
- req_context->response_data.size));
- cb_ret = JS_Call(ctx, req_context->resolve_func, JS_UNDEFINED, 1, &ret_val);
- JS_FreeValue(ctx, cb_ret);
- JS_FreeValue(ctx, ret_val);
- free_fetch_request_context(req_context);
+ headers = js_mallocz(ctx, (length + 1) * sizeof (char *));
+ if (!headers) {
+ goto exception;
+ }
+
+ for (uint32_t i = 0; i < length; i++) {
+ char *hval;
+ JSValue item = JS_GetPropertyUint32(ctx, js_headers, i);
+ if (JS_IsException(item)) {
+ goto exception;
+ }
+ const char *cstr = JS_ToCString(ctx, item);
+ if (!cstr) {
+ JS_FreeValue(ctx, item);
+ goto exception;
+ }
+ hval = js_strdup(ctx, cstr);
+ if (!hval) {
+ goto exception;
+ }
+ JS_FreeCString(ctx, cstr);
+ JS_FreeValue(ctx, item);
+ headers[i] = hval;
+ }
+ return headers;
+exception:
+ free_http_headers(ctx, headers);
+ return NULL;
}
+
/**
* fetchHttp(url, { method, headers, body }): Promise<Response>
*/
static JSValue js_os_fetchHttp(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
+ int argc, JSValueConst *argv)
{
+ JSValue ret_val = JS_UNINITIALIZED;
JSRuntime *rt = JS_GetRuntime(ctx);
JSThreadState *ts = JS_GetRuntimeOpaque(rt);
- const char *req_url = NULL;
- JSValue ret_val = JS_UNDEFINED;
JSValue resolving_funs[2];
JSValue options = JS_UNINITIALIZED;
JSValue method = JS_UNINITIALIZED;
const char *method_str = NULL;
- CURLMcode mres;
- CurlRequestContext *req_context = {0};
+ const char *req_url = NULL;
+ struct JSHttpRequestInfo req = { 0 };
+ HttpRequestContext *req_context = NULL;
BOOL debug = FALSE;
- BOOL req_started = FALSE;
+ int redirect = 0;
+ int ret;
- req_context = js_mallocz(ctx, sizeof *req_context);
+ if (NULL == ts->http_client_impl) {
+ JS_ThrowInternalError(ctx, "no HTTP client implementation available");
+ goto exception;
+ }
+ req_context = js_mallocz(ctx, sizeof *req_context);
req_context->ctx = ctx;
- req_context->headers_list = JS_NewArray(ctx);
- dbuf_init(&req_context->response_data);
req_url = JS_ToCString(ctx, argv[0]);
if (!req_url) {
@@ -2342,116 +2377,106 @@ static JSValue js_os_fetchHttp(JSContext *ctx, JSValueConst this_val,
options = argv[1];
if (JS_VALUE_GET_TAG(options) == JS_TAG_UNDEFINED) {
method = JS_NewString(ctx, "get");
- }
- else if (JS_VALUE_GET_TAG(options) == JS_TAG_OBJECT) {
+ } else if (JS_VALUE_GET_TAG(options) == JS_TAG_OBJECT) {
+ int has_prop_redirect;
+
method = JS_GetPropertyStr(ctx, options, "method");
debug = expect_property_str_bool(ctx, options, "debug");
+
+ has_prop_redirect = JS_HasPropertyStr(ctx, options, "redirect");
+ if (has_prop_redirect < 0) {
+ goto exception;
+ }
+ if (has_prop_redirect) {
+ int32_t redir_num;
+ JSValue redir_val = JS_GetPropertyStr(ctx, options, "redirect");
+ if (JS_IsException(redir_val)) {
+ goto exception;
+ }
+ if (JS_ToInt32(ctx, &redir_num, redir_val)) {
+ goto exception;
+ }
+ if (redir_num < 0 || redir_num > JS_HTTP_REDIRECT_ERROR) {
+ JS_ThrowTypeError(ctx, "redirect option out of range");
+ goto exception;
+ }
+ redirect = redir_num;
+ }
} else {
JS_ThrowTypeError(ctx, "invalid options");
goto exception;
}
- req_context->curl = curl_easy_init();
- if (!req_context->curl) {
- JS_ThrowInternalError(ctx, "unable to init libcurl");
- goto exception;
- }
- curl_easy_setopt(req_context->curl, CURLOPT_PRIVATE, req_context);
- curl_easy_setopt(req_context->curl, CURLOPT_SHARE, ts->curlsh);
- curl_easy_setopt(req_context->curl, CURLOPT_URL, req_url);
-// curl_easy_setopt(req_context->curl, CURLOPT_DNS_SERVERS, "8.8.8.8");
- curl_easy_setopt(req_context->curl, CURLOPT_DNS_SERVERS, "9.9.9.9");
- curl_easy_setopt(req_context->curl, CURLOPT_USERAGENT, "qtart");
- curl_easy_setopt(req_context->curl, CURLOPT_CAINFO, "/etc/ssl/certs/ca-certificates.crt");
- curl_easy_setopt(req_context->curl, CURLOPT_HEADERFUNCTION, curl_header_callback);
- curl_easy_setopt(req_context->curl, CURLOPT_HEADERDATA, req_context);
- curl_easy_setopt(req_context->curl, CURLOPT_WRITEFUNCTION, curl_write_cb);
- curl_easy_setopt(req_context->curl, CURLOPT_WRITEDATA, req_context);
- if (debug == TRUE) {
- curl_easy_setopt(req_context->curl, CURLOPT_VERBOSE, 1L);
- }
-
- // FIXME: This is only a temporary hack until we have proper TLS CA support
- // on all platforms
- curl_easy_setopt(req_context->curl, CURLOPT_SSL_VERIFYPEER, 0);
- curl_easy_setopt(req_context->curl, CURLOPT_SSL_VERIFYHOST, 0);
-
if (JS_VALUE_GET_TAG(options) == JS_TAG_OBJECT) {
JSValue header_item = JS_GetPropertyStr(ctx, options, "headers");
if (JS_IsException(header_item)) {
goto exception;
}
if (JS_VALUE_GET_TAG(header_item) == JS_TAG_OBJECT) {
- if (0 != gather_headers(ctx, header_item, req_context)) {
- JS_FreeValue(ctx, header_item);
- goto exception;
+ char **headers = gather_http_headers(ctx, header_item);
+ if (NULL == headers) {
+ JS_FreeValue(ctx, header_item);
+ goto exception;
}
+ req.request_headers = headers;
}
JS_FreeValue(ctx, header_item);
}
- method_str = JS_ToCString(ctx, method);
-
- if (0 == strcasecmp(method_str, "get")) {
- curl_easy_setopt(req_context->curl, CURLOPT_HTTPGET, 1L);
- if (!req_context->client_has_accept_header) {
- req_context->headers = curl_slist_append (req_context->headers, "Accept: application/json");
- }
- } else if (0 == strcasecmp(method_str, "delete")) {
- curl_easy_setopt(req_context->curl, CURLOPT_HTTPGET, 1L);
- curl_easy_setopt(req_context->curl, CURLOPT_CUSTOMREQUEST, "DELETE");
- } else if ((0 == strcasecmp(method_str, "post")) ||
- (0 == strcasecmp(method_str, "put"))) {
+ if (JS_VALUE_GET_TAG(options) == JS_TAG_OBJECT) {
JSValue data;
uint8_t *data_ptr;
size_t data_len;
+ int has_prop;
- data = JS_GetPropertyStr(ctx, options, "data");
- if (JS_IsException(data)) {
- goto exception;
- }
- data_ptr = JS_GetArrayBuffer(ctx, &data_len, data);
- if (!data_ptr) {
+ has_prop = JS_HasPropertyStr(ctx, options, "data");
+
+ if (-1 == has_prop) {
goto exception;
}
- req_context->readbuf = malloc(data_len);
- memcpy(req_context->readbuf, data_ptr, data_len);
- req_context->readlen = data_len;
- JS_FreeValue(ctx, data);
- curl_easy_setopt(req_context->curl, CURLOPT_POST, 1L);
- if (0 == strcasecmp(method_str, "put")) {
- curl_easy_setopt(req_context->curl, CURLOPT_CUSTOMREQUEST, "PUT");
- }
- curl_easy_setopt(req_context->curl, CURLOPT_READFUNCTION, read_callback);
- curl_easy_setopt(req_context->curl, CURLOPT_READDATA, req_context);
- if (!req_context->client_has_content_type_header) {
- req_context->headers = curl_slist_append(req_context->headers, "Content-Type: application/json");
+
+ if (has_prop) {
+ data = JS_GetPropertyStr(ctx, options, "data");
+ if (JS_IsException(data)) {
+ goto exception;
+ }
+ if (!JS_IsNull(data) && !JS_IsUndefined(data)) {
+ data_ptr = JS_GetArrayBuffer(ctx, &data_len, data);
+ if (!data_ptr) {
+ goto exception;
+ }
+ }
+ req.req_body = data_ptr;
+ req.req_body_len = data_len;
}
- } else {
- JS_ThrowTypeError(ctx, "invalid request method");
- goto exception;
}
- curl_easy_setopt(req_context->curl, CURLOPT_HTTPHEADER, req_context->headers);
+ method_str = JS_ToCString(ctx, method);
+
+ req.method = method_str;
+ req.url = req_url;
+ req.debug = debug;
+ req.redirect = redirect;
+ req.response_cb = &handle_http_resp;
+ req.response_cb_cls = req_context;
+ ret = ts->http_client_impl->req_create(ts->http_client_impl->cls, &req);
- mres = curl_multi_add_handle(ts->curlm, req_context->curl);
- if (CURLM_OK != mres) {
- JS_ThrowInternalError(ctx, "fetch failed: %s", curl_multi_strerror(mres));
+ if (ret < 0) {
+ JS_ThrowInternalError(ctx, "failed to create request");
goto exception;
}
- list_add_tail(&req_context->link, &ts->curl_requests);
- req_started = TRUE;
+
+ list_add_tail(&req_context->link, &ts->http_requests);
ret_val = JS_NewPromiseCapability(ctx, resolving_funs);
if (JS_IsException(ret_val)) {
goto done;
}
+ req_context->request_id = ret;
req_context->resolve_func = resolving_funs[0];
req_context->reject_func = resolving_funs[1];
done:
- if (FALSE == req_started) {
- free_fetch_request_context(req_context);
- }
+ free_http_headers(ctx, req.request_headers);
JS_FreeValue(ctx, method);
JS_FreeCString(ctx, req_url);
JS_FreeCString(ctx, method_str);
@@ -2459,6 +2484,7 @@ done:
exception:
ret_val = JS_EXCEPTION;
goto done;
+
}
#endif
@@ -2735,38 +2761,97 @@ static int handle_host_message(JSRuntime *rt, JSContext *ctx)
return ret;
}
-// Perform curl network requests and
-static void do_curl(JSContext *ctx)
+/* return 1 if a message was handled, 0 if no message */
+static int handle_http_message(JSRuntime *rt, JSContext *ctx)
{
#ifndef NO_HTTP
- JSRuntime *rt = JS_GetRuntime(ctx);
- JSThreadState *ts = JS_GetRuntimeOpaque(rt);
- CURLMcode curl_ret;
- struct CURLMsg *m;
- int running_handles;
+ JSThreadState *ts = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
+ JSHttpMessagePipe *hp = ts->http_pipe;
+ int ret;
+ struct list_head *el;
+ struct list_head *req_el;
+ JSHttpMessage *msg;
+ JSValue obj, func, retval;
+ HttpRequestContext *request_ctx;
+
+ pthread_mutex_lock(&hp->mutex);
+ if (!list_empty(&hp->msg_queue)) {
+ el = hp->msg_queue.next;
+ msg = list_entry(el, JSHttpMessage, link);
- curl_ret = curl_multi_perform(ts->curlm, &running_handles);
- if (0 != curl_ret) {
- fprintf(stderr, "curlm error: %s\n", curl_multi_strerror(curl_ret));
- return;
- }
+ /* remove the message from the queue */
+ list_del(&msg->link);
- do {
- int msgq = 0;
- m = curl_multi_info_read(ts->curlm, &msgq);
- if (m && (m->msg == CURLMSG_DONE)) {
- CurlRequestContext *req_ctx;
- CURL *e = m->easy_handle;
- CURLcode result = m->data.result;
- if (CURLE_OK != curl_easy_getinfo(e, CURLINFO_PRIVATE, &req_ctx)) {
- fprintf(stderr, "fatal: curl handle has no private data");
- continue;
+ if (list_empty(&hp->msg_queue)) {
+ uint8_t buf[16];
+ int ret;
+ for(;;) {
+ ret = read(hp->read_fd, buf, sizeof(buf));
+ if (ret >= 0)
+ break;
+ if (errno != EAGAIN && errno != EINTR)
+ break;
}
- curl_multi_remove_handle(ts->curlm, e);
- finish_fetch_http(req_ctx, result);
}
- } while (m);
-#endif
+
+ pthread_mutex_unlock(&hp->mutex);
+
+ list_for_each(req_el, &ts->http_requests) {
+ request_ctx = list_entry(req_el, HttpRequestContext, link);
+ if (request_ctx->request_id == msg->request_id) {
+ if (msg->status != 0) {
+ JSValue headers_list = JS_NewArray(ctx);
+
+ obj = JS_NewObject(ctx);
+
+ if (msg->response_headers) {
+ char **h = msg->response_headers;
+ while (*h) {
+ qjs_array_append_new(ctx, headers_list, JS_NewString(ctx, *h));
+ h++;
+ }
+ }
+ JS_SetPropertyStr(ctx, obj, "headers", headers_list);
+
+ //JS_SetPropertyStr(ctx, obj, "data", JS_NewTypedArray(ctx, JS_NewArrayBufferCopy(ctx, msg->body, msg->body_len), 1));
+ JS_SetPropertyStr(ctx, obj, "data", JS_NewArrayBufferCopy(ctx, msg->body, msg->body_len));
+
+ JS_SetPropertyStr(ctx, obj, "status", JS_NewInt32(ctx, msg->status));
+ func = JS_DupValue(ctx, request_ctx->resolve_func);
+ retval = JS_Call(ctx, func, JS_UNDEFINED, 1, (JSValueConst *)&obj);
+ JS_FreeValue(ctx, obj);
+ JS_FreeValue(ctx, func);
+ if (JS_IsException(retval)) {
+ js_std_dump_error(ctx);
+ } else {
+ JS_FreeValue(ctx, retval);
+ }
+ } else {
+ JSAtom atom_message;
+
+ atom_message = JS_NewAtom(ctx, "message");
+ obj = JS_NewError(ctx);
+ JS_DefinePropertyValue(ctx, obj, atom_message,
+ JS_NewString(ctx, msg->errmsg),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ retval = JS_Call(ctx, request_ctx->reject_func, JS_UNDEFINED, 1, &obj);
+ JS_FreeAtom(ctx, atom_message);
+ JS_FreeValue(ctx, retval);
+ }
+ break;
+ }
+ }
+
+ js_free_http_message(msg);
+ ret = 1;
+ } else {
+ pthread_mutex_unlock(&hp->mutex);
+ ret = 0;
+ }
+ return ret;
+#else
+ return 0;
+#endif /* NO_HTTP */
}
static int js_os_poll(JSContext *ctx)
@@ -2799,7 +2884,7 @@ static int js_os_poll(JSContext *ctx)
}
#ifndef NO_HTTP
- have_http_requests = !list_empty(&ts->curl_requests);
+ have_http_requests = !list_empty(&ts->http_requests);
#endif
if ((!have_http_requests) && list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers) &&
@@ -2860,36 +2945,8 @@ static int js_os_poll(JSContext *ctx)
fd_max = max_int(fd_max, ts->host_pipe->read_fd);
FD_SET(ts->host_pipe->read_fd, &rfds);
-#ifndef NO_HTTP
- {
- int curl_fd_max = -1;
- CURLMcode mret;
- long timeo = -1;
-
- mret = curl_multi_fdset(ts->curlm, &rfds, &wfds, &ecxfds, &curl_fd_max);
- if (CURLM_OK != mret) {
- fprintf(stderr, "curlm error: %s\n", curl_multi_strerror(mret));
- } else if (curl_fd_max != -1) {
- fd_max = max_int(fd_max, curl_fd_max);
- }
-
- curl_multi_timeout(ts->curlm, &timeo);
- if (timeo > 0) {
- long timeo_sec = timeo / 1000;
- long timeo_usec = (timeo % 1000) * 1000;
- tvp = &tv;
- if (tv.tv_sec < timeo_sec) {
- tv.tv_sec = timeo_sec;
- tv.tv_usec = timeo_usec;
- } else if ((tv.tv_sec == timeo_sec) && tv.tv_usec < timeo_usec) {
- tv.tv_usec = timeo_usec;
- }
- } else if (timeo == 0) {
- do_curl(ctx);
- goto done;
- }
- }
-#endif
+ fd_max = max_int(fd_max, ts->http_pipe->read_fd);
+ FD_SET(ts->http_pipe->read_fd, &rfds);
ret = select(fd_max + 1, &rfds, &wfds, NULL, tvp);
if (ret > 0) {
@@ -2926,7 +2983,11 @@ static int js_os_poll(JSContext *ctx)
}
}
- do_curl(ctx);
+ if (FD_ISSET(ts->http_pipe->read_fd, &rfds)) {
+ if (handle_http_message(rt, ctx)) {
+ goto done;
+ }
+ }
}
done:
return 0;
@@ -3811,6 +3872,27 @@ static JSHostMessagePipe *js_new_host_message_pipe(void)
return ps;
}
+static JSHttpMessagePipe *js_new_http_message_pipe(void)
+{
+ JSHttpMessagePipe *ps;
+ int pipe_fds[2];
+
+ if (pipe(pipe_fds) < 0)
+ return NULL;
+
+ ps = malloc(sizeof(*ps));
+ if (!ps) {
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ return NULL;
+ }
+ init_list_head(&ps->msg_queue);
+ pthread_mutex_init(&ps->mutex, NULL);
+ ps->read_fd = pipe_fds[0];
+ ps->write_fd = pipe_fds[1];
+ return ps;
+}
+
static JSWorkerMessagePipe *js_dup_message_pipe(JSWorkerMessagePipe *ps)
{
atomic_add_int(&ps->ref_count, 1);
@@ -3880,6 +3962,32 @@ static void js_free_host_message_pipe(JSHostMessagePipe *ps)
}
}
+#ifndef NO_HTTP
+
+static void js_free_http_message_pipe(JSHttpMessagePipe *ps)
+{
+ struct list_head *el, *el1;
+ JSHttpMessage *msg;
+ int ref_count;
+
+ if (!ps)
+ return;
+
+ assert(ref_count >= 0);
+ if (ref_count == 0) {
+ list_for_each_safe(el, el1, &ps->msg_queue) {
+ msg = list_entry(el, JSHttpMessage, link);
+ js_free_http_message(msg);
+ }
+ pthread_mutex_destroy(&ps->mutex);
+ close(ps->read_fd);
+ close(ps->write_fd);
+ free(ps);
+ }
+}
+
+#endif
+
static void js_free_port(JSRuntime *rt, JSWorkerMessageHandler *port)
{
if (port) {
@@ -4416,13 +4524,6 @@ static int js_os_init(JSContext *ctx, JSModuleDef *m)
JS_NewClassID(&js_os_timer_class_id);
JS_NewClass(JS_GetRuntime(ctx), js_os_timer_class_id, &js_os_timer_class);
-#ifndef NO_HTTP
- if (CURLE_OK != curl_global_init (CURL_GLOBAL_DEFAULT)) {
- JS_ThrowInternalError(ctx, "unable to init libcurl (global)");
- return -1;
- }
-#endif
-
#ifdef USE_WORKER
{
JSRuntime *rt = JS_GetRuntime(ctx);
@@ -4537,6 +4638,7 @@ oom_fail:
init_list_head(&ts->port_list);
ts->on_host_message_func = JS_NULL;
ts->host_pipe = js_new_host_message_pipe();
+ ts->http_pipe = js_new_http_message_pipe();
if (!ts->host_pipe) {
goto oom_fail;
}
@@ -4544,12 +4646,7 @@ oom_fail:
JS_SetRuntimeOpaque(rt, ts);
#ifndef NO_HTTP
- ts->curlm = curl_multi_init();
- ts->curlsh = curl_share_init();
- curl_share_setopt(ts->curlsh, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
- curl_share_setopt(ts->curlsh, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
- curl_share_setopt(ts->curlsh, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
- init_list_head(&ts->curl_requests);
+ init_list_head(&ts->http_requests);
#endif
#ifdef USE_WORKER
@@ -4588,16 +4685,10 @@ void js_std_free_handlers(JSRuntime *rt)
}
#ifndef NO_HTTP
-
- list_for_each_safe(el, el1, &ts->curl_requests) {
- CurlRequestContext *request_ctx = list_entry(el, CurlRequestContext, link);
- free_fetch_request_context(request_ctx);
+ list_for_each_safe(el, el1, &ts->http_requests) {
+ HttpRequestContext *request_ctx = list_entry(el, HttpRequestContext, link);
+ free_http_request_context(request_ctx);
}
-
- curl_multi_cleanup(ts->curlm);
- ts->curlm = NULL;
- curl_share_cleanup(ts->curlsh);
- ts->curlsh = NULL;
#endif
JS_FreeValueRT(rt, ts->on_host_message_func);
@@ -4610,6 +4701,10 @@ void js_std_free_handlers(JSRuntime *rt)
js_free_host_message_pipe(ts->host_pipe);
+#ifndef NO_HTTP
+ js_free_http_message_pipe(ts->http_pipe);
+#endif
+
free(ts);
JS_SetRuntimeOpaque(rt, NULL); /* fail safe */
}