summaryrefslogtreecommitdiff
path: root/src/couch/priv/couch_js/60/http.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/couch/priv/couch_js/60/http.cpp')
-rw-r--r--src/couch/priv/couch_js/60/http.cpp649
1 files changed, 0 insertions, 649 deletions
diff --git a/src/couch/priv/couch_js/60/http.cpp b/src/couch/priv/couch_js/60/http.cpp
deleted file mode 100644
index e1e44d622..000000000
--- a/src/couch/priv/couch_js/60/http.cpp
+++ /dev/null
@@ -1,649 +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 <js/Initialization.h>
-#include "config.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);
-}
-
-
-bool
-http_ctor(JSContext* cx, JSObject* req)
-{
- return false;
-}
-
-
-void
-http_dtor(JSFreeOp* fop, JSObject* req)
-{
- return;
-}
-
-
-bool
-http_open(JSContext* cx, JSObject* req, JS::Value mth, JS::Value url, JS::Value snc)
-{
- return false;
-}
-
-
-bool
-http_set_hdr(JSContext* cx, JSObject* req, JS::Value name, JS::Value val)
-{
- return false;
-}
-
-
-bool
-http_send(JSContext* cx, JSObject* req, JS::Value body)
-{
- return false;
-}
-
-
-int
-http_status(JSContext* cx, JSObject* req)
-{
- return -1;
-}
-
-bool
-http_uri(JSContext* cx, JSObject* req, couch_args* args, JS::Value* uri_val)
-{
- return 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
-#endif
-
-
-typedef struct curl_slist CurlHeaders;
-
-
-typedef struct {
- int method;
- std::string url;
- CurlHeaders* req_headers;
- int16_t last_status;
-} HTTPData;
-
-
-const 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 bool go(JSContext* cx, JSObject* obj, HTTPData* http, std::string& body);
-
-
-bool
-http_ctor(JSContext* cx, JSObject* req)
-{
- HTTPData* http = new HTTPData();
- bool ret = false;
-
- if(!http)
- {
- JS_ReportErrorUTF8(cx, "Failed to create CouchHTTP instance.");
- goto error;
- }
-
- http->method = -1;
- http->req_headers = NULL;
- http->last_status = -1;
-
- JS_SetPrivate(req, http);
-
- ret = true;
- goto success;
-
-error:
- if(http) delete http;
-
-success:
- return ret;
-}
-
-
-void
-http_dtor(JSFreeOp* fop, JSObject* obj)
-{
- HTTPData* http = (HTTPData*) JS_GetPrivate(obj);
- if(http) {
- if(http->req_headers) curl_slist_free_all(http->req_headers);
- delete http;
- }
-}
-
-
-bool
-http_open(JSContext* cx, JSObject* req, JS::Value mth, JS::Value url, JS::Value snc)
-{
- HTTPData* http = (HTTPData*) JS_GetPrivate(req);
- int methid;
-
- if(!http) {
- JS_ReportErrorUTF8(cx, "Invalid CouchHTTP instance.");
- return false;
- }
-
- if(!mth.isString()) {
- JS_ReportErrorUTF8(cx, "Method must be a string.");
- return false;
- }
-
- std::string method;
- if(!js_to_string(cx, JS::RootedValue(cx, mth), method)) {
- JS_ReportErrorUTF8(cx, "Failed to encode method.");
- return false;
- }
-
- for(methid = 0; METHODS[methid] != NULL; methid++) {
- if(strcasecmp(METHODS[methid], method.c_str()) == 0) break;
- }
-
- if(methid > OPTIONS) {
- JS_ReportErrorUTF8(cx, "Invalid method specified.");
- return false;
- }
-
- http->method = methid;
-
- if(!url.isString()) {
- JS_ReportErrorUTF8(cx, "URL must be a string");
- return false;
- }
-
- std::string urlstr;
- if(!js_to_string(cx, JS::RootedValue(cx, url), urlstr)) {
- JS_ReportErrorUTF8(cx, "Failed to encode URL.");
- return false;
- }
- http->url = urlstr;
-
- if(snc.isBoolean() && snc.isTrue()) {
- JS_ReportErrorUTF8(cx, "Synchronous flag must be false.");
- return false;
- }
-
- 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:");
-
- return true;
-}
-
-
-bool
-http_set_hdr(JSContext* cx, JSObject* req, JS::Value name, JS::Value val)
-{
- HTTPData* http = (HTTPData*) JS_GetPrivate(req);
-
- if(!http) {
- JS_ReportErrorUTF8(cx, "Invalid CouchHTTP instance.");
- return false;
- }
-
- if(!name.isString())
- {
- JS_ReportErrorUTF8(cx, "Header names must be strings.");
- return false;
- }
-
- std::string keystr;
- if(!js_to_string(cx, JS::RootedValue(cx, name), keystr))
- {
- JS_ReportErrorUTF8(cx, "Failed to encode header name.");
- return false;
- }
-
- if(!val.isString())
- {
- JS_ReportErrorUTF8(cx, "Header values must be strings.");
- return false;
- }
-
- std::string valstr;
- if(!js_to_string(cx, JS::RootedValue(cx, val), valstr)) {
- JS_ReportErrorUTF8(cx, "Failed to encode header value.");
- return false;
- }
-
- std::string header = keystr + ": " + valstr;
- http->req_headers = curl_slist_append(http->req_headers, header.c_str());
-
- return true;
-}
-
-bool
-http_send(JSContext* cx, JSObject* req, JS::Value body)
-{
- HTTPData* http = (HTTPData*) JS_GetPrivate(req);
-
- if(!http) {
- JS_ReportErrorUTF8(cx, "Invalid CouchHTTP instance.");
- return false;
- }
-
- std::string bodystr;
- if(!js_to_string(cx, JS::RootedValue(cx, body), bodystr)) {
- JS_ReportErrorUTF8(cx, "Failed to encode body.");
- return false;
- }
-
- return go(cx, req, http, bodystr);
-}
-
-int
-http_status(JSContext* cx, JSObject* req)
-{
- HTTPData* http = (HTTPData*) JS_GetPrivate(req);
-
- if(!http) {
- JS_ReportErrorUTF8(cx, "Invalid CouchHTTP instance.");
- return false;
- }
-
- return http->last_status;
-}
-
-bool
-http_uri(JSContext* cx, JSObject* req, couch_args* args, JS::Value* 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_NewStringCopyZ(cx, "http://localhost:15986/");
- *uri_val = JS::StringValue(uri_str);
- JS_SetReservedSlot(req, 0, *uri_val);
- return true;
- }
-
- // Else check to see if the base url is cached in a reserved slot
- *uri_val = JS_GetReservedSlot(req, 0);
- if (!(*uri_val).isUndefined()) {
- return 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_ReportErrorUTF8(cx, "Failed to read couch.uri file.");
- goto error;
- }
-
- fclose(uri_fp);
- *uri_val = JS::StringValue(uri_str);
- JS_SetReservedSlot(req, 0, *uri_val);
- return true;
-
-error:
- if(uri_fp) fclose(uri_fp);
- return false;
-}
-
-
-// Curl Helpers
-
-typedef struct {
- HTTPData* http;
- JSContext* cx;
- JSObject* resp_headers;
- const 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 bool
-go(JSContext* cx, JSObject* obj, HTTPData* http, std::string& body)
-{
- CurlState state;
- JSString* jsbody;
- bool ret = false;
- JS::Value tmp;
- JS::RootedObject robj(cx, obj);
- JS::RootedValue vobj(cx);
-
-
- state.cx = cx;
- state.http = http;
-
- state.sendbuf = body.c_str();
- state.sendlen = body.size();
- 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_ReportErrorUTF8(cx, "Failed to initialize cURL handle.");
- if(state.recvbuf) JS_free(cx, state.recvbuf);
- return ret;
- }
-
- tmp = JS_GetReservedSlot(obj, 0);
-
- std::string referer;
- if(!js_to_string(cx, JS::RootedValue(cx, tmp), referer)) {
- JS_ReportErrorUTF8(cx, "Failed to encode referer.");
- if(state.recvbuf) JS_free(cx, state.recvbuf);
- return ret;
- }
- curl_easy_setopt(HTTP_HANDLE, CURLOPT_REFERER, referer.c_str());
-
- if(http->method < 0 || http->method > OPTIONS) {
- JS_ReportErrorUTF8(cx, "INTERNAL: Unknown method.");
- if(state.recvbuf) JS_free(cx, state.recvbuf);
- return ret;
- }
-
- 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.size() > 0) {
- curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, body.size());
- } 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.c_str());
- 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_ReportErrorUTF8(cx, "Failed to execute HTTP request: %s", ERRBUF);
- if(state.recvbuf) JS_free(cx, state.recvbuf);
- return ret;
- }
-
- if(!state.resp_headers) {
- JS_ReportErrorUTF8(cx, "Failed to recieve HTTP headers.");
- if(state.recvbuf) JS_free(cx, state.recvbuf);
- return ret;
- }
- tmp = JS::ObjectValue(*state.resp_headers);
- JS::RootedValue rtmp(cx, tmp);
-
- if(!JS_DefineProperty(
- cx, robj,
- "_headers",
- rtmp,
- JSPROP_READONLY
- )) {
- JS_ReportErrorUTF8(cx, "INTERNAL: Failed to set response headers.");
- if(state.recvbuf) JS_free(cx, state.recvbuf);
- return ret;;
- }
-
- if(state.recvbuf) {
- state.recvbuf[state.read] = '\0';
- std::string bodystr(state.recvbuf, state.read);
- jsbody = string_to_js(cx, bodystr);
- 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 char16_t.
- jsbody = JS_NewStringCopyN(cx, state.recvbuf, state.read);
- if(!jsbody) {
- if(!JS_IsExceptionPending(cx)) {
- JS_ReportErrorUTF8(cx, "INTERNAL: Failed to decode body.");
- }
- if(state.recvbuf) JS_free(cx, state.recvbuf);
- return ret;
- }
- }
- tmp = JS::StringValue(jsbody);
- } else {
- tmp = JS_GetEmptyStringValue(cx);
- }
-
- JS::RootedValue rtmp2(cx, tmp);
-
- if(!JS_DefineProperty(
- cx, robj,
- "responseText",
- rtmp2,
- JSPROP_READONLY
- )) {
- JS_ReportErrorUTF8(cx, "INTERNAL: Failed to set responseText.");
- if(state.recvbuf) JS_free(cx, state.recvbuf);
- return ret;
- }
-
- ret = true;
- 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 = static_cast<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 = static_cast<CurlState*>(ptr);
- if(origin != SEEK_SET) return -1;
-
- state->sent = static_cast<size_t>(offset);
- return static_cast<int>(state->sent);
-}
-
-static size_t
-recv_header(void *ptr, size_t size, size_t nmem, void *data)
-{
- CurlState* state = static_cast<CurlState*>(data);
- char code[4];
- char* header = static_cast<char*>(ptr);
- size_t length = size * nmem;
- JSString* hdr = NULL;
- uint32_t hdrlen;
-
- 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);
- 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.
- std::string hdrstr(header, length);
- hdr = string_to_js(state->cx, hdrstr);
- if(!hdr) {
- return CURLE_WRITE_ERROR;
- }
-
- JS::RootedObject obj(state->cx, state->resp_headers);
- if(!JS_GetArrayLength(state->cx, obj, &hdrlen)) {
- return CURLE_WRITE_ERROR;
- }
-
- JS::RootedString hdrval(state->cx, hdr);
- if(!JS_SetElement(state->cx, obj, 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 = static_cast<CurlState*>(data);
- size_t length = size * nmem;
- char* tmp = NULL;
-
- if(!state->recvbuf) {
- state->recvlen = 4096;
- state->read = 0;
- state->recvbuf = static_cast<char*>(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.
- size_t oldlen = state->recvlen;
- while(length+1 > state->recvlen - state->read) state->recvlen *= 2;
- tmp = static_cast<char*>(JS_realloc(
- state->cx,
- state->recvbuf,
- oldlen,
- state->recvlen
- ));
- if(!tmp) return CURLE_WRITE_ERROR;
- state->recvbuf = tmp;
-
- memcpy(state->recvbuf + state->read, ptr, length);
- state->read += length;
- return length;
-}
-
-#endif /* HAVE_CURL */