diff options
Diffstat (limited to 'src/couch/priv/couch_js/1.8.5/http.c')
-rw-r--r-- | src/couch/priv/couch_js/1.8.5/http.c | 701 |
1 files changed, 0 insertions, 701 deletions
diff --git a/src/couch/priv/couch_js/1.8.5/http.c b/src/couch/priv/couch_js/1.8.5/http.c deleted file mode 100644 index c4b389659..000000000 --- a/src/couch/priv/couch_js/1.8.5/http.c +++ /dev/null @@ -1,701 +0,0 @@ -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy of -// the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <jsapi.h> -#include "config.h" -#include "utf8.h" -#include "util.h" - -// Soft dependency on cURL bindings because they're -// only used when running the JS tests from the -// command line which is rare. -#ifndef HAVE_CURL - -void -http_check_enabled() -{ - fprintf(stderr, "HTTP API was disabled at compile time.\n"); - exit(3); -} - - -JSBool -http_ctor(JSContext* cx, JSObject* req) -{ - return JS_FALSE; -} - - -JSBool -http_dtor(JSContext* cx, JSObject* req) -{ - return JS_FALSE; -} - - -JSBool -http_open(JSContext* cx, JSObject* req, jsval mth, jsval url, jsval snc) -{ - return JS_FALSE; -} - - -JSBool -http_set_hdr(JSContext* cx, JSObject* req, jsval name, jsval val) -{ - return JS_FALSE; -} - - -JSBool -http_send(JSContext* cx, JSObject* req, jsval body) -{ - return JS_FALSE; -} - - -int -http_status(JSContext* cx, JSObject* req, jsval body) -{ - return -1; -} - -JSBool -http_uri(JSContext* cx, JSObject* req, couch_args* args, jsval* uri_val) -{ - return JS_FALSE; -} - - -#else -#include <curl/curl.h> -#ifndef XP_WIN -#include <unistd.h> -#endif - - -void -http_check_enabled() -{ - return; -} - - -// Map some of the string function names to things which exist on Windows -#ifdef XP_WIN -#define strcasecmp _strcmpi -#define strncasecmp _strnicmp -#define snprintf _snprintf -#endif - - -typedef struct curl_slist CurlHeaders; - - -typedef struct { - int method; - char* url; - CurlHeaders* req_headers; - jsint last_status; -} HTTPData; - - -char* METHODS[] = {"GET", "HEAD", "POST", "PUT", "DELETE", "COPY", "OPTIONS", NULL}; - - -#define GET 0 -#define HEAD 1 -#define POST 2 -#define PUT 3 -#define DELETE 4 -#define COPY 5 -#define OPTIONS 6 - - -static JSBool -go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t blen); - - -static JSString* -str_from_binary(JSContext* cx, char* data, size_t length); - - -JSBool -http_ctor(JSContext* cx, JSObject* req) -{ - HTTPData* http = NULL; - JSBool ret = JS_FALSE; - - http = (HTTPData*) malloc(sizeof(HTTPData)); - if(!http) - { - JS_ReportError(cx, "Failed to create CouchHTTP instance."); - goto error; - } - - http->method = -1; - http->url = NULL; - http->req_headers = NULL; - http->last_status = -1; - - if(!JS_SetPrivate(cx, req, http)) - { - JS_ReportError(cx, "Failed to set private CouchHTTP data."); - goto error; - } - - ret = JS_TRUE; - goto success; - -error: - if(http) free(http); - -success: - return ret; -} - - -void -http_dtor(JSContext* cx, JSObject* obj) -{ - HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj); - if(http) { - if(http->url) free(http->url); - if(http->req_headers) curl_slist_free_all(http->req_headers); - free(http); - } -} - - -JSBool -http_open(JSContext* cx, JSObject* req, jsval mth, jsval url, jsval snc) -{ - HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req); - char* method = NULL; - int methid; - JSBool ret = JS_FALSE; - - if(!http) { - JS_ReportError(cx, "Invalid CouchHTTP instance."); - goto done; - } - - if(JSVAL_IS_VOID(mth)) { - JS_ReportError(cx, "You must specify a method."); - goto done; - } - - method = enc_string(cx, mth, NULL); - if(!method) { - JS_ReportError(cx, "Failed to encode method."); - goto done; - } - - for(methid = 0; METHODS[methid] != NULL; methid++) { - if(strcasecmp(METHODS[methid], method) == 0) break; - } - - if(methid > OPTIONS) { - JS_ReportError(cx, "Invalid method specified."); - goto done; - } - - http->method = methid; - - if(JSVAL_IS_VOID(url)) { - JS_ReportError(cx, "You must specify a URL."); - goto done; - } - - if(http->url != NULL) { - free(http->url); - http->url = NULL; - } - - http->url = enc_string(cx, url, NULL); - if(http->url == NULL) { - JS_ReportError(cx, "Failed to encode URL."); - goto done; - } - - if(JSVAL_IS_BOOLEAN(snc) && JSVAL_TO_BOOLEAN(snc)) { - JS_ReportError(cx, "Synchronous flag must be false."); - goto done; - } - - if(http->req_headers) { - curl_slist_free_all(http->req_headers); - http->req_headers = NULL; - } - - // Disable Expect: 100-continue - http->req_headers = curl_slist_append(http->req_headers, "Expect:"); - - ret = JS_TRUE; - -done: - if(method) free(method); - return ret; -} - - -JSBool -http_set_hdr(JSContext* cx, JSObject* req, jsval name, jsval val) -{ - HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req); - char* keystr = NULL; - char* valstr = NULL; - char* hdrbuf = NULL; - size_t hdrlen = -1; - JSBool ret = JS_FALSE; - - if(!http) { - JS_ReportError(cx, "Invalid CouchHTTP instance."); - goto done; - } - - if(JSVAL_IS_VOID(name)) - { - JS_ReportError(cx, "You must speciy a header name."); - goto done; - } - - keystr = enc_string(cx, name, NULL); - if(!keystr) - { - JS_ReportError(cx, "Failed to encode header name."); - goto done; - } - - if(JSVAL_IS_VOID(val)) - { - JS_ReportError(cx, "You must specify a header value."); - goto done; - } - - valstr = enc_string(cx, val, NULL); - if(!valstr) - { - JS_ReportError(cx, "Failed to encode header value."); - goto done; - } - - hdrlen = strlen(keystr) + strlen(valstr) + 3; - hdrbuf = (char*) malloc(hdrlen * sizeof(char)); - if(!hdrbuf) { - JS_ReportError(cx, "Failed to allocate header buffer."); - goto done; - } - - snprintf(hdrbuf, hdrlen, "%s: %s", keystr, valstr); - http->req_headers = curl_slist_append(http->req_headers, hdrbuf); - - ret = JS_TRUE; - -done: - if(keystr) free(keystr); - if(valstr) free(valstr); - if(hdrbuf) free(hdrbuf); - return ret; -} - -JSBool -http_send(JSContext* cx, JSObject* req, jsval body) -{ - HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req); - char* bodystr = NULL; - size_t bodylen = 0; - JSBool ret = JS_FALSE; - - if(!http) { - JS_ReportError(cx, "Invalid CouchHTTP instance."); - goto done; - } - - if(!JSVAL_IS_VOID(body)) { - bodystr = enc_string(cx, body, &bodylen); - if(!bodystr) { - JS_ReportError(cx, "Failed to encode body."); - goto done; - } - } - - ret = go(cx, req, http, bodystr, bodylen); - -done: - if(bodystr) free(bodystr); - return ret; -} - -int -http_status(JSContext* cx, JSObject* req) -{ - HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req); - - if(!http) { - JS_ReportError(cx, "Invalid CouchHTTP instance."); - return JS_FALSE; - } - - return http->last_status; -} - -JSBool -http_uri(JSContext* cx, JSObject* req, couch_args* args, jsval* uri_val) -{ - FILE* uri_fp = NULL; - JSString* uri_str; - - // Default is http://localhost:15986/ when no uri file is specified - if (!args->uri_file) { - uri_str = JS_InternString(cx, "http://localhost:15986/"); - *uri_val = STRING_TO_JSVAL(uri_str); - JS_SetReservedSlot(cx, req, 0, *uri_val); - return JS_TRUE; - } - - // Else check to see if the base url is cached in a reserved slot - if (JS_GetReservedSlot(cx, req, 0, uri_val) && !JSVAL_IS_VOID(*uri_val)) { - return JS_TRUE; - } - - // Read the first line of the couch.uri file. - if(!((uri_fp = fopen(args->uri_file, "r")) && - (uri_str = couch_readline(cx, uri_fp)))) { - JS_ReportError(cx, "Failed to read couch.uri file."); - goto error; - } - - fclose(uri_fp); - *uri_val = STRING_TO_JSVAL(uri_str); - JS_SetReservedSlot(cx, req, 0, *uri_val); - return JS_TRUE; - -error: - if(uri_fp) fclose(uri_fp); - return JS_FALSE; -} - - -// Curl Helpers - -typedef struct { - HTTPData* http; - JSContext* cx; - JSObject* resp_headers; - char* sendbuf; - size_t sendlen; - size_t sent; - int sent_once; - char* recvbuf; - size_t recvlen; - size_t read; -} CurlState; - -/* - * I really hate doing this but this doesn't have to be - * uber awesome, it just has to work. - */ -CURL* HTTP_HANDLE = NULL; -char ERRBUF[CURL_ERROR_SIZE]; - -static size_t send_body(void *ptr, size_t size, size_t nmem, void *data); -static int seek_body(void *ptr, curl_off_t offset, int origin); -static size_t recv_body(void *ptr, size_t size, size_t nmem, void *data); -static size_t recv_header(void *ptr, size_t size, size_t nmem, void *data); - -static JSBool -go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen) -{ - CurlState state; - char* referer; - JSString* jsbody; - JSBool ret = JS_FALSE; - jsval tmp; - - state.cx = cx; - state.http = http; - - state.sendbuf = body; - state.sendlen = bodylen; - state.sent = 0; - state.sent_once = 0; - - state.recvbuf = NULL; - state.recvlen = 0; - state.read = 0; - - if(HTTP_HANDLE == NULL) { - HTTP_HANDLE = curl_easy_init(); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_READFUNCTION, send_body); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_SEEKFUNCTION, - (curl_seek_callback) seek_body); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_HEADERFUNCTION, recv_header); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEFUNCTION, recv_body); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_NOPROGRESS, 1); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_ERRORBUFFER, ERRBUF); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_COOKIEFILE, ""); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_USERAGENT, - "CouchHTTP Client - Relax"); - } - - if(!HTTP_HANDLE) { - JS_ReportError(cx, "Failed to initialize cURL handle."); - goto done; - } - - if(!JS_GetReservedSlot(cx, obj, 0, &tmp)) { - JS_ReportError(cx, "Failed to readreserved slot."); - goto done; - } - - if(!(referer = enc_string(cx, tmp, NULL))) { - JS_ReportError(cx, "Failed to encode referer."); - goto done; - } - curl_easy_setopt(HTTP_HANDLE, CURLOPT_REFERER, referer); - free(referer); - - if(http->method < 0 || http->method > OPTIONS) { - JS_ReportError(cx, "INTERNAL: Unknown method."); - goto done; - } - - curl_easy_setopt(HTTP_HANDLE, CURLOPT_CUSTOMREQUEST, METHODS[http->method]); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_NOBODY, 0); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_UPLOAD, 0); - - if(http->method == HEAD) { - curl_easy_setopt(HTTP_HANDLE, CURLOPT_NOBODY, 1); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 0); - } else if(http->method == POST || http->method == PUT) { - curl_easy_setopt(HTTP_HANDLE, CURLOPT_UPLOAD, 1); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 0); - } - - if(body && bodylen) { - curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, bodylen); - } else { - curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, 0); - } - - // curl_easy_setopt(HTTP_HANDLE, CURLOPT_VERBOSE, 1); - - curl_easy_setopt(HTTP_HANDLE, CURLOPT_URL, http->url); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_HTTPHEADER, http->req_headers); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_READDATA, &state); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_SEEKDATA, &state); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEHEADER, &state); - curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEDATA, &state); - - if(curl_easy_perform(HTTP_HANDLE) != 0) { - JS_ReportError(cx, "Failed to execute HTTP request: %s", ERRBUF); - goto done; - } - - if(!state.resp_headers) { - JS_ReportError(cx, "Failed to recieve HTTP headers."); - goto done; - } - - tmp = OBJECT_TO_JSVAL(state.resp_headers); - if(!JS_DefineProperty( - cx, obj, - "_headers", - tmp, - NULL, NULL, - JSPROP_READONLY - )) { - JS_ReportError(cx, "INTERNAL: Failed to set response headers."); - goto done; - } - - if(state.recvbuf) { - state.recvbuf[state.read] = '\0'; - jsbody = dec_string(cx, state.recvbuf, state.read+1); - if(!jsbody) { - // If we can't decode the body as UTF-8 we forcefully - // convert it to a string by just forcing each byte - // to a jschar. - jsbody = str_from_binary(cx, state.recvbuf, state.read); - if(!jsbody) { - if(!JS_IsExceptionPending(cx)) { - JS_ReportError(cx, "INTERNAL: Failed to decode body."); - } - goto done; - } - } - tmp = STRING_TO_JSVAL(jsbody); - } else { - tmp = JS_GetEmptyStringValue(cx); - } - - if(!JS_DefineProperty( - cx, obj, - "responseText", - tmp, - NULL, NULL, - JSPROP_READONLY - )) { - JS_ReportError(cx, "INTERNAL: Failed to set responseText."); - goto done; - } - - ret = JS_TRUE; - -done: - if(state.recvbuf) JS_free(cx, state.recvbuf); - return ret; -} - -static size_t -send_body(void *ptr, size_t size, size_t nmem, void *data) -{ - CurlState* state = (CurlState*) data; - size_t length = size * nmem; - size_t towrite = state->sendlen - state->sent; - - // Assume this is cURL trying to resend a request that - // failed. - if(towrite == 0 && state->sent_once == 0) { - state->sent_once = 1; - return 0; - } else if(towrite == 0) { - state->sent = 0; - state->sent_once = 0; - towrite = state->sendlen; - } - - if(length < towrite) towrite = length; - - memcpy(ptr, state->sendbuf + state->sent, towrite); - state->sent += towrite; - - return towrite; -} - -static int -seek_body(void* ptr, curl_off_t offset, int origin) -{ - CurlState* state = (CurlState*) ptr; - if(origin != SEEK_SET) return -1; - - state->sent = (size_t) offset; - return (int) state->sent; -} - -static size_t -recv_header(void *ptr, size_t size, size_t nmem, void *data) -{ - CurlState* state = (CurlState*) data; - char code[4]; - char* header = (char*) ptr; - size_t length = size * nmem; - JSString* hdr = NULL; - jsuint hdrlen; - jsval hdrval; - - if(length > 7 && strncasecmp(header, "HTTP/1.", 7) == 0) { - if(length < 12) { - return CURLE_WRITE_ERROR; - } - - memcpy(code, header+9, 3*sizeof(char)); - code[3] = '\0'; - state->http->last_status = atoi(code); - - state->resp_headers = JS_NewArrayObject(state->cx, 0, NULL); - if(!state->resp_headers) { - return CURLE_WRITE_ERROR; - } - - return length; - } - - // We get a notice at the \r\n\r\n after headers. - if(length <= 2) { - return length; - } - - // Append the new header to our array. - hdr = dec_string(state->cx, header, length); - if(!hdr) { - return CURLE_WRITE_ERROR; - } - - if(!JS_GetArrayLength(state->cx, state->resp_headers, &hdrlen)) { - return CURLE_WRITE_ERROR; - } - - hdrval = STRING_TO_JSVAL(hdr); - if(!JS_SetElement(state->cx, state->resp_headers, hdrlen, &hdrval)) { - return CURLE_WRITE_ERROR; - } - - return length; -} - -static size_t -recv_body(void *ptr, size_t size, size_t nmem, void *data) -{ - CurlState* state = (CurlState*) data; - size_t length = size * nmem; - char* tmp = NULL; - - if(!state->recvbuf) { - state->recvlen = 4096; - state->read = 0; - state->recvbuf = JS_malloc(state->cx, state->recvlen); - } - - if(!state->recvbuf) { - return CURLE_WRITE_ERROR; - } - - // +1 so we can add '\0' back up in the go function. - while(length+1 > state->recvlen - state->read) state->recvlen *= 2; - tmp = JS_realloc(state->cx, state->recvbuf, state->recvlen); - if(!tmp) return CURLE_WRITE_ERROR; - state->recvbuf = tmp; - - memcpy(state->recvbuf + state->read, ptr, length); - state->read += length; - return length; -} - -JSString* -str_from_binary(JSContext* cx, char* data, size_t length) -{ - jschar* conv = (jschar*) JS_malloc(cx, length * sizeof(jschar)); - JSString* ret = NULL; - size_t i; - - if(!conv) return NULL; - - for(i = 0; i < length; i++) { - conv[i] = (jschar) data[i]; - } - - ret = JS_NewUCString(cx, conv, length); - if(!ret) JS_free(cx, conv); - - return ret; -} - -#endif /* HAVE_CURL */ |