couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dav...@apache.org
Subject svn commit: r884672 - in /couchdb/trunk: ./ bin/ etc/couchdb/ src/couchdb/priv/ src/couchdb/priv/couch_js/ test/etap/
Date Thu, 26 Nov 2009 19:31:01 GMT
Author: davisp
Date: Thu Nov 26 19:31:00 2009
New Revision: 884672

URL: http://svn.apache.org/viewvc?rev=884672&view=rev
Log:
Complete refactoring of couch_js.

In particular, the cURL bindings have been rewritten to be more useful
and easily applied in command line scripts.


Added:
    couchdb/trunk/src/couchdb/priv/couch_js/http.c
    couchdb/trunk/src/couchdb/priv/couch_js/http.h
    couchdb/trunk/src/couchdb/priv/couch_js/main.c
    couchdb/trunk/src/couchdb/priv/couch_js/utf8.c
    couchdb/trunk/src/couchdb/priv/couch_js/utf8.h
Removed:
    couchdb/trunk/src/couchdb/priv/couch_js/couch_js.c
    couchdb/trunk/src/couchdb/priv/couch_js/curlhelper.c
    couchdb/trunk/src/couchdb/priv/couch_js/curlhelper.h
Modified:
    couchdb/trunk/.gitignore
    couchdb/trunk/bin/Makefile.am
    couchdb/trunk/etc/couchdb/Makefile.am
    couchdb/trunk/src/couchdb/priv/Makefile.am
    couchdb/trunk/test/etap/Makefile.am

Modified: couchdb/trunk/.gitignore
URL: http://svn.apache.org/viewvc/couchdb/trunk/.gitignore?rev=884672&r1=884671&r2=884672&view=diff
==============================================================================
--- couchdb/trunk/.gitignore (original)
+++ couchdb/trunk/.gitignore Thu Nov 26 19:31:00 2009
@@ -58,7 +58,12 @@
 src/couchdb/priv/couch_icu_driver.la
 src/couchdb/priv/couchjs
 src/couchdb/priv/couchspawnkillable
+src/couchdb/priv/stat_descriptions.cfg
+src/erlang-oauth/oauth.app
+src/ibrowse/ibrowse.app
+src/mochiweb/mochiweb.app
 test/local.ini
+test/etap/run
 test/etap/test_util.erl
 share/server/main.js
 

Modified: couchdb/trunk/bin/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/trunk/bin/Makefile.am?rev=884672&r1=884671&r2=884672&view=diff
==============================================================================
--- couchdb/trunk/bin/Makefile.am (original)
+++ couchdb/trunk/bin/Makefile.am Thu Nov 26 19:31:00 2009
@@ -60,7 +60,7 @@
 	chmod +x $@
 
 couchjs_dev: couchjs.tpl
-	sed -e "s|%locallibbindir%|$(abs_top_builddir)/src/couchdb|g" \
+	sed -e "s|%locallibbindir%|$(abs_top_builddir)/src/couchdb/priv|g" \
 	    -e "s|%bug_uri%|@bug_uri@|g" \
 	    -e "s|%package_author_address%|@package_author_address@|g" \
 	    -e "s|%package_author_name%|@package_author_name@|g" \

Modified: couchdb/trunk/etc/couchdb/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/trunk/etc/couchdb/Makefile.am?rev=884672&r1=884671&r2=884672&view=diff
==============================================================================
--- couchdb/trunk/etc/couchdb/Makefile.am (original)
+++ couchdb/trunk/etc/couchdb/Makefile.am Thu Nov 26 19:31:00 2009
@@ -11,7 +11,7 @@
 ## the License.
 
 couchprivlibdir = $(localerlanglibdir)/couch-$(version)/priv/lib
-devcouchprivlibdir = $(abs_top_builddir)/src/couchdb/.libs
+devcouchprivlibdir = $(abs_top_builddir)/src/couchdb/priv/.libs
 
 localconf_DATA = default.ini
 noinst_DATA = default_dev.ini local_dev.ini

Modified: couchdb/trunk/src/couchdb/priv/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/Makefile.am?rev=884672&r1=884671&r2=884672&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/priv/Makefile.am (original)
+++ couchdb/trunk/src/couchdb/priv/Makefile.am Thu Nov 26 19:31:00 2009
@@ -15,11 +15,41 @@
 couchprivlibdir = $(couchlibdir)/priv/lib
 
 EXTRA_DIST = \
