summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/couchdb/priv/couch_js/http.c675
-rw-r--r--src/couchdb/priv/couch_js/http.h18
-rw-r--r--src/couchdb/priv/couch_js/main.c338
-rw-r--r--src/couchdb/priv/couch_js/utf8.c286
-rw-r--r--src/couchdb/priv/couch_js/utf8.h19
-rw-r--r--src/couchdb/priv/spawnkillable/couchspawnkillable_win.c145
-rw-r--r--src/rexi.erl91
-rw-r--r--src/rexi_app.erl11
-rw-r--r--src/rexi_monitor.erl40
-rw-r--r--src/rexi_server.erl86
-rw-r--r--src/rexi_sup.erl15
11 files changed, 1481 insertions, 243 deletions
diff --git a/src/couchdb/priv/couch_js/http.c b/src/couchdb/priv/couch_js/http.c
new file mode 100644
index 00000000..6c2a8a82
--- /dev/null
+++ b/src/couchdb/priv/couch_js/http.c
@@ -0,0 +1,675 @@
+// 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 <jsapi.h>
+#include <curl/curl.h>
+
+#include "utf8.h"
+
+#ifdef XP_WIN
+// Map some of the string function names to things which exist on Windows
+#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", 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 JSString*
+str_from_binary(JSContext* cx, char* data, size_t length);
+
+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* 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;
+ 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(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(http->method < 0 || http->method > COPY)
+ {
+ 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) // Is good enough?
+ {
+ 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;
+ 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;
+}
+
+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;
+}
diff --git a/src/couchdb/priv/couch_js/http.h b/src/couchdb/priv/couch_js/http.h
new file mode 100644
index 00000000..b5f8c70f
--- /dev/null
+++ b/src/couchdb/priv/couch_js/http.h
@@ -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
diff --git a/src/couchdb/priv/couch_js/main.c b/src/couchdb/priv/couch_js/main.c
new file mode 100644
index 00000000..376aa15b
--- /dev/null
+++ b/src/couchdb/priv/couch_js/main.c
@@ -0,0 +1,338 @@
+// 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;
+ JSFunctionSpec* sp = 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;
+
+ for(sp = global_functions; sp->name != NULL; sp++)
+ {
+ if(!JS_DefineFunction(cx, global,
+ sp->name, sp->call, sp->nargs, sp->flags))
+ {
+ fprintf(stderr, "Failed to create function: %s\n", sp->name);
+ 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;
+ }
+
+ if(argc == 0)
+ {
+ execute_script(cx, global, NULL);
+ }
+ else
+ {
+ execute_script(cx, global, argv[1]);
+ }
+
+ FINISH_REQUEST(cx);
+
+ JS_DestroyContext(cx);
+ JS_DestroyRuntime(rt);
+ JS_ShutDown();
+
+ return gExitCode;
+}
diff --git a/src/couchdb/priv/couch_js/utf8.c b/src/couchdb/priv/couch_js/utf8.c
new file mode 100644
index 00000000..699a6fee
--- /dev/null
+++ b/src/couchdb/priv/couch_js/utf8.c
@@ -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 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 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
diff --git a/src/couchdb/priv/couch_js/utf8.h b/src/couchdb/priv/couch_js/utf8.h
new file mode 100644
index 00000000..00f6b736
--- /dev/null
+++ b/src/couchdb/priv/couch_js/utf8.h
@@ -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
diff --git a/src/couchdb/priv/spawnkillable/couchspawnkillable_win.c b/src/couchdb/priv/spawnkillable/couchspawnkillable_win.c
new file mode 100644
index 00000000..06782315
--- /dev/null
+++ b/src/couchdb/priv/spawnkillable/couchspawnkillable_win.c
@@ -0,0 +1,145 @@
+// 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.
+
+// Do what 2 lines of shell script in couchspawnkillable does...
+// * Create a new suspended process with the same (duplicated) standard
+// handles as us.
+// * Write a line to stdout, consisting of the path to ourselves, plus
+// '--kill {pid}' where {pid} is the PID of the newly created process.
+// * Un-suspend the new process.
+// * Wait for the process to terminate.
+// * Terminate with the child's exit-code.
+
+// Later, couch will call us with --kill and the PID, so we dutifully
+// terminate the specified PID.
+
+#include <stdlib.h>
+#include "windows.h"
+
+char *get_child_cmdline(int argc, char **argv)
+{
+ // make a new command-line, but skipping me.
+ // XXX - todo - spaces etc in args???
+ int i;
+ char *p, *cmdline;
+ int nchars = 0;
+ int nthis = 1;
+ for (i=1;i<argc;i++)
+ nchars += strlen(argv[i])+1;
+ cmdline = p = malloc(nchars+1);
+ if (!cmdline)
+ return NULL;
+ for (i=1;i<argc;i++) {
+ nthis = strlen(argv[i]);
+ strncpy(p, argv[i], nthis);
+ p[nthis] = ' ';
+ p += nthis+1;
+ }
+ // Replace the last space we added above with a '\0'
+ cmdline[nchars-1] = '\0';
+ return cmdline;
+}
+
+// create the child process, returning 0, or the exit-code we will
+// terminate with.
+int create_child(int argc, char **argv, PROCESS_INFORMATION *pi)
+{
+ char buf[1024];
+ DWORD dwcreate;
+ STARTUPINFO si;
+ char *cmdline;
+ if (argc < 2)
+ return 1;
+ cmdline = get_child_cmdline(argc, argv);
+ if (!cmdline)
+ return 2;
+
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ // depending on how *our* parent is started, we may or may not have
+ // a valid stderr stream - so although we try and duplicate it, only
+ // failing to duplicate stdin and stdout are considered fatal.
+ if (!DuplicateHandle(GetCurrentProcess(),
+ GetStdHandle(STD_INPUT_HANDLE),
+ GetCurrentProcess(),
+ &si.hStdInput,
+ 0,
+ TRUE, // inheritable
+ DUPLICATE_SAME_ACCESS) ||
+ !DuplicateHandle(GetCurrentProcess(),
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ GetCurrentProcess(),
+ &si.hStdOutput,
+ 0,
+ TRUE, // inheritable
+ DUPLICATE_SAME_ACCESS)) {
+ return 3;
+ }
+ DuplicateHandle(GetCurrentProcess(),
+ GetStdHandle(STD_ERROR_HANDLE),
+ GetCurrentProcess(),
+ &si.hStdError,
+ 0,
+ TRUE, // inheritable
+ DUPLICATE_SAME_ACCESS);
+
+ si.dwFlags = STARTF_USESTDHANDLES;
+ dwcreate = CREATE_SUSPENDED;
+ if (!CreateProcess( NULL, cmdline,
+ NULL,
+ NULL,
+ TRUE, // inherit handles
+ dwcreate,
+ NULL, // environ
+ NULL, // cwd
+ &si,
+ pi))
+ return 4;
+ return 0;
+}
+
+// and here we go...
+int main(int argc, char **argv)
+{
+ char out_buf[1024];
+ int rc;
+ DWORD cbwritten;
+ DWORD exitcode;
+ PROCESS_INFORMATION pi;
+ if (argc==3 && strcmp(argv[1], "--kill")==0) {
+ HANDLE h = OpenProcess(PROCESS_TERMINATE, 0, atoi(argv[2]));
+ if (!h)
+ return 1;
+ if (!TerminateProcess(h, 0))
+ return 2;
+ CloseHandle(h);
+ return 0;
+ }
+ // spawn the new suspended process
+ rc = create_child(argc, argv, &pi);
+ if (rc)
+ return rc;
+ // Write the 'terminate' command, which includes this PID, back to couch.
+ // *sob* - what about spaces etc?
+ sprintf_s(out_buf, sizeof(out_buf), "%s --kill %d\n",
+ argv[0], pi.dwProcessId);
+ WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), out_buf, strlen(out_buf),
+ &cbwritten, NULL);
+ // Let the child process go...
+ ResumeThread(pi.hThread);
+ // Wait for the process to terminate so we can reflect the exit code
+ // back to couch.
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ if (!GetExitCodeProcess(pi.hProcess, &exitcode))
+ return 6;
+ return exitcode;
+}
diff --git a/src/rexi.erl b/src/rexi.erl
deleted file mode 100644
index 8ab1f05e..00000000
--- a/src/rexi.erl
+++ /dev/null
@@ -1,91 +0,0 @@
--module(rexi).
--export([start/0, stop/0, restart/0]).
--export([cast/2, cast/3, kill/2]).
--export([reply/1, sync_reply/1, sync_reply/2]).
--export([async_server_call/2, async_server_call/3]).
-
--define(SERVER, rexi_server).
-
-start() ->
- application:start(rexi).
-
-stop() ->
- application:stop(rexi).
-
-restart() ->
- stop(), start().
-
-%% @equiv cast(Node, self(), MFA)
--spec cast(node(), {atom(), atom(), list()}) -> reference().
-cast(Node, MFA) ->
- cast(Node, self(), MFA).
-
-%% @doc Executes apply(M, F, A) on Node.
-%% You might want to use this instead of rpc:cast/4 for two reasons. First,
-%% the Caller pid and the returned reference are inserted into the remote
-%% process' dictionary as `rexi_from', so it has a way to communicate with you.
-%% Second, the remote process is monitored. If it exits with a Reason other
-%% than normal, Caller will receive a message of the form
-%% `{Ref, {rexi_EXIT, Reason}}' where Ref is the returned reference.
--spec cast(node(), pid(), {atom(), atom(), list()}) -> reference().
-cast(Node, Caller, MFA) ->
- Ref = make_ref(),
- ok = gen_server:cast({?SERVER, Node}, {doit, {Caller,Ref}, MFA}),
- Ref.
-
-%% @doc Sends an async kill signal to the remote process associated with Ref.
-%% No rexi_EXIT message will be sent.
--spec kill(node(), reference()) -> ok.
-kill(Node, Ref) ->
- ok = gen_server:cast({?SERVER, Node}, {kill, Ref}).
-
-%% @equiv async_server_call(Server, self(), Request)
--spec async_server_call(pid() | {atom(),node()}, any()) -> reference().
-async_server_call(Server, Request) ->
- async_server_call(Server, self(), Request).
-
-%% @doc Sends a properly formatted gen_server:call Request to the Server and
-%% returns the reference which the Server will include in its reply. The
-%% function acts more like cast() than call() in that the server process
-%% is not monitored. Clients who want to know if the server is alive should
-%% monitor it themselves before calling this function.
--spec async_server_call(pid() | {atom(),node()}, pid(), any()) -> reference().
-async_server_call(Server, Caller, Request) ->
- Ref = make_ref(),
- do_send(Server, {'$gen_call', {Caller,Ref}, Request}),
- Ref.
-
-%% @doc convenience function to reply to the original rexi Caller.
--spec reply(any()) -> any().
-reply(Reply) ->
- {Caller, Ref} = get(rexi_from),
- erlang:send(Caller, {Ref,Reply}).
-
-%% @equiv sync_reply(Reply, 300000)
-sync_reply(Reply) ->
- sync_reply(Reply, 300000).
-
-%% @doc convenience function to reply to caller and wait for response. Message
-%% is of the form {OriginalRef, {self(),reference()}, Reply}, which enables the
-%% original caller to respond back.
--spec sync_reply(any(), pos_integer() | infinity) -> any().
-sync_reply(Reply, Timeout) ->
- {Caller, Ref} = get(rexi_from),
- Tag = make_ref(),
- erlang:send(Caller, {Ref, {self(),Tag}, Reply}),
- receive {Tag, Response} ->
- Response
- after Timeout ->
- timeout
- end.
-
-%% internal functions %%
-
-% send a message as quickly as possible
-do_send(Dest, Msg) ->
- case erlang:send(Dest, Msg, [noconnect]) of
- noconnect ->
- spawn(erlang, send, [Dest, Msg]);
- ok ->
- ok
- end.
diff --git a/src/rexi_app.erl b/src/rexi_app.erl
deleted file mode 100644
index dda57752..00000000
--- a/src/rexi_app.erl
+++ /dev/null
@@ -1,11 +0,0 @@
--module(rexi_app).
--behaviour(application).
--export([start/2, stop/1]).
-
--include_lib("eunit/include/eunit.hrl").
-
-start(_Type, StartArgs) ->
- rexi_sup:start_link(StartArgs).
-
-stop(_State) ->
- ok.
diff --git a/src/rexi_monitor.erl b/src/rexi_monitor.erl
deleted file mode 100644
index bbff22b3..00000000
--- a/src/rexi_monitor.erl
+++ /dev/null
@@ -1,40 +0,0 @@
--module(rexi_monitor).
--export([start/1, stop/1]).
-
--include_lib("eunit/include/eunit.hrl").
-
-%% @doc spawn_links a process which monitors the supplied list of items and
-%% returns the process ID. If a monitored process exits, the caller will
-%% receive a {rexi_DOWN, MonitoringPid, DeadPid, Reason} message.
--spec start([pid() | atom() | {atom(),node()}]) -> pid().
-start(Procs) ->
- Parent = self(),
- spawn_link(fun() ->
- [erlang:monitor(process, P) || P <- Procs],
- wait_monitors(Parent)
- end).
-
-%% @doc Cleanly shut down the monitoring process and flush all rexi_DOWN
-%% messages from our mailbox.
--spec stop(pid()) -> ok.
-stop(MonitoringPid) ->
- MonitoringPid ! {self(), shutdown},
- flush_down_messages().
-
-%% internal functions %%
-
-wait_monitors(Parent) ->
- receive
- {'DOWN', _, process, Pid, Reason} ->
- Parent ! {rexi_DOWN, self(), Pid, Reason},
- wait_monitors(Parent);
- {Parent, shutdown} ->
- ok
- end.
-
-flush_down_messages() ->
- receive {rexi_DOWN, _, _, _} ->
- flush_down_messages()
- after 0 ->
- ok
- end.
diff --git a/src/rexi_server.erl b/src/rexi_server.erl
deleted file mode 100644
index c4dc740d..00000000
--- a/src/rexi_server.erl
+++ /dev/null
@@ -1,86 +0,0 @@
--module(rexi_server).
--behaviour(gen_server).
--export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
- code_change/3]).
-
--export([start_link/0, init_p/2]).
-
--include_lib("eunit/include/eunit.hrl").
-
--record(st, {
- workers = ets:new(workers, [private, {keypos,2}])
-}).
-
-start_link() ->
- gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
-
-init([]) ->
- {ok, #st{}}.
-
-handle_call(_Request, _From, St) ->
- {reply, ignored, St}.
-
-handle_cast({doit, From, MFA}, #st{workers=Workers} = St) ->
- {LocalPid, Ref} = spawn_monitor(?MODULE, init_p, [From, MFA]),
- {noreply, St#st{workers = add_worker({LocalPid, Ref, From}, Workers)}};
-
-handle_cast({kill, FromRef}, #st{workers=Workers} = St) ->
- case find_worker_from(FromRef, Workers) of
- {Pid, KeyRef, {_, FromRef}} ->
- erlang:demonitor(KeyRef),
- exit(Pid, kill),
- {noreply, St#st{workers = remove_worker(KeyRef, Workers)}};
- false ->
- {noreply, St}
- end.
-
-handle_info({'DOWN', Ref, process, _, normal}, #st{workers=Workers} = St) ->
- {noreply, St#st{workers = remove_worker(Ref, Workers)}};
-
-handle_info({'DOWN', Ref, process, Pid, Reason}, #st{workers=Workers} = St) ->
- case find_worker(Ref, Workers) of
- {Pid, Ref, From} ->
- notify_caller(From, Reason),
- {noreply, St#st{workers = remove_worker(Ref, Workers)}};
- false ->
- {noreply, St}
- end;
-
-handle_info(_Info, St) ->
- {noreply, St}.
-
-terminate(_Reason, St) ->
- ets:foldl(fun({Pid, _, _}, _) -> exit(Pid,kill) end, nil, St#st.workers),
- ok.
-
-code_change(_OldVsn, St, _Extra) ->
- {ok, St}.
-
-%% @doc initializes a process started by rexi_server.
--spec init_p({pid(), reference()}, {atom(), atom(), list()}) -> any().
-init_p(From, {M,F,A}) ->
- put(rexi_from, From),
- put(initial_call, {M,F,length(A)}),
- try apply(M, F, A) catch _:Reason -> exit(Reason) end.
-
-%% internal
-
-add_worker(Worker, Tab) ->
- ets:insert(Tab, Worker), Tab.
-
-remove_worker(Ref, Tab) ->
- ets:delete(Tab, Ref), Tab.
-
-find_worker(Ref, Tab) ->
- case ets:lookup(Tab, Ref) of [] -> false; [Worker] -> Worker end.
-
-find_worker_from(Ref, Tab) ->
- case ets:match_object(Tab, {'_', '_', {'_', Ref}}) of
- [] ->
- false;
- [Worker] ->
- Worker
- end.
-
-notify_caller({Caller, Ref}, Reason) ->
- Caller ! {Ref, {rexi_EXIT, Reason}}.
diff --git a/src/rexi_sup.erl b/src/rexi_sup.erl
deleted file mode 100644
index 3a518e7b..00000000
--- a/src/rexi_sup.erl
+++ /dev/null
@@ -1,15 +0,0 @@
--module(rexi_sup).
--behaviour(supervisor).
--export([init/1]).
-
--export([start_link/1]).
-
--include_lib("eunit/include/eunit.hrl").
-
-start_link(Args) ->
- supervisor:start_link({local,?MODULE}, ?MODULE, Args).
-
-init([]) ->
- Mod = rexi_server,
- Spec = {Mod, {Mod,start_link,[]}, permanent, 100, worker, [Mod]},
- {ok, {{one_for_one, 3, 10}, [Spec]}}.