From a378a6a13981b962039c8cd11e11caf0759965bd Mon Sep 17 00:00:00 2001 From: Randall Leeds Date: Sun, 2 Oct 2011 04:11:54 -0700 Subject: improve argument parsing in couchjs This change makes argument parsing in couchjs more robust: * Removes the need for couchjs script * Adds long options * Makes all the options to couchjs available (fix COUCHDB-893) Conflicts: bin/Makefile.am bin/couchjs.tpl.in configure.ac --- bin/Makefile.am | 37 +++------------- bin/couchjs.tpl.in | 92 ---------------------------------------- configure.ac | 6 +-- src/couchdb/priv/Makefile.am | 14 ++++++ src/couchdb/priv/couch_js/help.h | 82 +++++++++++++++++++++++++++++++++++ src/couchdb/priv/couch_js/util.c | 50 ++++++++++++++++------ src/couchdb/priv/couch_js/util.h | 1 - 7 files changed, 144 insertions(+), 138 deletions(-) delete mode 100644 bin/couchjs.tpl.in create mode 100644 src/couchdb/priv/couch_js/help.h diff --git a/bin/Makefile.am b/bin/Makefile.am index b8b818cb..7d50d4e4 100644 --- a/bin/Makefile.am +++ b/bin/Makefile.am @@ -13,13 +13,13 @@ if WINDOWS bin_SCRIPTS = couchdb.bat else -bin_SCRIPTS = couchdb couchjs +bin_SCRIPTS = couchdb endif noinst_SCRIPTS = couchjs_dev if HELP2MAN -dist_man1_MANS = couchdb.1 couchjs.1 +dist_man1_MANS = couchdb.1 endif CLEANFILES = $(bin_SCRIPTS) $(dist_man1_MANS) $(noinst_SCRIPTS) @@ -48,27 +48,12 @@ couchdb: couchdb.tpl $@ < $< chmod +x $@ -couchjs: couchjs.tpl - sed -e "s|%locallibbindir%|@locallibbindir@|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" \ - -e "s|%package_name%|@package_name@|g" \ - -e "s|%version%|@version@|g" \ - -e "s|%couchjs_command_name%|$(couchjs_command_name)|g" > \ - $@ < $< - chmod +x $@ +install-exec-hook: + $(LN_S) -f "$(locallibbindir)/$(couchjs_command_name)" \ + "$(DESTDIR)$(bindir)/$(couchjs_command_name)" -couchjs_dev: couchjs.tpl - 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" \ - -e "s|%package_name%|@package_name@|g" \ - -e "s|%version%|@version@|g" \ - -e "s|%couchjs_command_name%|$(couchjs_command_name)|g" > \ - $@ < $< - chmod +x $@ +couchjs_dev: + $(LN_S) -f "$(abs_top_builddir)/src/couchdb/priv/couchjs" "$@" couchdb.bat: couchdb.bat.tpl sed -e "s|%ICU_CONFIG%|$(ICU_CONFIG)|g" \ @@ -88,11 +73,3 @@ couchdb.1: couchdb.tpl.in $(HELP2MAN_EXECUTABLE) $(HELP2MAN_OPTION) \ --name="Apache CouchDB database server" ./couchdb --output $@; \ fi - -couchjs.1: couchjs.tpl.in - touch $@ - if test -x "$(HELP2MAN_EXECUTABLE)"; then \ - $(MAKE) -f Makefile couchjs; \ - $(HELP2MAN_EXECUTABLE) $(HELP2MAN_OPTION) \ - --name="Apache CouchDB JavaScript interpreter" ./couchjs --output $@; \ - fi diff --git a/bin/couchjs.tpl.in b/bin/couchjs.tpl.in deleted file mode 100644 index 6927a0d4..00000000 --- a/bin/couchjs.tpl.in +++ /dev/null @@ -1,92 +0,0 @@ -#! /bin/sh -e - -# 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. - -SCRIPT_OK=0 -SCRIPT_ERROR=1 - -DEFAULT_VERSION=170 - -basename=`basename $0` - -display_version () { - cat << EOF -$basename - %package_name% %version% - -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. -EOF -} - -display_help () { - cat << EOF -Usage: $basename [FILE] - -The $basename command runs the %package_name% JavaScript interpreter. - -The exit status is 0 for success or 1 for failure. - -Options: - - -h display a short help message and exit - -V display version information and exit - -Report bugs at <%bug_uri%>. -EOF -} - -display_error () { - if test -n "$1"; then - echo $1 >&2 - fi - echo >&2 - echo "Try \`"$basename" -h' for more information." >&2 - exit $SCRIPT_ERROR -} - -run_couchjs () { - exec %locallibbindir%/%couchjs_command_name% $@ -} - -parse_script_option_list () { - set +e - options=`getopt hV $@` - if test ! $? -eq 0; then - display_error - fi - set -e - eval set -- $options - while [ $# -gt 0 ]; do - case "$1" in - -h) shift; display_help; exit $SCRIPT_OK;; - -V) shift; display_version; exit $SCRIPT_OK;; - --) shift; break;; - *) break;; - esac - done - option_list=`echo $@ | sed 's/--//'` - if test -z "$option_list"; then - display_error "You must specify a FILE." - fi - run_couchjs $option_list -} - -parse_script_option_list $@ diff --git a/configure.ac b/configure.ac index b5f8697a..5124b8b1 100644 --- a/configure.ac +++ b/configure.ac @@ -10,7 +10,8 @@ dnl WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the dnl License for the specific language governing permissions and limitations dnl under the License. -AC_INIT([LOCAL_PACKAGE_NAME], [LOCAL_VERSION], [], [LOCAL_PACKAGE_TARNAME]) +AC_INIT([LOCAL_PACKAGE_NAME], [LOCAL_VERSION], [LOCAL_BUG_URI], + [LOCAL_PACKAGE_TARNAME]) AC_PREREQ([2.59]) @@ -339,7 +340,7 @@ AC_ARG_VAR([HELP2MAN_EXECUTABLE], [path to the `help2man' program]) if test -n "$HELP2MAN_EXECUTABLE"; then help2man_enabled=true else - if test -f "$srcdir/bin/couchdb.1" -a -f "$srcdir/bin/couchjs.1"; then + if test -f "$srcdir/bin/couchdb.1" -a -f "$srcdir/src/couchdb/priv/couchjs.1"; then help2man_enabled=true else help2man_enabled=false @@ -389,7 +390,6 @@ AC_SUBST(abs_top_builddir) AC_REVISION([LOCAL_VERSION]) AC_CONFIG_FILES([Makefile]) -AC_CONFIG_FILES([bin/couchjs.tpl]) AC_CONFIG_FILES([bin/couchdb.tpl]) AC_CONFIG_FILES([bin/couchdb.bat.tpl]) AC_CONFIG_FILES([bin/Makefile]) diff --git a/src/couchdb/priv/Makefile.am b/src/couchdb/priv/Makefile.am index 4a32c0a2..130da9f4 100644 --- a/src/couchdb/priv/Makefile.am +++ b/src/couchdb/priv/Makefile.am @@ -41,6 +41,7 @@ couch_icu_driver_la_LDFLAGS += -no-undefined endif COUCHJS_SRCS = \ + couch_js/help.h \ couch_js/http.c \ couch_js/http.h \ couch_js/main.c \ @@ -58,6 +59,19 @@ couchjs_LDADD = $(CURL_LDFLAGS) @JSLIB@ couchpriv_DATA = stat_descriptions.cfg couchpriv_PROGRAMS = couchspawnkillable +if HELP2MAN +dist_man1_MANS = couchjs.1 +endif + +HELP2MAN_OPTION=--no-info --help-option="-h" --version-option="-V" + +couchjs.1: couchjs + touch $@ + if test -x "$(HELP2MAN_EXECUTABLE)"; then \ + $(HELP2MAN_EXECUTABLE) $(HELP2MAN_OPTION) \ + --name="$(package_name) JavaScript interpreter" ./couchjs --output $@; \ + fi + %.cfg: %.cfg.in cp $< $@ diff --git a/src/couchdb/priv/couch_js/help.h b/src/couchdb/priv/couch_js/help.h new file mode 100644 index 00000000..e7c3af0e --- /dev/null +++ b/src/couchdb/priv/couch_js/help.h @@ -0,0 +1,82 @@ +// 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 COUCHJS_HELP_H +#define COUCHJS_HELP_H + +#include + +#include "config.h" + +static const char VERSION_TEMPLATE[] = + "%s - %s\n" + "\n" + "Licensed under the Apache License, Version 2.0 (the \"License\"); you may " + "not use\n" + "this file except in compliance with the License. You may obtain a copy of" + "the\n" + "License at\n" + "\n" + " http://www.apache.org/licenses/LICENSE-2.0\n" + "\n" + "Unless required by applicable law or agreed to in writing, software " + "distributed\n" + "under the License is distributed on an \"AS IS\" BASIS, WITHOUT " + "WARRANTIES OR\n" + "CONDITIONS OF ANY KIND, either express or implied. See the License " + "for the\n" + "specific language governing permissions and limitations under the " + "License.\n"; + +static const char USAGE_TEMPLATE[] = + "Usage: %s [FILE]\n" + "\n" + "The %s command runs the %s JavaScript interpreter.\n" + "\n" + "The exit status is 0 for success or 1 for failure.\n" + "\n" + "Options:\n" + "\n" + " -h, --help display a short help message and exit\n" + " -V, --version display version information and exit\n" + " -H, --http install %s cURL bindings (only avaiable\n" + " if package was built with cURL available)\n" + " --stack-size=SIZE specify that the interpreter should set the\n" + " the stack quota for JS contexts to SIZE bytes\n" + "\n" + "Report bugs at <%s>.\n"; + +#define BASENAME basename((char*)argv[0]) + +#define couch_version(basename) \ + fprintf( \ + stdout, \ + VERSION_TEMPLATE, \ + basename, \ + PACKAGE_STRING) + +#define DISPLAY_VERSION couch_version(BASENAME) + + +#define couch_usage(basename) \ + fprintf( \ + stdout, \ + USAGE_TEMPLATE, \ + basename, \ + basename, \ + PACKAGE_NAME, \ + basename, \ + PACKAGE_BUGREPORT) + +#define DISPLAY_USAGE couch_usage(BASENAME) + +#endif // Included help.h diff --git a/src/couchdb/priv/couch_js/util.c b/src/couchdb/priv/couch_js/util.c index 070d7172..ab1295f8 100644 --- a/src/couchdb/priv/couch_js/util.c +++ b/src/couchdb/priv/couch_js/util.c @@ -15,6 +15,7 @@ #include +#include "help.h" #include "util.h" #include "utf8.h" @@ -79,31 +80,56 @@ couch_parse_args(int argc, const char* argv[]) args->stack_size = 8L * 1024L; while(i < argc) { - if(strcmp("--http", argv[i]) == 0) { + if(strcmp("-h", argv[i]) == 0 || + strcmp("--help", argv[i]) == 0) { + DISPLAY_USAGE; + exit(0); + } else if(strcmp("-V", argv[i]) == 0 || + strcmp("--version", argv[i]) == 0) { + DISPLAY_VERSION; + exit(0); + } else if(strcmp("-H", argv[i]) == 0 || + strcmp("--http", argv[i]) == 0) { args->use_http = 1; - } else if(strcmp("--stack-size", argv[i]) == 0) { - args->stack_size = atoi(argv[i+1]); + } else if(strncmp("--stack-size", argv[i], 12) == 0) { + if(argv[i][12] == '\0') { + args->stack_size = atoi(argv[++i]); + } else if(argv[i][12] == '=') { + args->stack_size = atoi(argv[i]+13); + } else { + DISPLAY_USAGE; + exit(2); + } + if(args->stack_size <= 0) { fprintf(stderr, "Invalid stack size.\n"); exit(2); } + } else if(strcmp("--", argv[i]) == 0) { + i++; + break; } else { - args->script = slurp_file(args->script, argv[i]); - if(args->script_name == NULL) { - if(strcmp(argv[i], "-") == 0) { - args->script_name = ""; - } else { - args->script_name = argv[i]; - } + break; + } + i++; + } + + while(i < argc) { + args->script = slurp_file(args->script, argv[i]); + if(args->script_name == NULL) { + if(strcmp(argv[i], "-") == 0) { + args->script_name = ""; } else { - args->script_name = ""; + args->script_name = argv[i]; } + } else { + args->script_name = ""; } i++; } if(args->script_name == NULL || args->script == NULL) { - fprintf(stderr, "No script provided.\n"); + DISPLAY_USAGE; exit(3); } diff --git a/src/couchdb/priv/couch_js/util.h b/src/couchdb/priv/couch_js/util.h index 6caebfa1..380ff846 100644 --- a/src/couchdb/priv/couch_js/util.h +++ b/src/couchdb/priv/couch_js/util.h @@ -22,7 +22,6 @@ typedef struct { char* script; } couch_args; -void couch_usage(); couch_args* couch_parse_args(int argc, const char* argv[]); int couch_fgets(char* buf, int size, FILE* fp); JSString* couch_readline(JSContext* cx, FILE* fp); -- cgit v1.2.3 From 76d90953bb6e16aec214549d5719b6b7e5fbedfc Mon Sep 17 00:00:00 2001 From: Randall Leeds Date: Sun, 2 Oct 2011 04:31:14 -0700 Subject: distclean couchjs.1 help2man file --- src/couchdb/priv/Makefile.am | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/couchdb/priv/Makefile.am b/src/couchdb/priv/Makefile.am index 130da9f4..b55b5905 100644 --- a/src/couchdb/priv/Makefile.am +++ b/src/couchdb/priv/Makefile.am @@ -14,6 +14,10 @@ couchlibdir = $(localerlanglibdir)/couch-$(version) couchprivdir = $(couchlibdir)/priv couchprivlibdir = $(couchlibdir)/priv/lib +if HELP2MAN +dist_man1_MANS = couchjs.1 +endif + EXTRA_DIST = \ spawnkillable/couchspawnkillable.sh \ stat_descriptions.cfg.in \ @@ -21,7 +25,7 @@ EXTRA_DIST = \ couch_js/sm180.c \ couch_js/sm185.c -CLEANFILES = stat_descriptions.cfg +CLEANFILES = $(dist_man1_MANS) stat_descriptions.cfg ICU_LOCAL_FLAGS = $(ICU_LOCAL_CFLAGS) $(ICU_LOCAL_LDFLAGS) if WINDOWS @@ -59,10 +63,6 @@ couchjs_LDADD = $(CURL_LDFLAGS) @JSLIB@ couchpriv_DATA = stat_descriptions.cfg couchpriv_PROGRAMS = couchspawnkillable -if HELP2MAN -dist_man1_MANS = couchjs.1 -endif - HELP2MAN_OPTION=--no-info --help-option="-h" --version-option="-V" couchjs.1: couchjs -- cgit v1.2.3 From f2211f21f2da9bf78845d3da6d6cb784dd8f1464 Mon Sep 17 00:00:00 2001 From: Randall Leeds Date: Sun, 2 Oct 2011 14:04:27 -0700 Subject: fix couchjs help language and use short options --- src/couchdb/priv/couch_js/help.h | 12 ++++++------ src/couchdb/priv/couch_js/util.c | 21 +++++---------------- test/javascript/run.tpl | 2 +- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/src/couchdb/priv/couch_js/help.h b/src/couchdb/priv/couch_js/help.h index e7c3af0e..84c83c06 100644 --- a/src/couchdb/priv/couch_js/help.h +++ b/src/couchdb/priv/couch_js/help.h @@ -46,12 +46,12 @@ static const char USAGE_TEMPLATE[] = "\n" "Options:\n" "\n" - " -h, --help display a short help message and exit\n" - " -V, --version display version information and exit\n" - " -H, --http install %s cURL bindings (only avaiable\n" - " if package was built with cURL available)\n" - " --stack-size=SIZE specify that the interpreter should set the\n" - " the stack quota for JS contexts to SIZE bytes\n" + " -h display a short help message and exit\n" + " -V display version information and exit\n" + " -H enable %s cURL bindings (only avaiable\n" + " if package was built with cURL available)\n" + " -S SIZE specify that the interpreter should set the\n" + " the stack quota for JS contexts to SIZE bytes\n" "\n" "Report bugs at <%s>.\n"; diff --git a/src/couchdb/priv/couch_js/util.c b/src/couchdb/priv/couch_js/util.c index ab1295f8..3076856a 100644 --- a/src/couchdb/priv/couch_js/util.c +++ b/src/couchdb/priv/couch_js/util.c @@ -80,27 +80,16 @@ couch_parse_args(int argc, const char* argv[]) args->stack_size = 8L * 1024L; while(i < argc) { - if(strcmp("-h", argv[i]) == 0 || - strcmp("--help", argv[i]) == 0) { + if(strcmp("-h", argv[i]) == 0) { DISPLAY_USAGE; exit(0); - } else if(strcmp("-V", argv[i]) == 0 || - strcmp("--version", argv[i]) == 0) { + } else if(strcmp("-V", argv[i]) == 0) { DISPLAY_VERSION; exit(0); - } else if(strcmp("-H", argv[i]) == 0 || - strcmp("--http", argv[i]) == 0) { + } else if(strcmp("-H", argv[i]) == 0) { args->use_http = 1; - } else if(strncmp("--stack-size", argv[i], 12) == 0) { - if(argv[i][12] == '\0') { - args->stack_size = atoi(argv[++i]); - } else if(argv[i][12] == '=') { - args->stack_size = atoi(argv[i]+13); - } else { - DISPLAY_USAGE; - exit(2); - } - + } else if(strcmp("-S", argv[i]) == 0) { + args->stack_size = atoi(argv[++i]); if(args->stack_size <= 0) { fprintf(stderr, "Invalid stack size.\n"); exit(2); diff --git a/test/javascript/run.tpl b/test/javascript/run.tpl index 1389a4f9..5f971581 100644 --- a/test/javascript/run.tpl +++ b/test/javascript/run.tpl @@ -27,4 +27,4 @@ cat $SCRIPT_DIR/json2.js \ $SCRIPT_DIR/test/*.js \ $JS_TEST_DIR/couch_http.js \ $JS_TEST_DIR/cli_runner.js \ - | $COUCHJS --http - + | $COUCHJS -H - -- cgit v1.2.3 From 6ee2a4879e809e9be5cd551902d90c663195b75b Mon Sep 17 00:00:00 2001 From: Robert Newson Date: Wed, 5 Oct 2011 13:50:50 +0100 Subject: update CHANGES and NEWS to include a one line summary for every ticket resolved in 1.1.1 --- CHANGES | 18 ++++++++++++++++++ NEWS | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/CHANGES b/CHANGES index 64b4f3c1..aef2c9ea 100644 --- a/CHANGES +++ b/CHANGES @@ -6,8 +6,26 @@ Version 1.1.1 This version has not been released yet. +* Add configurable maximum to the number of bytes returned by _log. +* Allow CommonJS modules to be an empty string. +* Bump minimum Erlang version to R13B02. +* Do not run deleted validate_doc_update functions. * ETags for views include current sequence if include_docs=true. +* Fix bug where duplicates can appear in _changes feed. +* Fix bug where update handlers break after conflict resolution. +* Fix bug with _replicator where include "filter" could crash couch. +* Fix crashes when compacting large views. +* Fix file descriptor leak in _log +* Fix missing revisions in _changes?style=all_docs. +* Improve handling of compaction at max_dbs_open limit. * JSONP responses now send "text/javascript" for Content-Type. +* Link to ICU 4.2 on Windows. +* Permit forward slashes in path to update functions. +* Reap couchjs processes that hit reduce_overflow error. +* Status code can be specified in update handlers. +* Support provides() in show functions. +* _view_cleanup when ddoc has no views now removes all index files. +* max_replication_retry_count now supports "infinity". Version 1.1.0 ------------- diff --git a/NEWS b/NEWS index d7dc7cf1..62ab43d0 100644 --- a/NEWS +++ b/NEWS @@ -12,8 +12,26 @@ Version 1.1.1 This version has not been released yet. +* Add configurable maximum to the number of bytes returned by _log. +* Allow CommonJS modules to be an empty string. +* Bump minimum Erlang version to R13B02. +* Do not run deleted validate_doc_update functions. * ETags for views include current sequence if include_docs=true. +* Fix bug where duplicates can appear in _changes feed. +* Fix bug where update handlers break after conflict resolution. +* Fix bug with _replicator where include "filter" could crash couch. +* Fix crashes when compacting large views. +* Fix file descriptor leak in _log +* Fix missing revisions in _changes?style=all_docs. +* Improve handling of compaction at max_dbs_open limit. * JSONP responses now send "text/javascript" for Content-Type. +* Link to ICU 4.2 on Windows. +* Permit forward slashes in path to update functions. +* Reap couchjs processes that hit reduce_overflow error. +* Status code can be specified in update handlers. +* Support provides() in show functions. +* _view_cleanup when ddoc has no views now removes all index files. +* max_replication_retry_count now supports "infinity". Version 1.1.0 ------------- -- cgit v1.2.3 From e6c0fc955ea2be0fd3c4e894fd0c7cc87202c5a5 Mon Sep 17 00:00:00 2001 From: Filipe David Manana Date: Wed, 5 Oct 2011 14:58:25 +0100 Subject: Update CHANGES and NEWS for the 1.1.1 release --- CHANGES | 3 +++ NEWS | 3 +++ 2 files changed, 6 insertions(+) diff --git a/CHANGES b/CHANGES index aef2c9ea..0c619ab9 100644 --- a/CHANGES +++ b/CHANGES @@ -26,6 +26,9 @@ This version has not been released yet. * Support provides() in show functions. * _view_cleanup when ddoc has no views now removes all index files. * max_replication_retry_count now supports "infinity". +* Fix replication crash when source database has a document with empty ID. +* Fix deadlock when assigning couchjs processes to serve requests. +* Fixes to the document multipart PUT API. Version 1.1.0 ------------- diff --git a/NEWS b/NEWS index 62ab43d0..13e6ac56 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,9 @@ This version has not been released yet. * Support provides() in show functions. * _view_cleanup when ddoc has no views now removes all index files. * max_replication_retry_count now supports "infinity". +* Fix replication crash when source database has a document with empty ID. +* Fix deadlock when assigning couchjs processes to serve requests. +* Fixes to the document multipart PUT API. Version 1.1.0 ------------- -- cgit v1.2.3 From 5b558c81ed9709fb286a6821e9ae6d6478012c2c Mon Sep 17 00:00:00 2001 From: Robert Newson Date: Thu, 6 Oct 2011 10:39:21 +0100 Subject: Remove SpiderMonkey 1.8.5 compatibility This commit reverts 1.8.5 compatibility including the corresponding couchjs paren hack as this leads to significant breakage in existing functions. --- configure.ac | 41 ++--- share/server/mimeparse.js | 2 +- share/server/util.js | 5 - share/www/script/couch_test_runner.js | 15 +- src/couchdb/priv/Makefile.am | 11 +- src/couchdb/priv/couch_js/http.c | 318 +++++++++++++++++++++------------ src/couchdb/priv/couch_js/http.h | 10 +- src/couchdb/priv/couch_js/main.c | 327 +++++++++++++++++++++++++++++++++- src/couchdb/priv/couch_js/utf8.c | 9 +- 9 files changed, 555 insertions(+), 183 deletions(-) diff --git a/configure.ac b/configure.ac index 5124b8b1..907fc6a2 100644 --- a/configure.ac +++ b/configure.ac @@ -109,15 +109,13 @@ esac AM_CONDITIONAL([WINDOWS], [test x$IS_WINDOWS = xTRUE]) -AC_CHECK_LIB([mozjs185], [JS_NewContext], [JS_LIB_BASE=mozjs185], [ - AC_CHECK_LIB([mozjs185-1.0], [JS_NewContext], [JS_LIB_BASE=mozjs185-1.0], [ - AC_CHECK_LIB([mozjs], [JS_NewContext], [JS_LIB_BASE=mozjs], [ - AC_CHECK_LIB([js], [JS_NewContext], [JS_LIB_BASE=js], [ - AC_CHECK_LIB([js3250], [JS_NewContext], [JS_LIB_BASE=js3250], [ - AC_CHECK_LIB([js32], [JS_NewContext], [JS_LIB_BASE=js32], [ - AC_MSG_ERROR([Could not find the js library. +AC_CHECK_LIB([mozjs], [JS_NewContext], [JS_LIB_BASE=mozjs], [ + AC_CHECK_LIB([js], [JS_NewContext], [JS_LIB_BASE=js], [ + AC_CHECK_LIB([js3250], [JS_NewContext], [JS_LIB_BASE=js3250], [ + AC_CHECK_LIB([js32], [JS_NewContext], [JS_LIB_BASE=js32], [ + AC_MSG_ERROR([Could not find the js library. -Is the Mozilla SpiderMonkey library installed?])])])])])])]) +Is the Mozilla SpiderMonkey library installed?])])])])]) AC_SUBST(JS_LIB_BASE) @@ -180,19 +178,16 @@ Are the Mozilla SpiderMonkey headers installed?]) AC_SUBST(JSLIB) AC_LANG_PUSH(C) - -AC_CHECK_LIB([$JS_LIB_BASE], [JS_NewCompartmentAndGlobalObject], - AC_DEFINE([SM185], [1], - [Use SpiderMonkey 1.8.5])) - -AC_CHECK_LIB([$JS_LIB_BASE], [JS_ThrowStopIteration], - AC_DEFINE([SM180], [1], - [Use SpiderMonkey 1.8.0])) - -AC_CHECK_LIB([$JS_LIB_BASE], [JS_GetStringCharsAndLength], - AC_DEFINE([HAVE_JS_GET_STRING_CHARS_AND_LENGTH], [1], - [Use newer JS_GetCharsAndLength function.])) - +OLD_CFLAGS="$CFLAGS" +CFLAGS="-Werror-implicit-function-declaration" +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[JS_SetOperationCallback(0, 0);]] + )], + AC_DEFINE([USE_JS_SETOPCB], [], [Use new JS_SetOperationCallback]) +) +CFLAGS="$OLD_CFLAGS" AC_LANG_POP(C) AC_ARG_WITH([win32-icu-binaries], [AC_HELP_STRING([--with-win32-icu-binaries=PATH], @@ -234,10 +229,10 @@ case "$(uname -s)" in CPPFLAGS="-D_XOPEN_SOURCE $CPPFLAGS" ;; FreeBSD) - LIBS="$LIBS -lm -lcrypt" + LIBS="$LIBS -lcrypt" ;; OpenBSD) - LIBS="$LIBS -lm -lcrypto" + LIBS="$LIBS -lcrypto" ;; esac diff --git a/share/server/mimeparse.js b/share/server/mimeparse.js index 42b600fa..3642a194 100644 --- a/share/server/mimeparse.js +++ b/share/server/mimeparse.js @@ -97,7 +97,7 @@ var Mimeparse = (function() { if ((type == targetType || type == "*" || targetType == "*") && (subtype == targetSubtype || subtype == "*" || targetSubtype == "*")) { var matchCount = 0; - for (var param in targetParams) { + for (param in targetParams) { if (param != 'q' && params[param] && params[param] == targetParams[param]) { matchCount += 1; } diff --git a/share/server/util.js b/share/server/util.js index f6fa60bb..0b812fe1 100644 --- a/share/server/util.js +++ b/share/server/util.js @@ -63,11 +63,6 @@ var Couch = { }, compileFunction : function(source, ddoc) { if (!source) throw(["error","not_found","missing function"]); - // Some newer SpiderMonkey's appear to not like evaluating - // an anonymous function at global scope. Simple fix just - // wraps the source with parens so the function object is - // returned correctly. - source = "(" + source + ")"; try { if (sandbox) { if (ddoc) { diff --git a/share/www/script/couch_test_runner.js b/share/www/script/couch_test_runner.js index e14640b6..55a6533f 100644 --- a/share/www/script/couch_test_runner.js +++ b/share/www/script/couch_test_runner.js @@ -414,22 +414,9 @@ function waitForSuccess(fun, tag) { function waitForRestart() { var waiting = true; - // Wait for the server to go down but don't - // wait too long because we might miss the - // unavailable period. - var count = 25; - while (waiting && count > 0) { - count--; - try { - CouchDB.request("GET", "/"); - } catch(e) { - waiting = false; - } - } - // Wait for it to come back up - waiting = true; while (waiting) { try { + CouchDB.request("GET", "/"); CouchDB.request("GET", "/"); waiting = false; } catch(e) { diff --git a/src/couchdb/priv/Makefile.am b/src/couchdb/priv/Makefile.am index b55b5905..ae1b3e38 100644 --- a/src/couchdb/priv/Makefile.am +++ b/src/couchdb/priv/Makefile.am @@ -20,10 +20,7 @@ endif EXTRA_DIST = \ spawnkillable/couchspawnkillable.sh \ - stat_descriptions.cfg.in \ - couch_js/sm170.c \ - couch_js/sm180.c \ - couch_js/sm185.c + stat_descriptions.cfg.in CLEANFILES = $(dist_man1_MANS) stat_descriptions.cfg @@ -50,14 +47,12 @@ COUCHJS_SRCS = \ couch_js/http.h \ couch_js/main.c \ couch_js/utf8.c \ - couch_js/utf8.h \ - couch_js/util.h \ - couch_js/util.c + couch_js/utf8.h locallibbin_PROGRAMS = couchjs couchjs_SOURCES = $(COUCHJS_SRCS) couchjs_LDFLAGS = $(CURL_LDFLAGS) -couchjs_CFLAGS = -g -Wall -Werror -D_BSD_SOURCE $(CURL_CFLAGS) +couchjs_CFLAGS = -D_BSD_SOURCE $(CURL_CFLAGS) couchjs_LDADD = $(CURL_LDFLAGS) @JSLIB@ couchpriv_DATA = stat_descriptions.cfg diff --git a/src/couchdb/priv/couch_js/http.c b/src/couchdb/priv/couch_js/http.c index 77078e35..6c2a8a82 100644 --- a/src/couchdb/priv/couch_js/http.c +++ b/src/couchdb/priv/couch_js/http.c @@ -14,31 +14,19 @@ #include #include #include -#include "config.h" -#include "utf8.h" - - #include +#include "utf8.h" -void -http_check_enabled() -{ - return; -} - - -// Map some of the string function names to things which exist on Windows #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; @@ -46,10 +34,8 @@ typedef struct { jsint last_status; } HTTPData; - char* METHODS[] = {"GET", "HEAD", "POST", "PUT", "DELETE", "COPY", NULL}; - #define GET 0 #define HEAD 1 #define POST 2 @@ -57,17 +43,14 @@ char* METHODS[] = {"GET", "HEAD", "POST", "PUT", "DELETE", "COPY", NULL}; #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); - -JSBool -http_ctor(JSContext* cx, JSObject* req) +static JSBool +constructor(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) { HTTPData* http = NULL; JSBool ret = JS_FALSE; @@ -84,12 +67,12 @@ http_ctor(JSContext* cx, JSObject* req) http->req_headers = NULL; http->last_status = -1; - if(!JS_SetPrivate(cx, req, http)) + if(!JS_SetPrivate(cx, obj, http)) { JS_ReportError(cx, "Failed to set private CouchHTTP data."); goto error; } - + ret = JS_TRUE; goto success; @@ -100,76 +83,90 @@ success: return ret; } - -void -http_dtor(JSContext* cx, JSObject* obj) +static void +destructor(JSContext* cx, JSObject* obj) { HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj); - if(http) { + 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); } } - -JSBool -http_open(JSContext* cx, JSObject* req, jsval mth, jsval url, jsval snc) -{ - HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req); +static JSBool +open(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) +{ + HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj); char* method = NULL; - int methid; + char* url = NULL; JSBool ret = JS_FALSE; + int methid; - if(!http) { + if(!http) + { JS_ReportError(cx, "Invalid CouchHTTP instance."); goto done; } - if(mth == JSVAL_VOID) { + if(argv[0] == JSVAL_VOID) + { JS_ReportError(cx, "You must specify a method."); goto done; } - method = enc_string(cx, mth, NULL); - if(!method) { + 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++) { + for(methid = 0; METHODS[methid] != NULL; methid++) + { if(strcasecmp(METHODS[methid], method) == 0) break; } - if(methid > COPY) { + if(methid > COPY) + { JS_ReportError(cx, "Invalid method specified."); goto done; } http->method = methid; - if(url == JSVAL_VOID) { + if(argv[1] == JSVAL_VOID) + { JS_ReportError(cx, "You must specify a URL."); goto done; } - if(http->url != NULL) { + if(http->url) + { free(http->url); http->url = NULL; } - http->url = enc_string(cx, url, NULL); - if(http->url == NULL) { + http->url = enc_string(cx, argv[1], NULL); + if(!http->url) + { JS_ReportError(cx, "Failed to encode URL."); goto done; } - if(snc != JSVAL_FALSE) { - JS_ReportError(cx, "Synchronous flag must be false."); + 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) { + if(http->req_headers) + { curl_slist_free_all(http->req_headers); http->req_headers = NULL; } @@ -184,42 +181,42 @@ done: return ret; } - -JSBool -http_set_hdr(JSContext* cx, JSObject* req, jsval name, jsval val) -{ - HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req); +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) { + if(!http) + { JS_ReportError(cx, "Invalid CouchHTTP instance."); goto done; } - if(name == JSVAL_VOID) + if(argv[0] == JSVAL_VOID) { JS_ReportError(cx, "You must speciy a header name."); goto done; } - keystr = enc_string(cx, name, NULL); + keystr = enc_string(cx, argv[0], NULL); if(!keystr) { JS_ReportError(cx, "Failed to encode header name."); goto done; } - if(val == JSVAL_VOID) + if(argv[1] == JSVAL_VOID) { JS_ReportError(cx, "You must specify a header value."); goto done; } - valstr = enc_string(cx, val, NULL); + valstr = enc_string(cx, argv[1], NULL); if(!valstr) { JS_ReportError(cx, "Failed to encode header value."); @@ -228,7 +225,8 @@ http_set_hdr(JSContext* cx, JSObject* req, jsval name, jsval val) hdrlen = strlen(keystr) + strlen(valstr) + 3; hdrbuf = (char*) malloc(hdrlen * sizeof(char)); - if(!hdrbuf) { + if(!hdrbuf) + { JS_ReportError(cx, "Failed to allocate header buffer."); goto done; } @@ -242,50 +240,121 @@ done: if(keystr) free(keystr); if(valstr) free(valstr); if(hdrbuf) free(hdrbuf); + return ret; } -JSBool -http_send(JSContext* cx, JSObject* req, jsval body) +static JSBool +sendreq(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) { - HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req); - char* bodystr = NULL; + HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj); + char* body = NULL; size_t bodylen = 0; JSBool ret = JS_FALSE; - if(!http) { + if(!http) + { JS_ReportError(cx, "Invalid CouchHTTP instance."); goto done; } - if(body != JSVAL_VOID && body != JS_GetEmptyStringValue(cx)) { - bodystr = enc_string(cx, body, &bodylen); - if(!bodystr) { + 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, req, http, bodystr, bodylen); + ret = go(cx, obj, http, body, bodylen); done: - if(bodystr) free(bodystr); + if(body) free(body); return ret; } -int -http_status(JSContext* cx, JSObject* req) +static JSBool +status(JSContext* cx, JSObject* obj, jsval idval, jsval* vp) { - HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req); + HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj); - if(!http) { + 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; - return http->last_status; + 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 { @@ -295,7 +364,6 @@ typedef struct { char* sendbuf; size_t sendlen; size_t sent; - int sent_once; char* recvbuf; size_t recvlen; size_t read; @@ -327,13 +395,13 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen) state.sendbuf = body; state.sendlen = bodylen; state.sent = 0; - state.sent_once = 0; state.recvbuf = NULL; state.recvlen = 0; state.read = 0; - if(HTTP_HANDLE == NULL) { + 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, @@ -348,12 +416,14 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen) "CouchHTTP Client - Relax"); } - if(!HTTP_HANDLE) { + if(!HTTP_HANDLE) + { JS_ReportError(cx, "Failed to initialize cURL handle."); goto done; } - if(http->method < 0 || http->method > COPY) { + if(http->method < 0 || http->method > COPY) + { JS_ReportError(cx, "INTERNAL: Unknown method."); goto done; } @@ -363,21 +433,27 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen) curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(HTTP_HANDLE, CURLOPT_UPLOAD, 0); - if(http->method == HEAD) { + 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) { + } + 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) { + if(body && bodylen) + { curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, bodylen); - } else { + } + else + { curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, 0); } - // curl_easy_setopt(HTTP_HANDLE, CURLOPT_VERBOSE, 1); + //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); @@ -386,32 +462,39 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen) curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEHEADER, &state); curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEDATA, &state); - if(curl_easy_perform(HTTP_HANDLE) != 0) { + if(curl_easy_perform(HTTP_HANDLE) != 0) + { JS_ReportError(cx, "Failed to execute HTTP request: %s", ERRBUF); goto done; } - if(!state.resp_headers) { + 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, + cx, + obj, "_headers", tmp, - NULL, NULL, + NULL, + NULL, JSPROP_READONLY - )) { + )) + { JS_ReportError(cx, "INTERNAL: Failed to set response headers."); goto done; } - if(state.recvbuf) { + if(state.recvbuf) // Is good enough? + { state.recvbuf[state.read] = '\0'; jsbody = dec_string(cx, state.recvbuf, state.read+1); - if(!jsbody) { + 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. @@ -424,17 +507,22 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen) } } tmp = STRING_TO_JSVAL(jsbody); - } else { + } + else + { tmp = JS_GetEmptyStringValue(cx); } if(!JS_DefineProperty( - cx, obj, + cx, + obj, "responseText", tmp, - NULL, NULL, + NULL, + NULL, JSPROP_READONLY - )) { + )) + { JS_ReportError(cx, "INTERNAL: Failed to set responseText."); goto done; } @@ -452,20 +540,15 @@ send_body(void *ptr, size_t size, size_t nmem, void *data) CurlState* state = (CurlState*) data; size_t length = size * nmem; size_t towrite = state->sendlen - state->sent; - - // Assume this is cURL trying to resend a request that - // failed. - if(towrite == 0 && state->sent_once == 0) { - state->sent_once = 1; + if(towrite == 0) + { return 0; - } else if(towrite == 0) { - state->sent = 0; - state->sent_once = 0; - towrite = state->sendlen; } 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; @@ -489,12 +572,15 @@ recv_header(void *ptr, size_t size, size_t nmem, void *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) { + if(length > 7 && strncasecmp(header, "HTTP/1.", 7) == 0) + { + if(length < 12) + { return CURLE_WRITE_ERROR; } @@ -503,7 +589,8 @@ recv_header(void *ptr, size_t size, size_t nmem, void *data) state->http->last_status = atoi(code); state->resp_headers = JS_NewArrayObject(state->cx, 0, NULL); - if(!state->resp_headers) { + if(!state->resp_headers) + { return CURLE_WRITE_ERROR; } @@ -511,22 +598,26 @@ recv_header(void *ptr, size_t size, size_t nmem, void *data) } // We get a notice at the \r\n\r\n after headers. - if(length <= 2) { + if(length <= 2) + { return length; } // Append the new header to our array. hdr = dec_string(state->cx, header, length); - if(!hdr) { + if(!hdr) + { return CURLE_WRITE_ERROR; } - if(!JS_GetArrayLength(state->cx, state->resp_headers, &hdrlen)) { + 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)) { + if(!JS_SetElement(state->cx, state->resp_headers, hdrlen, &hdrval)) + { return CURLE_WRITE_ERROR; } @@ -540,13 +631,15 @@ recv_body(void *ptr, size_t size, size_t nmem, void *data) size_t length = size * nmem; char* tmp = NULL; - if(!state->recvbuf) { + if(!state->recvbuf) + { state->recvlen = 4096; state->read = 0; state->recvbuf = JS_malloc(state->cx, state->recvlen); } - if(!state->recvbuf) { + if(!state->recvbuf) + { return CURLE_WRITE_ERROR; } @@ -570,7 +663,8 @@ str_from_binary(JSContext* cx, char* data, size_t length) if(!conv) return NULL; - for(i = 0; i < length; i++) { + for(i = 0; i < length; i++) + { conv[i] = (jschar) data[i]; } diff --git a/src/couchdb/priv/couch_js/http.h b/src/couchdb/priv/couch_js/http.h index 373d1e48..b5f8c70f 100644 --- a/src/couchdb/priv/couch_js/http.h +++ b/src/couchdb/priv/couch_js/http.h @@ -13,12 +13,6 @@ #ifndef COUCH_JS_HTTP_H #define COUCH_JS_HTTP_H -void http_check_enabled(); -JSBool http_ctor(JSContext* cx, JSObject* req); -void http_dtor(JSContext* cx, JSObject* req); -JSBool http_open(JSContext* cx, JSObject* req, jsval mth, jsval url, jsval snc); -JSBool http_set_hdr(JSContext* cx, JSObject* req, jsval name, jsval val); -JSBool http_send(JSContext* cx, JSObject* req, jsval body); -int http_status(JSContext* cx, JSObject* req); +JSObject* install_http(JSContext* cx, JSObject* global); -#endif +#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 index 209bb023..376aa15b 100644 --- a/src/couchdb/priv/couch_js/main.c +++ b/src/couchdb/priv/couch_js/main.c @@ -10,12 +10,329 @@ // License for the specific language governing permissions and limitations under // the License. +#include +#include +#include +#include #include "config.h" -#if defined(SM185) -#include "sm185.c" -#elif defined(SM180) -#include "sm180.c" +#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 -#include "sm170.c" +#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 \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 index d6064267..699a6fee 100644 --- a/src/couchdb/priv/couch_js/utf8.c +++ b/src/couchdb/priv/couch_js/utf8.c @@ -11,7 +11,6 @@ // the License. #include -#include "config.h" static int enc_char(uint8 *utf8Buffer, uint32 ucs4Char) @@ -122,7 +121,7 @@ char* enc_string(JSContext* cx, jsval arg, size_t* buflen) { JSString* str = NULL; - const jschar* src = NULL; + jschar* src = NULL; char* bytes = NULL; size_t srclen = 0; size_t byteslen = 0; @@ -130,12 +129,8 @@ enc_string(JSContext* cx, jsval arg, size_t* buflen) str = JS_ValueToString(cx, arg); if(!str) goto error; -#ifdef HAVE_JS_GET_STRING_CHARS_AND_LENGTH - src = JS_GetStringCharsAndLength(cx, str, &srclen); -#else src = JS_GetStringChars(str); srclen = JS_GetStringLength(str); -#endif if(!enc_charbuf(src, srclen, NULL, &byteslen)) goto error; @@ -288,4 +283,4 @@ error: success: return str; -} +} \ No newline at end of file -- cgit v1.2.3 From 4818fdd3269a3082a193ec69f4e8e39d95c15e86 Mon Sep 17 00:00:00 2001 From: Robert Newson Date: Thu, 6 Oct 2011 10:46:04 +0100 Subject: fix shadowing warning. --- src/couchdb/couch_doc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/couchdb/couch_doc.erl b/src/couchdb/couch_doc.erl index 01e92836..f55e951b 100644 --- a/src/couchdb/couch_doc.erl +++ b/src/couchdb/couch_doc.erl @@ -313,7 +313,7 @@ max_seq(Tree, UpdateSeq) -> end, couch_key_tree:fold(FoldFun, UpdateSeq, Tree). -to_doc_info_path(#full_doc_info{id=Id,rev_tree=Tree,update_seq=Seq}) -> +to_doc_info_path(#full_doc_info{id=Id,rev_tree=Tree,update_seq=Seq0}) -> RevInfosAndPath = [{#rev_info{deleted=Del,body_sp=Bp,seq=Seq,rev={Pos,RevId}}, Path} || {{Del, Bp, Seq},{Pos, [RevId|_]}=Path} <- @@ -326,7 +326,7 @@ to_doc_info_path(#full_doc_info{id=Id,rev_tree=Tree,update_seq=Seq}) -> end, RevInfosAndPath), [{_RevInfo, WinPath}|_] = SortedRevInfosAndPath, RevInfos = [RevInfo || {RevInfo, _Path} <- SortedRevInfosAndPath], - {#doc_info{id=Id, high_seq=max_seq(Tree, Seq), revs=RevInfos}, WinPath}. + {#doc_info{id=Id, high_seq=max_seq(Tree, Seq0), revs=RevInfos}, WinPath}. -- cgit v1.2.3 From a628a43b58c7f85e228b148cea5d74a8b24bff70 Mon Sep 17 00:00:00 2001 From: Robert Newson Date: Thu, 6 Oct 2011 20:49:17 +0100 Subject: Revert "Remove SpiderMonkey 1.8.5 compatibility" This reverts commit 5b558c81ed9709fb286a6821e9ae6d6478012c2c. --- configure.ac | 41 +++-- share/server/mimeparse.js | 2 +- share/server/util.js | 5 + share/www/script/couch_test_runner.js | 15 +- src/couchdb/priv/Makefile.am | 11 +- src/couchdb/priv/couch_js/http.c | 318 ++++++++++++--------------------- src/couchdb/priv/couch_js/http.h | 10 +- src/couchdb/priv/couch_js/main.c | 327 +--------------------------------- src/couchdb/priv/couch_js/utf8.c | 9 +- 9 files changed, 183 insertions(+), 555 deletions(-) diff --git a/configure.ac b/configure.ac index 907fc6a2..5124b8b1 100644 --- a/configure.ac +++ b/configure.ac @@ -109,13 +109,15 @@ esac AM_CONDITIONAL([WINDOWS], [test x$IS_WINDOWS = xTRUE]) -AC_CHECK_LIB([mozjs], [JS_NewContext], [JS_LIB_BASE=mozjs], [ - AC_CHECK_LIB([js], [JS_NewContext], [JS_LIB_BASE=js], [ - AC_CHECK_LIB([js3250], [JS_NewContext], [JS_LIB_BASE=js3250], [ - AC_CHECK_LIB([js32], [JS_NewContext], [JS_LIB_BASE=js32], [ - AC_MSG_ERROR([Could not find the js library. +AC_CHECK_LIB([mozjs185], [JS_NewContext], [JS_LIB_BASE=mozjs185], [ + AC_CHECK_LIB([mozjs185-1.0], [JS_NewContext], [JS_LIB_BASE=mozjs185-1.0], [ + AC_CHECK_LIB([mozjs], [JS_NewContext], [JS_LIB_BASE=mozjs], [ + AC_CHECK_LIB([js], [JS_NewContext], [JS_LIB_BASE=js], [ + AC_CHECK_LIB([js3250], [JS_NewContext], [JS_LIB_BASE=js3250], [ + AC_CHECK_LIB([js32], [JS_NewContext], [JS_LIB_BASE=js32], [ + AC_MSG_ERROR([Could not find the js library. -Is the Mozilla SpiderMonkey library installed?])])])])]) +Is the Mozilla SpiderMonkey library installed?])])])])])])]) AC_SUBST(JS_LIB_BASE) @@ -178,16 +180,19 @@ Are the Mozilla SpiderMonkey headers installed?]) AC_SUBST(JSLIB) AC_LANG_PUSH(C) -OLD_CFLAGS="$CFLAGS" -CFLAGS="-Werror-implicit-function-declaration" -AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM( - [[#include ]], - [[JS_SetOperationCallback(0, 0);]] - )], - AC_DEFINE([USE_JS_SETOPCB], [], [Use new JS_SetOperationCallback]) -) -CFLAGS="$OLD_CFLAGS" + +AC_CHECK_LIB([$JS_LIB_BASE], [JS_NewCompartmentAndGlobalObject], + AC_DEFINE([SM185], [1], + [Use SpiderMonkey 1.8.5])) + +AC_CHECK_LIB([$JS_LIB_BASE], [JS_ThrowStopIteration], + AC_DEFINE([SM180], [1], + [Use SpiderMonkey 1.8.0])) + +AC_CHECK_LIB([$JS_LIB_BASE], [JS_GetStringCharsAndLength], + AC_DEFINE([HAVE_JS_GET_STRING_CHARS_AND_LENGTH], [1], + [Use newer JS_GetCharsAndLength function.])) + AC_LANG_POP(C) AC_ARG_WITH([win32-icu-binaries], [AC_HELP_STRING([--with-win32-icu-binaries=PATH], @@ -229,10 +234,10 @@ case "$(uname -s)" in CPPFLAGS="-D_XOPEN_SOURCE $CPPFLAGS" ;; FreeBSD) - LIBS="$LIBS -lcrypt" + LIBS="$LIBS -lm -lcrypt" ;; OpenBSD) - LIBS="$LIBS -lcrypto" + LIBS="$LIBS -lm -lcrypto" ;; esac diff --git a/share/server/mimeparse.js b/share/server/mimeparse.js index 3642a194..42b600fa 100644 --- a/share/server/mimeparse.js +++ b/share/server/mimeparse.js @@ -97,7 +97,7 @@ var Mimeparse = (function() { if ((type == targetType || type == "*" || targetType == "*") && (subtype == targetSubtype || subtype == "*" || targetSubtype == "*")) { var matchCount = 0; - for (param in targetParams) { + for (var param in targetParams) { if (param != 'q' && params[param] && params[param] == targetParams[param]) { matchCount += 1; } diff --git a/share/server/util.js b/share/server/util.js index 0b812fe1..f6fa60bb 100644 --- a/share/server/util.js +++ b/share/server/util.js @@ -63,6 +63,11 @@ var Couch = { }, compileFunction : function(source, ddoc) { if (!source) throw(["error","not_found","missing function"]); + // Some newer SpiderMonkey's appear to not like evaluating + // an anonymous function at global scope. Simple fix just + // wraps the source with parens so the function object is + // returned correctly. + source = "(" + source + ")"; try { if (sandbox) { if (ddoc) { diff --git a/share/www/script/couch_test_runner.js b/share/www/script/couch_test_runner.js index 55a6533f..e14640b6 100644 --- a/share/www/script/couch_test_runner.js +++ b/share/www/script/couch_test_runner.js @@ -414,9 +414,22 @@ function waitForSuccess(fun, tag) { function waitForRestart() { var waiting = true; - while (waiting) { + // Wait for the server to go down but don't + // wait too long because we might miss the + // unavailable period. + var count = 25; + while (waiting && count > 0) { + count--; try { CouchDB.request("GET", "/"); + } catch(e) { + waiting = false; + } + } + // Wait for it to come back up + waiting = true; + while (waiting) { + try { CouchDB.request("GET", "/"); waiting = false; } catch(e) { diff --git a/src/couchdb/priv/Makefile.am b/src/couchdb/priv/Makefile.am index ae1b3e38..b55b5905 100644 --- a/src/couchdb/priv/Makefile.am +++ b/src/couchdb/priv/Makefile.am @@ -20,7 +20,10 @@ endif EXTRA_DIST = \ spawnkillable/couchspawnkillable.sh \ - stat_descriptions.cfg.in + stat_descriptions.cfg.in \ + couch_js/sm170.c \ + couch_js/sm180.c \ + couch_js/sm185.c CLEANFILES = $(dist_man1_MANS) stat_descriptions.cfg @@ -47,12 +50,14 @@ COUCHJS_SRCS = \ couch_js/http.h \ couch_js/main.c \ couch_js/utf8.c \ - couch_js/utf8.h + couch_js/utf8.h \ + couch_js/util.h \ + couch_js/util.c locallibbin_PROGRAMS = couchjs couchjs_SOURCES = $(COUCHJS_SRCS) couchjs_LDFLAGS = $(CURL_LDFLAGS) -couchjs_CFLAGS = -D_BSD_SOURCE $(CURL_CFLAGS) +couchjs_CFLAGS = -g -Wall -Werror -D_BSD_SOURCE $(CURL_CFLAGS) couchjs_LDADD = $(CURL_LDFLAGS) @JSLIB@ couchpriv_DATA = stat_descriptions.cfg diff --git a/src/couchdb/priv/couch_js/http.c b/src/couchdb/priv/couch_js/http.c index 6c2a8a82..77078e35 100644 --- a/src/couchdb/priv/couch_js/http.c +++ b/src/couchdb/priv/couch_js/http.c @@ -14,19 +14,31 @@ #include #include #include +#include "config.h" +#include "utf8.h" + + #include -#include "utf8.h" -#ifdef XP_WIN +void +http_check_enabled() +{ + return; +} + + // Map some of the string function names to things which exist on Windows +#ifdef XP_WIN #define strcasecmp _strcmpi #define strncasecmp _strnicmp #define snprintf _snprintf #endif + typedef struct curl_slist CurlHeaders; + typedef struct { int method; char* url; @@ -34,8 +46,10 @@ typedef struct { jsint last_status; } HTTPData; + char* METHODS[] = {"GET", "HEAD", "POST", "PUT", "DELETE", "COPY", NULL}; + #define GET 0 #define HEAD 1 #define POST 2 @@ -43,14 +57,17 @@ char* METHODS[] = {"GET", "HEAD", "POST", "PUT", "DELETE", "COPY", NULL}; #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) + +JSBool +http_ctor(JSContext* cx, JSObject* req) { HTTPData* http = NULL; JSBool ret = JS_FALSE; @@ -67,12 +84,12 @@ constructor(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) http->req_headers = NULL; http->last_status = -1; - if(!JS_SetPrivate(cx, obj, http)) + if(!JS_SetPrivate(cx, req, http)) { JS_ReportError(cx, "Failed to set private CouchHTTP data."); goto error; } - + ret = JS_TRUE; goto success; @@ -83,90 +100,76 @@ success: return ret; } -static void -destructor(JSContext* cx, JSObject* obj) + +void +http_dtor(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) { 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); + +JSBool +http_open(JSContext* cx, JSObject* req, jsval mth, jsval url, jsval snc) +{ + HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req); char* method = NULL; - char* url = NULL; - JSBool ret = JS_FALSE; int methid; + JSBool ret = JS_FALSE; - if(!http) - { + if(!http) { JS_ReportError(cx, "Invalid CouchHTTP instance."); goto done; } - if(argv[0] == JSVAL_VOID) - { + if(mth == JSVAL_VOID) { JS_ReportError(cx, "You must specify a method."); goto done; } - method = enc_string(cx, argv[0], NULL); - if(!method) - { + method = enc_string(cx, mth, NULL); + if(!method) { JS_ReportError(cx, "Failed to encode method."); goto done; } - for(methid = 0; METHODS[methid] != NULL; methid++) - { + for(methid = 0; METHODS[methid] != NULL; methid++) { if(strcasecmp(METHODS[methid], method) == 0) break; } - if(methid > COPY) - { + if(methid > COPY) { JS_ReportError(cx, "Invalid method specified."); goto done; } http->method = methid; - if(argv[1] == JSVAL_VOID) - { + if(url == JSVAL_VOID) { JS_ReportError(cx, "You must specify a URL."); goto done; } - if(http->url) - { + if(http->url != NULL) { free(http->url); http->url = NULL; } - http->url = enc_string(cx, argv[1], NULL); - if(!http->url) - { + http->url = enc_string(cx, url, NULL); + if(http->url == NULL) { 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."); + if(snc != JSVAL_FALSE) { + JS_ReportError(cx, "Synchronous flag must be false."); goto done; } - if(http->req_headers) - { + if(http->req_headers) { curl_slist_free_all(http->req_headers); http->req_headers = NULL; } @@ -181,42 +184,42 @@ done: return ret; } -static JSBool -setheader(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) -{ - HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj); + +JSBool +http_set_hdr(JSContext* cx, JSObject* req, jsval name, jsval val) +{ + HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req); char* keystr = NULL; char* valstr = NULL; char* hdrbuf = NULL; size_t hdrlen = -1; JSBool ret = JS_FALSE; - if(!http) - { + if(!http) { JS_ReportError(cx, "Invalid CouchHTTP instance."); goto done; } - if(argv[0] == JSVAL_VOID) + if(name == JSVAL_VOID) { JS_ReportError(cx, "You must speciy a header name."); goto done; } - keystr = enc_string(cx, argv[0], NULL); + keystr = enc_string(cx, name, NULL); if(!keystr) { JS_ReportError(cx, "Failed to encode header name."); goto done; } - if(argv[1] == JSVAL_VOID) + if(val == JSVAL_VOID) { JS_ReportError(cx, "You must specify a header value."); goto done; } - valstr = enc_string(cx, argv[1], NULL); + valstr = enc_string(cx, val, NULL); if(!valstr) { JS_ReportError(cx, "Failed to encode header value."); @@ -225,8 +228,7 @@ setheader(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) hdrlen = strlen(keystr) + strlen(valstr) + 3; hdrbuf = (char*) malloc(hdrlen * sizeof(char)); - if(!hdrbuf) - { + if(!hdrbuf) { JS_ReportError(cx, "Failed to allocate header buffer."); goto done; } @@ -240,121 +242,50 @@ 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) +JSBool +http_send(JSContext* cx, JSObject* req, jsval body) { - HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj); - char* body = NULL; + HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req); + char* bodystr = NULL; size_t bodylen = 0; JSBool ret = JS_FALSE; - if(!http) - { + 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) - { + if(body != JSVAL_VOID && body != JS_GetEmptyStringValue(cx)) { + bodystr = enc_string(cx, body, &bodylen); + if(!bodystr) { JS_ReportError(cx, "Failed to encode body."); goto done; } } - ret = go(cx, obj, http, body, bodylen); + ret = go(cx, req, http, bodystr, bodylen); done: - if(body) free(body); + if(bodystr) free(bodystr); return ret; } -static JSBool -status(JSContext* cx, JSObject* obj, jsval idval, jsval* vp) +int +http_status(JSContext* cx, JSObject* req) { - HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj); + HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req); - if(!http) - { + 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; + return http->last_status; } - // Curl Helpers typedef struct { @@ -364,6 +295,7 @@ typedef struct { char* sendbuf; size_t sendlen; size_t sent; + int sent_once; char* recvbuf; size_t recvlen; size_t read; @@ -395,13 +327,13 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen) state.sendbuf = body; state.sendlen = bodylen; state.sent = 0; + state.sent_once = 0; state.recvbuf = NULL; state.recvlen = 0; state.read = 0; - if(HTTP_HANDLE == NULL) - { + 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, @@ -416,14 +348,12 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen) "CouchHTTP Client - Relax"); } - if(!HTTP_HANDLE) - { + if(!HTTP_HANDLE) { JS_ReportError(cx, "Failed to initialize cURL handle."); goto done; } - if(http->method < 0 || http->method > COPY) - { + if(http->method < 0 || http->method > COPY) { JS_ReportError(cx, "INTERNAL: Unknown method."); goto done; } @@ -433,27 +363,21 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen) curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(HTTP_HANDLE, CURLOPT_UPLOAD, 0); - if(http->method == HEAD) - { + 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) - { + } 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) - { + if(body && bodylen) { curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, bodylen); - } - else - { + } else { curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, 0); } - //curl_easy_setopt(HTTP_HANDLE, CURLOPT_VERBOSE, 1); + // 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); @@ -462,39 +386,32 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen) curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEHEADER, &state); curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEDATA, &state); - if(curl_easy_perform(HTTP_HANDLE) != 0) - { + if(curl_easy_perform(HTTP_HANDLE) != 0) { JS_ReportError(cx, "Failed to execute HTTP request: %s", ERRBUF); goto done; } - if(!state.resp_headers) - { + 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, + cx, obj, "_headers", tmp, - NULL, - NULL, + NULL, NULL, JSPROP_READONLY - )) - { + )) { JS_ReportError(cx, "INTERNAL: Failed to set response headers."); goto done; } - if(state.recvbuf) // Is good enough? - { + if(state.recvbuf) { state.recvbuf[state.read] = '\0'; jsbody = dec_string(cx, state.recvbuf, state.read+1); - if(!jsbody) - { + 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. @@ -507,22 +424,17 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen) } } tmp = STRING_TO_JSVAL(jsbody); - } - else - { + } else { tmp = JS_GetEmptyStringValue(cx); } if(!JS_DefineProperty( - cx, - obj, + cx, obj, "responseText", tmp, - NULL, - NULL, + NULL, NULL, JSPROP_READONLY - )) - { + )) { JS_ReportError(cx, "INTERNAL: Failed to set responseText."); goto done; } @@ -540,15 +452,20 @@ 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) - { + + // 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; - //fprintf(stderr, "%lu %lu %lu %lu\n", state->bodyused, state->bodyread, length, towrite); - memcpy(ptr, state->sendbuf + state->sent, towrite); state->sent += towrite; @@ -572,15 +489,12 @@ recv_header(void *ptr, size_t size, size_t nmem, void *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) - { + if(length > 7 && strncasecmp(header, "HTTP/1.", 7) == 0) { + if(length < 12) { return CURLE_WRITE_ERROR; } @@ -589,8 +503,7 @@ recv_header(void *ptr, size_t size, size_t nmem, void *data) state->http->last_status = atoi(code); state->resp_headers = JS_NewArrayObject(state->cx, 0, NULL); - if(!state->resp_headers) - { + if(!state->resp_headers) { return CURLE_WRITE_ERROR; } @@ -598,26 +511,22 @@ recv_header(void *ptr, size_t size, size_t nmem, void *data) } // We get a notice at the \r\n\r\n after headers. - if(length <= 2) - { + if(length <= 2) { return length; } // Append the new header to our array. hdr = dec_string(state->cx, header, length); - if(!hdr) - { + if(!hdr) { return CURLE_WRITE_ERROR; } - if(!JS_GetArrayLength(state->cx, state->resp_headers, &hdrlen)) - { + 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)) - { + if(!JS_SetElement(state->cx, state->resp_headers, hdrlen, &hdrval)) { return CURLE_WRITE_ERROR; } @@ -631,15 +540,13 @@ recv_body(void *ptr, size_t size, size_t nmem, void *data) size_t length = size * nmem; char* tmp = NULL; - if(!state->recvbuf) - { + if(!state->recvbuf) { state->recvlen = 4096; state->read = 0; state->recvbuf = JS_malloc(state->cx, state->recvlen); } - if(!state->recvbuf) - { + if(!state->recvbuf) { return CURLE_WRITE_ERROR; } @@ -663,8 +570,7 @@ str_from_binary(JSContext* cx, char* data, size_t length) if(!conv) return NULL; - for(i = 0; i < length; i++) - { + for(i = 0; i < length; i++) { conv[i] = (jschar) data[i]; } diff --git a/src/couchdb/priv/couch_js/http.h b/src/couchdb/priv/couch_js/http.h index b5f8c70f..373d1e48 100644 --- a/src/couchdb/priv/couch_js/http.h +++ b/src/couchdb/priv/couch_js/http.h @@ -13,6 +13,12 @@ #ifndef COUCH_JS_HTTP_H #define COUCH_JS_HTTP_H -JSObject* install_http(JSContext* cx, JSObject* global); +void http_check_enabled(); +JSBool http_ctor(JSContext* cx, JSObject* req); +void http_dtor(JSContext* cx, JSObject* req); +JSBool http_open(JSContext* cx, JSObject* req, jsval mth, jsval url, jsval snc); +JSBool http_set_hdr(JSContext* cx, JSObject* req, jsval name, jsval val); +JSBool http_send(JSContext* cx, JSObject* req, jsval body); +int http_status(JSContext* cx, JSObject* req); -#endif \ No newline at end of file +#endif diff --git a/src/couchdb/priv/couch_js/main.c b/src/couchdb/priv/couch_js/main.c index 376aa15b..209bb023 100644 --- a/src/couchdb/priv/couch_js/main.c +++ b/src/couchdb/priv/couch_js/main.c @@ -10,329 +10,12 @@ // License for the specific language governing permissions and limitations under // the License. -#include -#include -#include -#include #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); +#if defined(SM185) +#include "sm185.c" +#elif defined(SM180) +#include "sm180.c" #else -#define SETUP_REQUEST(cx) -#define FINISH_REQUEST(cx) +#include "sm170.c" #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 \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 index 699a6fee..d6064267 100644 --- a/src/couchdb/priv/couch_js/utf8.c +++ b/src/couchdb/priv/couch_js/utf8.c @@ -11,6 +11,7 @@ // the License. #include +#include "config.h" static int enc_char(uint8 *utf8Buffer, uint32 ucs4Char) @@ -121,7 +122,7 @@ char* enc_string(JSContext* cx, jsval arg, size_t* buflen) { JSString* str = NULL; - jschar* src = NULL; + const jschar* src = NULL; char* bytes = NULL; size_t srclen = 0; size_t byteslen = 0; @@ -129,8 +130,12 @@ enc_string(JSContext* cx, jsval arg, size_t* buflen) str = JS_ValueToString(cx, arg); if(!str) goto error; +#ifdef HAVE_JS_GET_STRING_CHARS_AND_LENGTH + src = JS_GetStringCharsAndLength(cx, str, &srclen); +#else src = JS_GetStringChars(str); srclen = JS_GetStringLength(str); +#endif if(!enc_charbuf(src, srclen, NULL, &byteslen)) goto error; @@ -283,4 +288,4 @@ error: success: return str; -} \ No newline at end of file +} -- cgit v1.2.3 From da22c19f7a7fdfae015dfb0db93b5bd9453eae6c Mon Sep 17 00:00:00 2001 From: Robert Newson Date: Thu, 6 Oct 2011 20:49:48 +0100 Subject: Revert "Fix function evaluation by newer SpiderMonkey's." This reverts commit 61f10e7590bd352b367ad426a56587cbef3700b6. --- share/server/util.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/share/server/util.js b/share/server/util.js index f6fa60bb..0b812fe1 100644 --- a/share/server/util.js +++ b/share/server/util.js @@ -63,11 +63,6 @@ var Couch = { }, compileFunction : function(source, ddoc) { if (!source) throw(["error","not_found","missing function"]); - // Some newer SpiderMonkey's appear to not like evaluating - // an anonymous function at global scope. Simple fix just - // wraps the source with parens so the function object is - // returned correctly. - source = "(" + source + ")"; try { if (sandbox) { if (ddoc) { -- cgit v1.2.3