-	couchspawnkillable.sh \
+	spawnkillable/couchspawnkillable.sh \
 	stat_descriptions.cfg.in
 
 CLEANFILES = stat_descriptions.cfg
 
+ICU_LOCAL_FLAGS = $(ICU_LOCAL_CFLAGS) $(ICU_LOCAL_LDFLAGS)
+if WINDOWS
+ICU_LOCAL_LIBS=-licuuc -licudt -licuin
+else
+ICU_LOCAL_LIBS=-licuuc -licudata -licui18n
+endif
+
+couchprivlib_LTLIBRARIES = couch_icu_driver.la
+couch_icu_driver_la_SOURCES = icu_driver/couch_icu_driver.c
+couch_icu_driver_la_LDFLAGS = -module -avoid-version $(ICU_LOCAL_FLAGS)
+couch_icu_driver_la_CFLAGS = $(ICU_LOCAL_FLAGS)
+couch_icu_driver_la_LIBADD = $(ICU_LOCAL_LIBS)
+
+if WINDOWS
+couch_icu_driver_la_LDFLAGS += -no-undefined
+endif
+
+COUCHJS_SRCS = \
+	couch_js/http.c \
+	couch_js/http.h \
+	couch_js/main.c \
+	couch_js/utf8.c \
+	couch_js/utf8.h
+
+locallibbin_PROGRAMS = couchjs
+couchjs_SOURCES = $(COUCHJS_SRCS)
+couchjs_LDFLAGS = $(CURL_LDFLAGS)
+couchjs_CFLAGS = $(CURL_CFLAGS)
+couchjs_LDADD = $(CURL_LDFLAGS) @JSLIB@
+
 couchpriv_DATA = stat_descriptions.cfg
 couchpriv_PROGRAMS = couchspawnkillable
 

