diff options
Diffstat (limited to 'quickjs/quickjs-libc.c')
-rw-r--r-- | quickjs/quickjs-libc.c | 753 |
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 */ } |