Added: couchdb/trunk/src/couchdb/priv/couch_js/http.c
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/couch_js/http.c?rev=884672&view=auto
==============================================================================
--- couchdb/trunk/src/couchdb/priv/couch_js/http.c (added)
+++ couchdb/trunk/src/couchdb/priv/couch_js/http.c Thu Nov 26 19:31:00 2009
@@ -0,0 +1,647 @@
+// 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 <stdlib.h>
+#include <string.h>
+#include <jsapi.h>
+#include <curl/curl.h>
+
+#include "utf8.h"
+
+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", NULL};
+
+#define GET     0
+#define HEAD    1
+#define POST    2
+#define PUT     3
+#define DELETE  4
+#define COPY    5
+
+static JSBool
+go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t blen);
+
+static JSBool
+constructor(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+    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, obj, 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;
+}
+
+static void
+destructor(JSContext* cx, JSObject* obj)
+{
+    HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
+    if(!http)
+    {
+        fprintf(stderr, "Unable to destroy invalid CouchHTTP instance.\n");
+    }
+    else
+    {
+        if(http->url) free(http->url);
+        if(http->req_headers) curl_slist_free_all(http->req_headers);
+        free(http);
+    }
+}
+
+static JSBool
+open(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{    
+    HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
+    char* method = NULL;
+    char* url = NULL;
+    JSBool ret = JS_FALSE;
+    int methid;
+
+    if(!http)
+    {
+        JS_ReportError(cx, "Invalid CouchHTTP instance.");
+        goto done;
+    }
+
+    if(argv[0] == JSVAL_VOID)
+    {
+        JS_ReportError(cx, "You must specify a method.");
+        goto done;
+    }
+
+    method = enc_string(cx, argv[0], 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 > COPY)
+    {
+        JS_ReportError(cx, "Invalid method specified.");
+        goto done;
+    }
+
+    http->method = methid;
+
+    if(argv[1] == JSVAL_VOID)
+    {
+        JS_ReportError(cx, "You must specify a URL.");
+        goto done;
+    }
+
+    if(http->url)
+    {
+        free(http->url);
+        http->url = NULL;
+    }
+
+    http->url = enc_string(cx, argv[1], NULL);
+    if(!http->url)
+    {
+        JS_ReportError(cx, "Failed to encode URL.");
+        goto done;
+    }
+    
+    if(argv[2] != JSVAL_VOID && argv[2] != JSVAL_FALSE)
+    {
+        JS_ReportError(cx, "Synchronous flag must be false if specified.");
+        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;
+}
+
+static JSBool
+setheader(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{    
+    HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
+    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(argv[0] == JSVAL_VOID)
+    {
+        JS_ReportError(cx, "You must speciy a header name.");
+        goto done;
+    }
+
+    keystr = enc_string(cx, argv[0], NULL);
+    if(!keystr)
+    {
+        JS_ReportError(cx, "Failed to encode header name.");
+        goto done;
+    }
+    
+    if(argv[1] == JSVAL_VOID)
+    {
+        JS_ReportError(cx, "You must specify a header value.");
+        goto done;
+    }
+    
+    valstr = enc_string(cx, argv[1], 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;
+}
+
+static JSBool
+sendreq(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+    HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
+    char* body = NULL;
+    size_t bodylen = 0;
+    JSBool ret = JS_FALSE;
+    
+    if(!http)
+    {
+        JS_ReportError(cx, "Invalid CouchHTTP instance.");
+        goto done;
+    }
+
+    if(argv[0] != JSVAL_VOID && argv[0] != JS_GetEmptyStringValue(cx))
+    {
+        body = enc_string(cx, argv[0], &bodylen);
+        if(!body)
+        {
+            JS_ReportError(cx, "Failed to encode body.");
+            goto done;
+        }
+    }
+
+    ret = go(cx, obj, http, body, bodylen);
+
+done:
+    if(body) free(body);
+    return ret;
+}
+
+static JSBool
+status(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
+{
+    HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
+    
+    if(!http)
+    {
+        JS_ReportError(cx, "Invalid CouchHTTP instance.");
+        return JS_FALSE;
+    }
+    
+    if(INT_FITS_IN_JSVAL(http->last_status))
+    {
+        *vp = INT_TO_JSVAL(http->last_status);
+        return JS_TRUE;
+    }
+    else
+    {
+        JS_ReportError(cx, "INTERNAL: Invalid last_status");
+        return JS_FALSE;
+    }
+}
+
+JSClass CouchHTTPClass = {
+    "CouchHTTP",
+    JSCLASS_HAS_PRIVATE
+        | JSCLASS_CONSTRUCT_PROTOTYPE
+        | JSCLASS_HAS_RESERVED_SLOTS(2),
+    JS_PropertyStub,
+    JS_PropertyStub,
+    JS_PropertyStub,
+    JS_PropertyStub,
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    destructor,
+    JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSPropertySpec CouchHTTPProperties[] = {
+    {"status", 0, JSPROP_READONLY, status, NULL},
+    {0, 0, 0, 0, 0}
+};
+
+JSFunctionSpec CouchHTTPFunctions[] = {
+    {"_open", open, 3, 0, 0},
+    {"_setRequestHeader", setheader, 2, 0, 0},
+    {"_send", sendreq, 1, 0, 0},
+    {0, 0, 0, 0, 0}
+};
+
+JSObject*
+install_http(JSContext* cx, JSObject* glbl)
+{
+    JSObject* klass = NULL;
+    HTTPData* http = NULL;
+
+    klass = JS_InitClass(
+        cx,
+        glbl,
+        NULL,
+        &CouchHTTPClass,
+        constructor,
+        0,
+        CouchHTTPProperties,
+        CouchHTTPFunctions,
+        NULL,
+        NULL
+    );
+
+    if(!klass)
+    {
+        fprintf(stderr, "Failed to initialize CouchHTTP class.\n");
+        return NULL;
+    }
+    
+    return klass;
+}
+
+
+// Curl Helpers
+
+typedef struct {
+    HTTPData*   http;
+    JSContext*  cx;
+    JSObject*   resp_headers;
+    char*       sendbuf;
+    size_t      sendlen;
+    size_t      sent;
+    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*       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;
+    JSString* jsbody;
+    JSBool ret = JS_FALSE;
+    jsval tmp;
+    
+    state.cx = cx;
+    state.http = http;
+    
+    state.sendbuf = body;
+    state.sendlen = bodylen;
+    state.sent = 0;
+
+    state.recvbuf = NULL;
+    state.recvlen = 0;
+    state.read = 0;
+
+    if(HANDLE == NULL)
+    {
+        HANDLE = curl_easy_init();
+        curl_easy_setopt(HANDLE, CURLOPT_READFUNCTION, send_body);
+        curl_easy_setopt(HANDLE, CURLOPT_SEEKFUNCTION, seek_body);
+        curl_easy_setopt(HANDLE, CURLOPT_HEADERFUNCTION, recv_header);
+        curl_easy_setopt(HANDLE, CURLOPT_WRITEFUNCTION, recv_body);
+        curl_easy_setopt(HANDLE, CURLOPT_NOPROGRESS, 1);
+        curl_easy_setopt(HANDLE, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+        curl_easy_setopt(HANDLE, CURLOPT_ERRORBUFFER, ERRBUF);
+        curl_easy_setopt(HANDLE, CURLOPT_COOKIEFILE, "");
+        curl_easy_setopt(HANDLE, CURLOPT_USERAGENT, "CouchHTTP Client - Relax");
+    }
+    
+    if(!HANDLE)
+    {
+        JS_ReportError(cx, "Failed to initialize cURL handle.");
+        goto error;
+    }
+
+    if(http->method < 0 || http->method > COPY)
+    {
+        JS_ReportError(cx, "INTERNAL: Unknown method.");
+        goto error;
+    }
+
+    curl_easy_setopt(HANDLE, CURLOPT_CUSTOMREQUEST, METHODS[http->method]);
+    curl_easy_setopt(HANDLE, CURLOPT_NOBODY, 0);
+    curl_easy_setopt(HANDLE, CURLOPT_FOLLOWLOCATION, 1);
+    curl_easy_setopt(HANDLE, CURLOPT_UPLOAD, 0);
+    
+    if(http->method == HEAD)
+    {
+        curl_easy_setopt(HANDLE, CURLOPT_NOBODY, 1);
+        curl_easy_setopt(HANDLE, CURLOPT_FOLLOWLOCATION, 0);
+    }
+    else if(http->method == POST || http->method == PUT)
+    {
+        curl_easy_setopt(HANDLE, CURLOPT_UPLOAD, 1);
+        curl_easy_setopt(HANDLE, CURLOPT_FOLLOWLOCATION, 0);
+    }
+    
+    if(body && bodylen)
+    {
+        curl_easy_setopt(HANDLE, CURLOPT_INFILESIZE, bodylen);        
+    }
+    else
+    {
+        curl_easy_setopt(HANDLE, CURLOPT_INFILESIZE, 0);
+    }
+
+    //curl_easy_setopt(HANDLE, CURLOPT_VERBOSE, 1);
+
+    curl_easy_setopt(HANDLE, CURLOPT_URL, http->url);
+    curl_easy_setopt(HANDLE, CURLOPT_HTTPHEADER, http->req_headers);
+    curl_easy_setopt(HANDLE, CURLOPT_READDATA, &state);
+    curl_easy_setopt(HANDLE, CURLOPT_SEEKDATA, &state);
+    curl_easy_setopt(HANDLE, CURLOPT_WRITEHEADER, &state);
+    curl_easy_setopt(HANDLE, CURLOPT_WRITEDATA, &state);
+
+    if(curl_easy_perform(HANDLE) != 0)
+    {
+        JS_ReportError(cx, "Failed to execute HTTP request: %s", ERRBUF);
+        goto error;
+    }
+    
+    if(!state.resp_headers)
+    {
+        JS_ReportError(cx, "Failed to recieve HTTP headers.");
+        goto error;
+    }
+
+    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 error;
+    }
+    
+    if(state.recvbuf) // Is good enough?
+    {
+        state.recvbuf[state.read] = '\0';
+        jsbody = dec_string(cx, state.recvbuf, state.read+1);
+        if(!jsbody)
+        {
+            // This is so dirty its not even almost funny. I'm ignoring
+            // all sorts of content-types and character sets and just falling
+            // back to doing a chop job when something doesn't decode as UTF-8
+            // which is pretty sad. But, if you hate me for it, then feel free
+            // to write a patch that does the proper content-type parsing and
+            // actually respects charsets as returned in headers.
+            jsbody = JS_NewString(cx, state.recvbuf, state.read);
+            if(!jsbody) {
+                JS_ReportError(cx, "INTERNAL: Failed to decode body.");
+                goto error;
+            }
+        }
+        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 error;
+    }
+    
+    ret = JS_TRUE;
+    goto success;
+
+error:
+    if(state.recvbuf) JS_free(cx, state.recvbuf);
+
+success:    
+    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;
+    if(towrite == 0)
+    {
+        return 0;
+    }
+
+    if(length < towrite) towrite = length;
+
+    //fprintf(stderr, "%lu %lu %lu %lu\n", state->bodyused, state->bodyread, length,
towrite);
+
+    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;
+    size_t index = 0;
+    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;
+}
+ 

Added: couchdb/trunk/src/couchdb/priv/couch_js/http.h
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/couch_js/http.h?rev=884672&view=auto
==============================================================================
--- couchdb/trunk/src/couchdb/priv/couch_js/http.h (added)
+++ couchdb/trunk/src/couchdb/priv/couch_js/http.h Thu Nov 26 19:31:00 2009
@@ -0,0 +1,18 @@
+// 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.
+
+#ifndef COUCH_JS_HTTP_H
+#define COUCH_JS_HTTP_H
+
+JSObject* install_http(JSContext* cx, JSObject* global);
+
+#endif
\ No newline at end of file

Added: couchdb/trunk/src/couchdb/priv/couch_js/main.c
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/couch_js/main.c?rev=884672&view=auto
==============================================================================
--- couchdb/trunk/src/couchdb/priv/couch_js/main.c (added)
+++ couchdb/trunk/src/couchdb/priv/couch_js/main.c Thu Nov 26 19:31:00 2009
@@ -0,0 +1,324 @@
+// 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <jsapi.h>
+#include "config.h"
+
+#include "utf8.h"
+#include "http.h"
+
+int gExitCode = 0;
+
+#ifdef JS_THREADSAFE
+#define SETUP_REQUEST(cx) \
+    JS_SetContextThread(cx); \
+    JS_BeginRequest(cx);
+#define FINISH_REQUEST(cx) \
+    JS_EndRequest(cx); \
+    JS_ClearContextThread(cx);
+#else
+#define SETUP_REQUEST(cx)
+#define FINISH_REQUEST(cx)
+#endif
+
+static JSBool
+evalcx(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+    JSString *str;
+    JSObject *sandbox;
+    JSContext *subcx;
+    const jschar *src;
+    size_t srclen;
+    JSBool ret = JS_FALSE;
+    jsval v;
+
+    sandbox = NULL;
+    if(!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sandbox))
+    {
+        return JS_FALSE;
+    }
+
+    subcx = JS_NewContext(JS_GetRuntime(cx), 8L * 1024L);
+    if(!subcx)
+    {
+        JS_ReportOutOfMemory(cx);
+        return JS_FALSE;
+    }
+
+    SETUP_REQUEST(subcx);
+
+    src = JS_GetStringChars(str);
+    srclen = JS_GetStringLength(str);
+
+    if(!sandbox)
+    {
+        sandbox = JS_NewObject(subcx, NULL, NULL, NULL);
+        if(!sandbox || !JS_InitStandardClasses(subcx, sandbox)) goto done;
+    }
+
+    if(srclen == 0)
+    {
+        *rval = OBJECT_TO_JSVAL(sandbox);
+    }
+    else
+    {
+        JS_EvaluateUCScript(subcx, sandbox, src, srclen, NULL, 0, rval);
+    }
+    
+    ret = JS_TRUE;
+
+done:
+    FINISH_REQUEST(subcx);
+    JS_DestroyContext(subcx);
+    return ret;
+}
+
+static JSBool
+gc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+    JS_GC(cx);
+    return JS_TRUE;
+}
+
+static JSBool
+print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+    uintN i;
+    char *bytes;
+
+    for(i = 0; i < argc; i++)
+    {
+        bytes = enc_string(cx, argv[i], NULL);
+        if(!bytes) return JS_FALSE;
+
+        fprintf(stdout, "%s%s", i ? " " : "", bytes);
+        JS_free(cx, bytes);
+    }
+
+    fputc('\n', stdout);
+    fflush(stdout);
+    return JS_TRUE;
+}
+
+static JSBool
+quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+    JS_ConvertArguments(cx, argc, argv, "/ i", &gExitCode);
+    return JS_FALSE;
+}
+
+static char*
+readfp(JSContext* cx, FILE* fp, size_t* buflen)
+{
+    char* bytes = NULL;
+    char* tmp = NULL;
+    size_t used = 0;
+    size_t byteslen = 256;
+    size_t readlen = 0;
+
+    bytes = JS_malloc(cx, byteslen);
+    if(bytes == NULL) return NULL;
+    
+    while((readlen = js_fgets(bytes+used, byteslen-used, stdin)) > 0)
+    {
+        used += readlen;
+
+        if(bytes[used-1] == '\n')
+        {
+            bytes[used-1] = '\0';
+            break;
+        }
+
+        // Double our buffer and read more.
+        byteslen *= 2;
+        tmp = JS_realloc(cx, bytes, byteslen);
+        if(!tmp)
+        {
+            JS_free(cx, bytes);
+            return NULL;
+        }
+        bytes = tmp;
+    }
+
+    *buflen = used;
+    return bytes;
+}
+
+static JSBool
+readline(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+    jschar *chars;
+    JSString *str;
+    char* bytes;
+    char* tmp;
+    size_t byteslen;
+
+    /* GC Occasionally */
+    JS_MaybeGC(cx);
+
+    bytes = readfp(cx, stdin, &byteslen);
+    if(!bytes) return JS_FALSE;
+    
+    /* Treat the empty string specially */
+    if(byteslen == 0)
+    {
+        *rval = JS_GetEmptyStringValue(cx);
+        JS_free(cx, bytes);
+        return JS_TRUE;
+    }
+
+    /* Shrink the buffer to the real size */
+    tmp = JS_realloc(cx, bytes, byteslen);
+    if(!tmp)
+    {
+        JS_free(cx, bytes);
+        return JS_FALSE;
+    }
+    bytes = tmp;
+    
+    str = dec_string(cx, bytes, byteslen);
+    JS_free(cx, bytes);
+
+    if(!str) return JS_FALSE;
+
+    *rval = STRING_TO_JSVAL(str);
+
+    return JS_TRUE;
+}
+
+static JSBool
+seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+    JSObject *target;
+    JSBool deep = JS_FALSE;
+
+    if (!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep))
+        return JS_FALSE;
+    if (!target)
+        return JS_TRUE;
+    return JS_SealObject(cx, target, deep);
+}
+
+static void
+execute_script(JSContext *cx, JSObject *obj, const char *filename) {
+    FILE *file;
+    JSScript *script;
+    jsval result;
+
+    if(!filename || strcmp(filename, "-") == 0)
+    {
+        file = stdin;
+    }
+    else
+    {
+        file = fopen(filename, "r");
+        if (!file)
+        {
+            fprintf(stderr, "could not open script file %s\n", filename);
+            gExitCode = 1;
+            return;
+        }
+    }
+
+    script = JS_CompileFileHandle(cx, obj, filename, file);
+    if(script)
+    {
+        JS_ExecuteScript(cx, obj, script, &result);
+        JS_DestroyScript(cx, script);
+    }
+}
+
+static void
+printerror(JSContext *cx, const char *mesg, JSErrorReport *report)
+{
+    if(!report || !JSREPORT_IS_WARNING(report->flags))
+    {
+        fprintf(stderr, "%s\n", mesg);
+    }
+}
+
+static JSFunctionSpec global_functions[] = {
+    {"evalcx", evalcx, 0, 0, 0},
+    {"gc", gc, 0, 0, 0},
+    {"print", print, 0, 0, 0},
+    {"quit", quit, 0, 0, 0},
+    {"readline", readline, 0, 0, 0},
+    {"seal", seal, 0, 0, 0},
+    {0, 0, 0, 0, 0}
+};
+
+static JSClass global_class = {
+    "GlobalClass",
+    JSCLASS_GLOBAL_FLAGS,
+    JS_PropertyStub,
+    JS_PropertyStub,
+    JS_PropertyStub,
+    JS_PropertyStub,
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    JS_FinalizeStub,
+    JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+int
+main(int argc, const char * argv[])
+{
+    JSRuntime* rt = NULL;
+    JSContext* cx = NULL;
+    JSObject *global = NULL;
+    int i = 0;
+    
+    rt = JS_NewRuntime(64L * 1024L * 1024L);
+    if (!rt) return 1;
+
+    cx = JS_NewContext(rt, 8L * 1024L);
+    if (!cx) return 1;
+
+    JS_SetErrorReporter(cx, printerror);
+    JS_ToggleOptions(cx, JSOPTION_XML);
+    
+    SETUP_REQUEST(cx);
+
+    global = JS_NewObject(cx, &global_class, NULL, NULL);
+    if (!global) return 1;
+    if (!JS_InitStandardClasses(cx, global)) return 1;
+
+    if(!JS_DefineFunctions(cx, global, global_functions))
+    {
+        return 1;
+    }
+
+    if(!install_http(cx, global))
+    {
+        return 1;
+    }
+    
+    JS_SetGlobalObject(cx, global);
+
+    if(argc != 2) {
+        fprintf(stderr, "incorrect number of arguments\n\n");
+        fprintf(stderr, "usage: %s <scriptfile>\n", argv[0]);
+        return 2;
+    }
+
+    execute_script(cx, global, argv[1]);
+
+    FINISH_REQUEST(cx);
+
+    JS_DestroyContext(cx);
+    JS_DestroyRuntime(rt);
+    JS_ShutDown();
+
+    return gExitCode;
+}

Added: couchdb/trunk/src/couchdb/priv/couch_js/utf8.c
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/couch_js/utf8.c?rev=884672&view=auto
==============================================================================
--- couchdb/trunk/src/couchdb/priv/couch_js/utf8.c (added)
+++ couchdb/trunk/src/couchdb/priv/couch_js/utf8.c Thu Nov 26 19:31:00 2009
@@ -0,0 +1,286 @@
+// 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 <jsapi.h>
+
+static inline int
+enc_char(uint8 *utf8Buffer, uint32 ucs4Char)
+{
+    int utf8Length = 1;
+
+    if (ucs4Char < 0x80)
+    {
+        *utf8Buffer = (uint8)ucs4Char;
+    }
+    else
+    {
+        int i;
+        uint32 a = ucs4Char >> 11;
+        utf8Length = 2;
+        while(a)
+        {
+            a >>= 5;
+            utf8Length++;
+        }
+        i = utf8Length;
+        while(--i)
+        {
+            utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80);
+            ucs4Char >>= 6;
+        }
+        *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char);
+    }
+
+    return utf8Length;
+}
+
+static JSBool
+enc_charbuf(const jschar* src, size_t srclen, char* dst, size_t* dstlenp)
+{
+    size_t i;
+    size_t utf8Len;
+    size_t dstlen = *dstlenp;
+    size_t origDstlen = dstlen;
+    jschar c;
+    jschar c2;
+    uint32 v;
+    uint8 utf8buf[6];
+
+    if(!dst)
+    {
+        dstlen = origDstlen = (size_t) -1;
+    }
+
+    while(srclen)
+    {
+        c = *src++;
+        srclen--;
+
+        if((c >= 0xDC00) && (c <= 0xDFFF)) goto bad_surrogate;
+        
+        if(c < 0xD800 || c > 0xDBFF)
+        {
+            v = c;
+        }
+        else
+        {
+            if(srclen < 1) goto buffer_too_small;
+            c2 = *src++;
+            srclen--;
+            if ((c2 < 0xDC00) || (c2 > 0xDFFF))
+            {
+                c = c2;
+                goto bad_surrogate;
+            }
+            v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
+        }
+        if(v < 0x0080)
+        {
+            /* no encoding necessary - performance hack */
+            if(!dstlen) goto buffer_too_small;
+            if(dst) *dst++ = (char) v;
+            utf8Len = 1;
+        }
+        else
+        {
+            utf8Len = enc_char(utf8buf, v);
+            if(utf8Len > dstlen) goto buffer_too_small;
+            if(dst)
+            {
+                for (i = 0; i < utf8Len; i++)
+                {
+                    *dst++ = (char) utf8buf[i];
+                }
+            }
+        }
+        dstlen -= utf8Len;
+    }
+    
+    *dstlenp = (origDstlen - dstlen);
+    return JS_TRUE;
+
+bad_surrogate:
+    *dstlenp = (origDstlen - dstlen);
+    return JS_FALSE;
+
+buffer_too_small:
+    *dstlenp = (origDstlen - dstlen);
+    return JS_FALSE;
+}
+
+char*
+enc_string(JSContext* cx, jsval arg, size_t* buflen)
+{
+    JSString* str = NULL;
+    jschar* src = NULL;
+    char* bytes = NULL;
+    size_t srclen = 0;
+    size_t byteslen = 0;
+    
+    str = JS_ValueToString(cx, arg);
+    if(!str) goto error;
+
+    src = JS_GetStringChars(str);
+    srclen = JS_GetStringLength(str);
+
+    if(!enc_charbuf(src, srclen, NULL, &byteslen)) goto error;
+    
+    bytes = JS_malloc(cx, (byteslen) + 1);
+    bytes[byteslen] = 0;
+    
+    if(!enc_charbuf(src, srclen, bytes, &byteslen)) goto error;
+
+    if(buflen) *buflen = byteslen;
+    goto success;
+
+error:
+    if(bytes != NULL) JS_free(cx, bytes);
+    bytes = NULL;
+
+success:
+    return bytes;
+}
+
+static inline uint32
+dec_char(const uint8 *utf8Buffer, int utf8Length)
+{
+    uint32 ucs4Char;
+    uint32 minucs4Char;
+
+    /* from Unicode 3.1, non-shortest form is illegal */
+    static const uint32 minucs4Table[] = {
+        0x00000080, 0x00000800, 0x0001000, 0x0020000, 0x0400000
+    };
+
+    if (utf8Length == 1)
+    {
+        ucs4Char = *utf8Buffer;
+    }
+    else
+    {
+        ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1);
+        minucs4Char = minucs4Table[utf8Length-2];
+        while(--utf8Length)
+        {
+            ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F);
+        }
+        if(ucs4Char < minucs4Char || ucs4Char == 0xFFFE || ucs4Char == 0xFFFF)
+        {
+            ucs4Char = 0xFFFD;
+        }
+    }
+
+    return ucs4Char;
+}
+
+static JSBool
+dec_charbuf(const char *src, size_t srclen, jschar *dst, size_t *dstlenp)
+{
+    uint32 v;
+    size_t offset = 0;
+    size_t j;
+    size_t n;
+    size_t dstlen = *dstlenp;
+    size_t origDstlen = dstlen;
+
+    if(!dst) dstlen = origDstlen = (size_t) -1;
+
+    while(srclen)
+    {
+        v = (uint8) *src;
+        n = 1;
+        
+        if(v & 0x80)
+        {
+            while(v & (0x80 >> n))
+            {
+                n++;
+            }
+            
+            if(n > srclen) goto buffer_too_small;
+            if(n == 1 || n > 6) goto bad_character;
+            
+            for(j = 1; j < n; j++)
+            {
+                if((src[j] & 0xC0) != 0x80) goto bad_character;
+            }
+
+            v = dec_char((const uint8 *) src, n);
+            if(v >= 0x10000)
+            {
+                v -= 0x10000;
+                
+                if(v > 0xFFFFF || dstlen < 2)
+                {
+                    *dstlenp = (origDstlen - dstlen);
+                    return JS_FALSE;
+                }
+                
+                if(dstlen < 2) goto buffer_too_small;
+
+                if(dst)
+                {
+                    *dst++ = (jschar)((v >> 10) + 0xD800);
+                    v = (jschar)((v & 0x3FF) + 0xDC00);
+                }
+                dstlen--;
+            }
+        }
+
+        if(!dstlen) goto buffer_too_small;
+        if(dst) *dst++ = (jschar) v;
+
+        dstlen--;
+        offset += n;
+        src += n;
+        srclen -= n;
+    }
+
+    *dstlenp = (origDstlen - dstlen);
+    return JS_TRUE;
+
+bad_character:
+    *dstlenp = (origDstlen - dstlen);
+    return JS_FALSE;
+
+buffer_too_small:
+    *dstlenp = (origDstlen - dstlen);
+    return JS_FALSE;
+}
+
+JSString*
+dec_string(JSContext* cx, const char* bytes, size_t byteslen)
+{
+    JSString* str = NULL;
+    jschar* chars = NULL;
+    size_t charslen;
+    
+    if(!dec_charbuf(bytes, byteslen, NULL, &charslen)) goto error;
+
+    chars = JS_malloc(cx, (charslen + 1) * sizeof(jschar));
+    if(!chars) return NULL;
+    chars[charslen] = 0;
+
+    if(!dec_charbuf(bytes, byteslen, chars, &charslen)) goto error;
+
+    str = JS_NewUCString(cx, chars, charslen - 1);
+    if(!str) goto error;
+
+    goto success;
+
+error:
+    if(chars != NULL) JS_free(cx, chars);
+    str = NULL;
+
+success:
+    return str;
+}
\ No newline at end of file

Added: couchdb/trunk/src/couchdb/priv/couch_js/utf8.h
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/couch_js/utf8.h?rev=884672&view=auto
==============================================================================
--- couchdb/trunk/src/couchdb/priv/couch_js/utf8.h (added)
+++ couchdb/trunk/src/couchdb/priv/couch_js/utf8.h Thu Nov 26 19:31:00 2009
@@ -0,0 +1,19 @@
+// 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.
+
+#ifndef COUCH_JS_UTF_8_H
+#define COUCH_JS_UTF_8_H
+
+char* enc_string(JSContext* cx, jsval arg, size_t* buflen);
+JSString* dec_string(JSContext* cx, const char* buf, size_t buflen);
+
+#endif
\ No newline at end of file

Modified: couchdb/trunk/test/etap/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/etap/Makefile.am?rev=884672&r1=884671&r2=884672&view=diff
==============================================================================
--- couchdb/trunk/test/etap/Makefile.am (original)
+++ couchdb/trunk/test/etap/Makefile.am Thu Nov 26 19:31:00 2009
@@ -28,7 +28,7 @@
 EXTRA_DIST = \
 	run.tpl \
     001-load.t \
-    002-erl-driver.t \
+    002-icu-driver.t \
     010-file-basics.t \
     011-file-headers.t \
     020-btree-basics.t \



Mime
View raw message