You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by jc...@apache.org on 2009/03/05 07:14:39 UTC

svn commit: r750332 [1/2] - in /couchdb/branches/rep_security: ./ bin/ etc/couchdb/ etc/default/ etc/init/ share/ share/server/ share/www/ share/www/script/ share/www/script/test/ share/www/style/ src/couchdb/ src/ibrowse/ src/mochiweb/ test/

Author: jchris
Date: Thu Mar  5 06:14:36 2009
New Revision: 750332

URL: http://svn.apache.org/viewvc?rev=750332&view=rev
Log:
svn merge to bring rep_security branch up to date with trunk as of r750305

Added:
    couchdb/branches/rep_security/share/www/script/test/etags_views.js
      - copied, changed from r750305, couchdb/trunk/share/www/script/test/etags_views.js
    couchdb/branches/rep_security/share/www/script/test/invalid_docids.js
      - copied, changed from r750305, couchdb/trunk/share/www/script/test/invalid_docids.js
    couchdb/branches/rep_security/share/www/script/test/list_views.js
      - copied, changed from r750305, couchdb/trunk/share/www/script/test/list_views.js
    couchdb/branches/rep_security/share/www/script/test/show_documents.js
      - copied unchanged from r750305, couchdb/trunk/share/www/script/test/show_documents.js
    couchdb/branches/rep_security/share/www/script/test/stats.js
      - copied unchanged from r750305, couchdb/trunk/share/www/script/test/stats.js
    couchdb/branches/rep_security/src/couchdb/couch_httpd_stats_handlers.erl
      - copied unchanged from r750305, couchdb/trunk/src/couchdb/couch_httpd_stats_handlers.erl
    couchdb/branches/rep_security/src/couchdb/couch_ref_counter.erl
      - copied unchanged from r750305, couchdb/trunk/src/couchdb/couch_ref_counter.erl
    couchdb/branches/rep_security/src/couchdb/couch_stats.hrl
      - copied unchanged from r750305, couchdb/trunk/src/couchdb/couch_stats.hrl
    couchdb/branches/rep_security/src/couchdb/couch_stats_aggregator.erl
      - copied unchanged from r750305, couchdb/trunk/src/couchdb/couch_stats_aggregator.erl
    couchdb/branches/rep_security/src/couchdb/couch_stats_collector.erl
      - copied unchanged from r750305, couchdb/trunk/src/couchdb/couch_stats_collector.erl
Removed:
    couchdb/branches/rep_security/src/couchdb/couch_file_stats.erl
Modified:
    couchdb/branches/rep_security/   (props changed)
    couchdb/branches/rep_security/THANKS
    couchdb/branches/rep_security/acinclude.m4.in
    couchdb/branches/rep_security/bin/Makefile.am
    couchdb/branches/rep_security/bin/couchdb.tpl.in
    couchdb/branches/rep_security/configure.ac
    couchdb/branches/rep_security/etc/couchdb/default.ini.tpl.in
    couchdb/branches/rep_security/etc/default/couchdb   (props changed)
    couchdb/branches/rep_security/etc/init/couchdb.tpl.in
    couchdb/branches/rep_security/share/Makefile.am
    couchdb/branches/rep_security/share/server/main.js
    couchdb/branches/rep_security/share/www/couch_tests.html
    couchdb/branches/rep_security/share/www/index.html
    couchdb/branches/rep_security/share/www/script/couch.js
    couchdb/branches/rep_security/share/www/script/couch_test_runner.js
    couchdb/branches/rep_security/share/www/script/futon.browse.js
    couchdb/branches/rep_security/share/www/script/jquery.couch.js
    couchdb/branches/rep_security/share/www/script/test/   (props changed)
    couchdb/branches/rep_security/share/www/script/test/attachments.js
    couchdb/branches/rep_security/share/www/script/test/basics.js
    couchdb/branches/rep_security/share/www/script/test/content_negotiation.js
    couchdb/branches/rep_security/share/www/script/test/security_validation.js
    couchdb/branches/rep_security/share/www/script/test/uuids.js
    couchdb/branches/rep_security/share/www/script/test/view_errors.js
    couchdb/branches/rep_security/share/www/style/layout.css
    couchdb/branches/rep_security/src/couchdb/Makefile.am
    couchdb/branches/rep_security/src/couchdb/couch_db.erl
    couchdb/branches/rep_security/src/couchdb/couch_db.hrl
    couchdb/branches/rep_security/src/couchdb/couch_db_updater.erl
    couchdb/branches/rep_security/src/couchdb/couch_doc.erl
    couchdb/branches/rep_security/src/couchdb/couch_file.erl
    couchdb/branches/rep_security/src/couchdb/couch_httpd.erl
    couchdb/branches/rep_security/src/couchdb/couch_httpd_db.erl
    couchdb/branches/rep_security/src/couchdb/couch_httpd_external.erl
    couchdb/branches/rep_security/src/couchdb/couch_httpd_misc_handlers.erl
    couchdb/branches/rep_security/src/couchdb/couch_httpd_show.erl
    couchdb/branches/rep_security/src/couchdb/couch_httpd_view.erl
    couchdb/branches/rep_security/src/couchdb/couch_query_servers.erl
    couchdb/branches/rep_security/src/couchdb/couch_rep.erl
    couchdb/branches/rep_security/src/couchdb/couch_server.erl
    couchdb/branches/rep_security/src/couchdb/couch_server_sup.erl
    couchdb/branches/rep_security/src/couchdb/couch_stream.erl
    couchdb/branches/rep_security/src/couchdb/couch_view.erl
    couchdb/branches/rep_security/src/ibrowse/Makefile.am
    couchdb/branches/rep_security/src/mochiweb/Makefile.am
    couchdb/branches/rep_security/src/mochiweb/mochijson.erl
    couchdb/branches/rep_security/src/mochiweb/mochijson2.erl
    couchdb/branches/rep_security/src/mochiweb/mochiweb.app
    couchdb/branches/rep_security/src/mochiweb/mochiweb_headers.erl
    couchdb/branches/rep_security/src/mochiweb/mochiweb_html.erl
    couchdb/branches/rep_security/src/mochiweb/mochiweb_http.erl
    couchdb/branches/rep_security/src/mochiweb/mochiweb_request.erl
    couchdb/branches/rep_security/src/mochiweb/mochiweb_util.erl
    couchdb/branches/rep_security/test/runner.sh
    couchdb/branches/rep_security/test/test.js

Propchange: couchdb/branches/rep_security/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Thu Mar  5 06:14:36 2009
@@ -1 +1,2 @@
 /couchdb/branches/form:729440-730015
+/couchdb/trunk:741843-750305

Modified: couchdb/branches/rep_security/THANKS
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/THANKS?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/THANKS (original)
+++ couchdb/branches/rep_security/THANKS Thu Mar  5 06:14:36 2009
@@ -26,5 +26,6 @@
  * Brian Palmer <ji...@brian.codekitchen.net>
  * Jason Davies <ja...@jasondavies.com>
  * Maximillian Dornseif <md...@hudora.de>
+ * Eric Casteleijn <er...@canonical.com>
 
 For a list of authors see the `AUTHORS` file.

Modified: couchdb/branches/rep_security/acinclude.m4.in
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/acinclude.m4.in?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/acinclude.m4.in (original)
+++ couchdb/branches/rep_security/acinclude.m4.in Thu Mar  5 06:14:36 2009
@@ -21,11 +21,10 @@
 m4_define([LOCAL_VERSION_REVISION], [0])
 m4_define([LOCAL_VERSION_STAGE], [a])
 m4_define([LOCAL_VERSION_RELEASE], [%release%])
-m4_define([LOCAL_VERSION_STATUS], [incubating])
 m4_define([LOCAL_VERSION_PRIMARY],
     [LOCAL_VERSION_MAJOR.LOCAL_VERSION_MINOR.LOCAL_VERSION_REVISION])
 m4_define([LOCAL_VERSION_SECONDARY],
-    [LOCAL_VERSION_STAGE[]LOCAL_VERSION_RELEASE-LOCAL_VERSION_STATUS])
+    [LOCAL_VERSION_STAGE[]LOCAL_VERSION_RELEASE])
 m4_define([LOCAL_VERSION],
     [LOCAL_VERSION_PRIMARY[]LOCAL_VERSION_SECONDARY])
 

Modified: couchdb/branches/rep_security/bin/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/bin/Makefile.am?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/bin/Makefile.am (original)
+++ couchdb/branches/rep_security/bin/Makefile.am Thu Mar  5 06:14:36 2009
@@ -29,7 +29,7 @@
 	    -e "s|%bindir%|@bindir@|g" \
 	    -e "s|%localerlanglibdir%|@localerlanglibdir@|g" \
 	    -e "s|%couchdbebindir%|couch-@version@/ebin|g" \
-	    -e "s|%mochiwebebindir%|mochiweb-r82/ebin|g" \
+	    -e "s|%mochiwebebindir%|mochiweb-r97/ebin|g" \
 	    -e "s|%ibrowseebindir%|ibrowse-1.4.1/ebin|g" \
 	    -e "s|%defaultini%|default.ini|g" \
 	    -e "s|%localini%|local.ini|g" \

Modified: couchdb/branches/rep_security/bin/couchdb.tpl.in
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/bin/couchdb.tpl.in?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/bin/couchdb.tpl.in (original)
+++ couchdb/branches/rep_security/bin/couchdb.tpl.in Thu Mar  5 06:14:36 2009
@@ -20,6 +20,7 @@
 KILL_BOOLEAN=false
 SHUTDOWN_BOOLEAN=false
 RECURSED_BOOLEAN=false
+RESET_CONFIG_BOOLEAN=true
 
 RESPAWN_TIMEOUT=0
 
@@ -70,6 +71,7 @@
   -h          display a short help message and exit
   -V          display version information and exit
   -c FILE     use configuration FILE (chainable, resets system default)
+  -C FILE     use configuration FILE (chainable, does not reset system default)
   -i          use the interactive Erlang shell
   -b          spawn as a background process
   -p FILE     set the background PID FILE (overrides system default)
@@ -168,7 +170,11 @@
         interactive_option="+Bd -noinput"
     fi
     if test -n "$INI_FILES"; then
-        ini_files="$INI_FILES"
+        if test "$RESET_CONFIG_BOOLEAN" = "true"; then
+            ini_files="$INI_FILES"
+        else
+            ini_files="$DEFAULT_INI_FILE $INI_FILES"
+        fi
     else
         ini_files="$DEFAULT_INI_FILE $LOCAL_INI_FILE"
     fi
@@ -258,7 +264,7 @@
 
 parse_script_option_list () {
     set +e
-    options=`getopt hVc:ibp:r:Ro:e:skd $@`
+    options=`getopt hVc:C:ibp:r:Ro:e:skd $@`
     if test ! $? -eq 0; then
         display_error
     fi
@@ -269,6 +275,7 @@
             -h) shift; display_help; exit $SCRIPT_OK;;
             -V) shift; display_version; exit $SCRIPT_OK;;
             -c) shift; INI_FILES="$INI_FILES $1"; shift;;
+            -C) shift; RESET_CONFIG_BOOLEAN=false; INI_FILES="$INI_FILES $1"; shift;;
             -i) shift; INTERACTIVE_BOOLEAN=true;;
             -b) shift; BACKGROUND_BOOLEAN=true;;
             -r) shift; RESPAWN_TIMEOUT=$1; shift;;

Modified: couchdb/branches/rep_security/configure.ac
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/configure.ac?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/configure.ac (original)
+++ couchdb/branches/rep_security/configure.ac Thu Mar  5 06:14:36 2009
@@ -76,7 +76,7 @@
 
 AC_ARG_VAR([FLAGS], [general flags to prepend to LDFLAGS and CPPFLAGS])
 
-LIB_FLAGS="-L/usr/local/lib -L/opt/local/lib $JS_LIB_FLAGS"
+LIB_FLAGS="$JS_LIB_FLAGS -L/usr/local/lib -L/opt/local/lib"
 LIBS="$LIB_FLAGS $LIBS"
 # XP_UNIX required for jsapi.h and has been tested to work on Linux and Darwin.
 FLAGS="$LIB_FLAGS $ERLANG_FLAGS $JS_FLAGS -DXP_UNIX $FLAGS"

Modified: couchdb/branches/rep_security/etc/couchdb/default.ini.tpl.in
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/etc/couchdb/default.ini.tpl.in?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/etc/couchdb/default.ini.tpl.in (original)
+++ couchdb/branches/rep_security/etc/couchdb/default.ini.tpl.in Thu Mar  5 06:14:36 2009
@@ -4,8 +4,10 @@
 
 [couchdb]
 database_dir = %localstatelibdir%
+view_index_dir = %localstatelibdir%
 util_driver_dir = %couchprivlibdir%
 max_document_size = 4294967296 ; 4 GB
+max_attachment_chunk_size = 4294967296 ; 4GB
 view_timeout = 5000 ; 5 seconds
 max_dbs_open = 100
 
@@ -33,6 +35,8 @@
 db_update_notifier={couch_db_update_notifier_sup, start_link, []}
 query_servers={couch_query_servers, start_link, []}
 httpd={couch_httpd, start_link, []}
+stats_aggregator={couch_stats_aggregator, start, []}
+stats_collector={couch_stats_collector, start, []}
 
 [httpd_global_handlers]
 / = {couch_httpd_misc_handlers, handle_welcome_req, <<"Welcome">>}
@@ -40,12 +44,12 @@
 
 _utils = {couch_httpd_misc_handlers, handle_utils_dir_req, "%localdatadir%/www"}
 _all_dbs = {couch_httpd_misc_handlers, handle_all_dbs_req}
-_stats = {couch_httpd_misc_handlers, handle_stats_req}
 _active_tasks = {couch_httpd_misc_handlers, handle_task_status_req}
 _config = {couch_httpd_misc_handlers, handle_config_req}
 _replicate = {couch_httpd_misc_handlers, handle_replicate_req}
 _uuids = {couch_httpd_misc_handlers, handle_uuids_req}
 _restart = {couch_httpd_misc_handlers, handle_restart_req}
+_stats = {couch_httpd_stats_handlers, handle_stats_req}
 
 [httpd_db_handlers]
 _view = {couch_httpd_view, handle_view_req}

Propchange: couchdb/branches/rep_security/etc/default/couchdb
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Thu Mar  5 06:14:36 2009
@@ -1,2 +1,3 @@
 /couchdb/branches/form/etc/default/couchdb:729440-730015
+/couchdb/trunk/etc/default/couchdb:741843-750305
 /incubator/couchdb/trunk/etc/default/couchdb:642419-694440

Modified: couchdb/branches/rep_security/etc/init/couchdb.tpl.in
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/etc/init/couchdb.tpl.in?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/etc/init/couchdb.tpl.in (original)
+++ couchdb/branches/rep_security/etc/init/couchdb.tpl.in Thu Mar  5 06:14:36 2009
@@ -63,6 +63,9 @@
     # Start Apache CouchDB as a background process.
 
     command="$COUCHDB -b"
+    if test -n "$COUCHDB_PID_FILE"; then 
+        command="$command -p $COUCHDB_PID_FILE" 
+    fi 
     if test -n "$COUCHDB_STDOUT_FILE"; then
         command="$command -o $COUCHDB_STDOUT_FILE"
     fi

Modified: couchdb/branches/rep_security/share/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/Makefile.am?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/share/Makefile.am (original)
+++ couchdb/branches/rep_security/share/Makefile.am Thu Mar  5 06:14:36 2009
@@ -71,4 +71,46 @@
     www/script/jquery.resizer.js \
     www/script/jquery.suggest.js \
     www/script/json2.js \
+    www/script/test/basics.js \
+    www/script/test/delayed_commits.js \
+    www/script/test/all_docs.js \
+    www/script/test/conflicts.js \
+    www/script/test/recreate_doc.js \
+    www/script/test/copy_move_doc.js \
+    www/script/test/uuids.js \
+    www/script/test/bulk_docs.js \
+    www/script/test/lots_of_docs.js \
+    www/script/test/reduce.js \
+    www/script/test/reduce_false.js \
+    www/script/test/design_options.js \
+    www/script/test/multiple_rows.js \
+    www/script/test/large_docs.js \
+    www/script/test/utf8.js \
+    www/script/test/attachments.js \
+    www/script/test/attachment_paths.js \
+    www/script/test/attachment_views.js \
+    www/script/test/design_paths.js \
+    www/script/test/content_negotiation.js \
+    www/script/test/design_docs.js \
+    www/script/test/invalid_docids.js \
+    www/script/test/view_collation.js \
+    www/script/test/view_conflicts.js \
+    www/script/test/view_errors.js \
+    www/script/test/view_include_docs.js \
+    www/script/test/view_multi_key_all_docs.js \
+    www/script/test/view_multi_key_design.js \
+    www/script/test/view_multi_key_temp.js \
+    www/script/test/view_pagination.js \
+    www/script/test/view_sandboxing.js \
+    www/script/test/view_xml.js \
+    www/script/test/replication.js \
+    www/script/test/etags_head.js \
+    www/script/test/etags_views.js \
+    www/script/test/show_documents.js \
+    www/script/test/list_views.js \
+    www/script/test/compact.js \
+    www/script/test/purge.js \
+    www/script/test/config.js \
+    www/script/test/security_validation.js \
+    www/script/test/stats.js \
     www/style/layout.css

Modified: couchdb/branches/rep_security/share/server/main.js
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/server/main.js?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/share/server/main.js [utf-8] (original)
+++ couchdb/branches/rep_security/share/server/main.js [utf-8] Thu Mar  5 06:14:36 2009
@@ -142,7 +142,7 @@
 // this function provides a shortcut for managing responses by Accept header
 respondWith = function(req, responders) {
   var bestKey = null, accept = req.headers["Accept"];
-  if (accept) {
+  if (accept && !req.query.format) {
     var provides = [];
     for (key in responders) {
       if (mimesByKey[key]) {
@@ -151,6 +151,8 @@
     }
     var bestMime = Mimeparse.bestMatch(provides, accept);
     bestKey = keysByMime[bestMime];
+  } else {
+    bestKey = req.query.format;
   }
   var rFunc = responders[bestKey || responders.fallback || "html"];
   if (rFunc) {      
@@ -352,25 +354,32 @@
         var listFun = funs[0];
         var head = cmd[1];
         var req = cmd[2];
-        row_line[listFun] = 0;
+        row_line[listFun] = { first_key: null, row_number: 0, prev_key: null };
         runRenderFunction(listFun, [head, null, req, null]);
         break;
       case "list_row":
         var listFun = funs[0];
         var row = cmd[1];
         var req = cmd[2];
-        runRenderFunction(listFun, [null, row, req, row_line[listFun]]);
-        row_line[listFun]++;
+        var row_info = row_line[listFun];
+        runRenderFunction(listFun, [null, row, req, row_info]);
+        if (row_info.first_key == null) {
+            row_info.first_key = row.key;
+        } else {
+            row_info.prev_key = row.key;
+        }
+        row_info.row_number++;
+        row_line[listFun] = row_info;
         break;
       case "list_tail":
         var listFun = funs[0];
         var req = cmd[1];
-        var row_number = null;
+        var row_info = null;
         try {
-            row_number = row_line[listFun];
+            row_info = row_line[listFun];
             delete row_line[listFun];
         } catch (e) {}
-        runRenderFunction(listFun, [null, null, req, row_number]);
+        runRenderFunction(listFun, [null, null, req, row_info]);
         break;
       default:
         print(toJSON({error: "query_server_error",

Modified: couchdb/branches/rep_security/share/www/couch_tests.html
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/www/couch_tests.html?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/share/www/couch_tests.html [utf-8] (original)
+++ couchdb/branches/rep_security/share/www/couch_tests.html [utf-8] Thu Mar  5 06:14:36 2009
@@ -34,7 +34,7 @@
         });
       });
       var testsPath = document.location.toString().split('?')[1];
-      loadTests(testsPath||"script/couch_tests.js")
+      loadScript(testsPath||"script/couch_tests.js")
     </script>
   </head>
   <body><div id="wrap">

Modified: couchdb/branches/rep_security/share/www/index.html
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/www/index.html?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/share/www/index.html [utf-8] (original)
+++ couchdb/branches/rep_security/share/www/index.html [utf-8] Thu Mar  5 06:14:36 2009
@@ -63,6 +63,7 @@
             <tr>
               <th>Name</th>
               <th class="size">Size</th>
+              <th class="apps">Applications</th>
               <th class="count">Number of Documents</th>
               <th class="seq">Update Seq</th>
             </tr>
@@ -71,7 +72,7 @@
           </tbody>
           <tbody class="footer">
             <tr>
-              <td colspan="4">
+              <td colspan="5">
                 <div id="paging">
                   <a class="prev">← Previous Page</a> |
                   <label>Rows per page: <select id="perpage">

Modified: couchdb/branches/rep_security/share/www/script/couch.js
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/www/script/couch.js?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/share/www/script/couch.js [utf-8] (original)
+++ couchdb/branches/rep_security/share/www/script/couch.js [utf-8] Thu Mar  5 06:14:36 2009
@@ -177,6 +177,10 @@
     CouchDB.maybeThrowError(this.last_req);
     return JSON.parse(this.last_req.responseText);
   }
+  
+  this.designDocs = function() {
+    return this.allDocs({startkey:"_design", endkey:"_design0"});
+  };
 
   this.allDocsBySeq = function(options,keys) {
     var req = null;
@@ -263,6 +267,15 @@
   return JSON.parse(CouchDB.last_req.responseText);
 }
 
+CouchDB.allDesignDocs = function() {
+  var ddocs = {}, dbs = CouchDB.allDbs();
+  for (var i=0; i < dbs.length; i++) {
+    var db = new CouchDB(dbs[i]);
+    ddocs[dbs[i]] = db.designDocs();
+  };
+  return ddocs;
+};
+
 CouchDB.getVersion = function() {
   CouchDB.last_req = CouchDB.request("GET", "/");
   CouchDB.maybeThrowError(CouchDB.last_req);
@@ -300,6 +313,16 @@
   return req;
 }
 
+CouchDB.requestStats = function(module, key, test) {
+  var query_arg = "";
+  if(test !== null) {
+    query_arg = "?flush=true";
+  }
+
+  var stat = CouchDB.request("GET", "/_stats/" + module + "/" + key + query_arg).responseText;
+  return JSON.parse(stat)[module][key];
+}
+
 CouchDB.uuids_cache = [];
 
 CouchDB.newUuids = function(n) {
@@ -313,7 +336,7 @@
     }
     return uuids;
   } else {
-    CouchDB.last_req = CouchDB.request("POST", "/_uuids?count=" + (100 + n));
+    CouchDB.last_req = CouchDB.request("GET", "/_uuids?count=" + (100 + n));
     CouchDB.maybeThrowError(CouchDB.last_req);
     var result = JSON.parse(CouchDB.last_req.responseText);
     CouchDB.uuids_cache =
@@ -332,3 +355,13 @@
     throw result;
   }
 }
+
+CouchDB.params = function(options) {
+  options = options || {};
+  var returnArray = [];
+  for(var key in options) {
+    var value = options[key];
+    returnArray.push(key + "=" + value);
+  }
+  return returnArray.join("&");
+}
\ No newline at end of file

Modified: couchdb/branches/rep_security/share/www/script/couch_test_runner.js
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/www/script/couch_test_runner.js?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/share/www/script/couch_test_runner.js (original)
+++ couchdb/branches/rep_security/share/www/script/couch_test_runner.js Thu Mar  5 06:14:36 2009
@@ -12,10 +12,6 @@
 
 // *********************** Test Framework of Sorts ************************* //
 
-function loadTests(url) {  
-  document.write('<script src="'+url+'"></script>');
-};
-
 function loadScript(url) {  
   if (typeof document != "undefined") document.write('<script src="'+url+'"></script>');
 };
@@ -114,7 +110,7 @@
   var name = $(cell).text();
   var win = window.open("", name, "width=700,height=500,resizable=yes,scrollbars=yes");
   win.document.title = name;
-  $("<pre></pre>").text(tests[name].toString()).appendTo(win.document.body).fadeIn();
+  $("<pre></pre>").text(couchTests[name].toString()).appendTo(win.document.body).fadeIn();
 }
 
 function updateTestsListing() {
@@ -156,13 +152,13 @@
 // display the line that failed.
 // Example:
 // T(MyValue==1);
-function T(arg1, arg2) {
+function T(arg1, arg2, testName) {
   if (!arg1) {
     if (currentRow) {
       if ($("td.details ol", currentRow).length == 0) {
         $("<ol></ol>").appendTo($("td.details", currentRow));
       }
-      $("<li><b>Assertion failed:</b> <code class='failure'></code></li>")
+      $("<li><b>Assertion " + (testName ? "'" + testName + "'" : "") + " failed:</b> <code class='failure'></code></li>")
         .find("code").text((arg2 != null ? arg2 : arg1).toString()).end()
         .appendTo($("td.details ol", currentRow));
     }
@@ -170,6 +166,10 @@
   }
 }
 
+function TEquals(expected, actual, testName) {
+  T(equals(expected, actual), "expected '" + expected + "', got '" + actual + "'", testName);
+}
+
 function equals(a,b) {
   if (a === b) return true;
   try {

Modified: couchdb/branches/rep_security/share/www/script/futon.browse.js
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/www/script/futon.browse.js?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/share/www/script/futon.browse.js [utf-8] (original)
+++ couchdb/branches/rep_security/share/www/script/futon.browse.js [utf-8] Thu Mar  5 06:14:36 2009
@@ -52,9 +52,16 @@
               $("#databases tbody.content").append("<tr>" + 
                 "<th><a href='database.html?" + encodeURIComponent(dbName) + "'>" +
                   dbName + "</a></th>" +
-                "<td class='size'></td><td class='count'></td>" +
+                "<td class='size'></td><td class='apps'></td><td class='count'></td>" +
                 "<td class='seq'></td></tr>");
-              $.couch.db(dbName).info({
+              var db = $.couch.db(dbName);
+              db.allApps({
+                eachApp : function(name, path) {
+                  $("#databases tbody.content tr:eq(" + idx + ")")
+                    .find("td.apps").append('<a href="'+path+'">'+name+'</a> ');
+                }
+              });
+              db.info({
                 success: function(info) {
                   $("#databases tbody.content tr:eq(" + idx + ")")
                     .find("td.size").text($.futon.formatSize(info.disk_size)).end()

Modified: couchdb/branches/rep_security/share/www/script/jquery.couch.js
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/www/script/jquery.couch.js?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/share/www/script/jquery.couch.js [utf-8] (original)
+++ couchdb/branches/rep_security/share/www/script/jquery.couch.js [utf-8] Thu Mar  5 06:14:36 2009
@@ -173,6 +173,38 @@
             }
           });
         },
+        allDesignDocs: function(options) {
+          options = options || {};
+          this.allDocs($.extend({startkey:"_design", endkey:"_design0"}, options));
+        },
+        allApps: function(options) {
+          options = options || {};
+          var self = this;
+          if (options.eachApp) {
+            this.allDesignDocs({
+              success: function(resp) {
+                $.each(resp.rows, function() {
+                  self.openDoc(this.id, {
+                    success: function(ddoc) {
+                      var index, appPath, appName = ddoc._id.split('/');
+                      appName.shift();
+                      appName = appName.join('/');
+                      index = ddoc.couchapp && ddoc.couchapp.index;
+                      if (index) {
+                        appPath = ['', name, index[0], appName, index[1]].join('/');
+                      } else if (ddoc._attachments && ddoc._attachments["index.html"]) {
+                        appPath = ['', name, '_design', appName, "index.html"].join('/');
+                      }
+                      if (appPath) options.eachApp(appName, appPath, ddoc);
+                    }
+                  });
+                });
+              }
+            });            
+          } else {
+            alert("please provide an eachApp function for allApps()");
+          }
+        },
         openDoc: function(docId, options) {
           options = options || {};
           $.ajax({

Propchange: couchdb/branches/rep_security/share/www/script/test/
------------------------------------------------------------------------------
    svn:mergeinfo = 

Modified: couchdb/branches/rep_security/share/www/script/test/attachments.js
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/www/script/test/attachments.js?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/share/www/script/test/attachments.js (original)
+++ couchdb/branches/rep_security/share/www/script/test/attachments.js Thu Mar  5 06:14:36 2009
@@ -33,7 +33,7 @@
   T(xhr.responseText == "This is a base64 encoded text");
   T(xhr.getResponseHeader("Content-Type") == "text/plain");
   T(xhr.getResponseHeader("Etag") == '"' + save_response.rev + '"');
-
+  
   // empty attachment
   var binAttDoc2 = {
     _id: "bin_doc2",

Modified: couchdb/branches/rep_security/share/www/script/test/basics.js
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/www/script/test/basics.js?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/share/www/script/test/basics.js (original)
+++ couchdb/branches/rep_security/share/www/script/test/basics.js Thu Mar  5 06:14:36 2009
@@ -141,4 +141,4 @@
     var locs = loc.split('/');
     T(locs[4] == resp.id);
     T(locs[3] == "test_suite_db");    
-  };
\ No newline at end of file
+  };

Modified: couchdb/branches/rep_security/share/www/script/test/content_negotiation.js
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/www/script/test/content_negotiation.js?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/share/www/script/test/content_negotiation.js (original)
+++ couchdb/branches/rep_security/share/www/script/test/content_negotiation.js Thu Mar  5 06:14:36 2009
@@ -20,6 +20,10 @@
   xhr = CouchDB.request("GET", "/test_suite_db/");
   T(xhr.getResponseHeader("Content-Type") == "text/plain;charset=utf-8");
 
+  // make sure JSON responses end in a newline
+  var text = xhr.responseText;
+  T(text[text.length-1] == "\n");
+
   xhr = CouchDB.request("GET", "/test_suite_db/", {
     headers: {"Accept": "text/html;text/plain;*/*"}
   });

Copied: couchdb/branches/rep_security/share/www/script/test/etags_views.js (from r750305, couchdb/trunk/share/www/script/test/etags_views.js)
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/www/script/test/etags_views.js?p2=couchdb/branches/rep_security/share/www/script/test/etags_views.js&p1=couchdb/trunk/share/www/script/test/etags_views.js&r1=750305&r2=750332&rev=750332&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/etags_views.js (original)
+++ couchdb/branches/rep_security/share/www/script/test/etags_views.js Thu Mar  5 06:14:36 2009
@@ -42,8 +42,7 @@
   T(db.save(designDoc).ok);
   var xhr;
   var docs = makeDocs(0, 10);
-  var saveResult = db.bulkSave(docs);
-  T(saveResult.ok);
+  db.bulkSave(docs);
   
   // verify get w/Etag on map view
   xhr = CouchDB.request("GET", "/test_suite_db/_view/etags/basicView");

Copied: couchdb/branches/rep_security/share/www/script/test/invalid_docids.js (from r750305, couchdb/trunk/share/www/script/test/invalid_docids.js)
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/www/script/test/invalid_docids.js?p2=couchdb/branches/rep_security/share/www/script/test/invalid_docids.js&p1=couchdb/trunk/share/www/script/test/invalid_docids.js&r1=750305&r2=750332&rev=750332&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/invalid_docids.js (original)
+++ couchdb/branches/rep_security/share/www/script/test/invalid_docids.js Thu Mar  5 06:14:36 2009
@@ -23,32 +23,32 @@
   //Test non-string
   try {
     db.save({"_id": 1});
-    T(1 == 0);
+    T(1 == 0, "doc id must be string");
   } catch(e) {
       T(db.last_req.status == 400);
-      T(e.error == "invalid_doc");
+      T(e.error == "bad_request");
   }
 
   // Test invalid _prefix
   try {
     db.save({"_id": "_invalid"});
-    T(1 == 0);
+    T(1 == 0, "doc id may not start with underscore");
   } catch(e) {
       T(db.last_req.status == 400);
-      T(e.error == "invalid_doc");
+      T(e.error == "bad_request");
   }
 
   // Test _bulk_docs explicitly.
   var docs = [{"_id": "_design/foo"}, {"_id": "_local/bar"}];
-  T(db.bulkSave(docs).ok);
+  db.bulkSave(docs);
   docs.forEach(function(d) {T(db.open(d._id)._id == d._id);});
 
   docs = [{"_id": "_invalid"}];
   try {
     db.bulkSave(docs);
-    T(1 == 0);
+    T(1 == 0, "doc id may not start with underscore, even in bulk docs");
   } catch(e) {
       T(db.last_req.status == 400);
-      T(e.error == "invalid_doc");
+      T(e.error == "bad_request");
   }
 };

Copied: couchdb/branches/rep_security/share/www/script/test/list_views.js (from r750305, couchdb/trunk/share/www/script/test/list_views.js)
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/www/script/test/list_views.js?p2=couchdb/branches/rep_security/share/www/script/test/list_views.js&p1=couchdb/trunk/share/www/script/test/list_views.js&r1=750305&r2=750332&rev=750332&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/list_views.js (original)
+++ couchdb/branches/rep_security/share/www/script/test/list_views.js Thu Mar  5 06:14:36 2009
@@ -137,8 +137,7 @@
   T(db.save(designDoc).ok);
   
   var docs = makeDocs(0, 10);
-  var saveResult = db.bulkSave(docs);
-  T(saveResult.ok);
+  db.bulkSave(docs);
   
   var view = db.view('lists/basicView');
   T(view.total_rows == 10);
@@ -207,8 +206,7 @@
   
   // verify the etags expire correctly
   var docs = makeDocs(11, 12);
-  var saveResult = db.bulkSave(docs);
-  T(saveResult.ok);
+  db.bulkSave(docs);
   
   xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/withReduce?group=true", {
     headers: {"if-none-match": etag}

Modified: couchdb/branches/rep_security/share/www/script/test/security_validation.js
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/www/script/test/security_validation.js?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/share/www/script/test/security_validation.js (original)
+++ couchdb/branches/rep_security/share/www/script/test/security_validation.js Thu Mar  5 06:14:36 2009
@@ -11,236 +11,236 @@
 // the License.
 
 couchTests.security_validation = function(debug) {
-    // This tests couchdb's security and validation features. This does
-    // not test authentication, except to use test authentication code made
-    // specifically for this testing. It is a WWWW-Authenticate scheme named
-    // X-Couch-Test-Auth, and the user names and passwords are hard coded
-    // on the server-side.
-    // 
-    // We could have used Basic authentication, however the XMLHttpRequest
-    // implementation for Firefox and Safari, and probably other browsers are
-    // broken (Firefox always prompts the user on 401 failures, Safari gives
-    // odd security errors when using different name/passwords, perhaps due
-    // to cross site scripting prevention).  These problems essentially make Basic
-    // authentication testing in the browser impossible. But while hard to
-    // test automated in the browser, Basic auth may still useful for real
-    // world use where these bugs/behaviors don't matter.
-    //
-    // So for testing purposes we are using this custom X-Couch-Test-Auth.
-    // It's identical to Basic auth, except it doesn't even base64 encode
-    // the "username:password" string, it's sent completely plain text.
-    // Firefox and Safari both deal with this correctly (which is to say
-    // they correctly do nothing special).
-  
-  
-    var db = new CouchDB("test_suite_db");
-    db.deleteDb();
-    db.createDb();
-    if (debug) debugger;
-  
-    run_on_modified_server(
+  // This tests couchdb's security and validation features. This does
+  // not test authentication, except to use test authentication code made
+  // specifically for this testing. It is a WWWW-Authenticate scheme named
+  // X-Couch-Test-Auth, and the user names and passwords are hard coded
+  // on the server-side.
+  // 
+  // We could have used Basic authentication, however the XMLHttpRequest
+  // implementation for Firefox and Safari, and probably other browsers are
+  // broken (Firefox always prompts the user on 401 failures, Safari gives
+  // odd security errors when using different name/passwords, perhaps due
+  // to cross site scripting prevention).  These problems essentially make Basic
+  // authentication testing in the browser impossible. But while hard to
+  // test automated in the browser, Basic auth may still useful for real
+  // world use where these bugs/behaviors don't matter.
+  //
+  // So for testing purposes we are using this custom X-Couch-Test-Auth.
+  // It's identical to Basic auth, except it doesn't even base64 encode
+  // the "username:password" string, it's sent completely plain text.
+  // Firefox and Safari both deal with this correctly (which is to say
+  // they correctly do nothing special).
+
+
+  var db = new CouchDB("test_suite_db");
+  db.deleteDb();
+  db.createDb();
+  if (debug) debugger;
+
+  run_on_modified_server(
     [{section: "httpd",
       key: "authentication_handler",
       value: "{couch_httpd, special_test_authentication_handler}"},
      {section:"httpd",
       key: "WWW-Authenticate",
       value:  "X-Couch-Test-Auth"}],
-    
+  
     function () {
-    // try saving document usin the wrong credentials
-    var wrongPasswordDb = new CouchDB("test_suite_db",
-      {"WWW-Authenticate": "X-Couch-Test-Auth Damien Katz:foo"}
-    );
-
-    try {
-      wrongPasswordDb.save({foo:1,author:"Damien Katz"});
-      T(false && "Can't get here. Should have thrown an error 1");
-    } catch (e) {
-      T(e.error == "unauthorized");
-      T(wrongPasswordDb.last_req.status == 401);
-    }
-  
-  
-    // Create the design doc that will run custom validation code
-    var designDoc = {
-      _id:"_design/test",
-      language: "javascript",
-      validate_doc_update: "(" + (function (newDoc, oldDoc, userCtx) {
-        // docs should have an author field.
-        if (!newDoc._deleted && !newDoc.author) {
-          throw {forbidden:
-              "Documents must have an author field"};
-        }
-        if (oldDoc && oldDoc.author != userCtx.name) {
-            throw {unauthorized:
-                "You are not the author of this document. You jerk."};
-        }
-      }).toString() + ")"
-    }
-
-    // Save a document normally
-    var userDb = new CouchDB("test_suite_db",
-      {"WWW-Authenticate": "X-Couch-Test-Auth Damien Katz:pecan pie"}
-    );
-  
-    T(userDb.save({_id:"testdoc", foo:1, author:"Damien Katz"}).ok);
-  
-    // Attempt to save the design as a non-admin
-    try {
-      userDb.save(designDoc);
-      T(false && "Can't get here. Should have thrown an error on design doc");
-    } catch (e) {
-      T(e.error == "unauthorized");
-      T(userDb.last_req.status == 401);
-    }
-  
-    // add user as admin
-    db.setAdmins(["Damien Katz"]);
-  
-    T(userDb.save(designDoc).ok);
-
-    // update the document
-    var doc = userDb.open("testdoc");
-    doc.foo=2;
-    T(userDb.save(doc).ok);
-  
-    // Save a document that's missing an author field.
-    try {
-      userDb.save({foo:1});
-      T(false && "Can't get here. Should have thrown an error 2");
-    } catch (e) {
-      T(e.error == "forbidden");
-      T(userDb.last_req.status == 403);
-    }
-
-    // Now attempt to update the document as a different user, Jan 
-    var user2Db = new CouchDB("test_suite_db",
-      {"WWW-Authenticate": "X-Couch-Test-Auth Jan Lehnardt:apple"}
-    );
-
-    var doc = user2Db.open("testdoc");
-    doc.foo=3;
-    try {
-      user2Db.save(doc);
-      T(false && "Can't get here. Should have thrown an error 3");
-    } catch (e) {
-      T(e.error == "unauthorized");
-      T(user2Db.last_req.status == 401);
-    }
-  
-    // Now have Damien change the author to Jan
-    doc = userDb.open("testdoc");
-    doc.author="Jan Lehnardt";
-    T(userDb.save(doc).ok);
-  
-    // Now update the document as Jan
-    doc = user2Db.open("testdoc");
-    doc.foo = 3;
-    T(user2Db.save(doc).ok);
-  
-    // Damien can't delete it
-    try {
-      userDb.deleteDoc(doc);
-      T(false && "Can't get here. Should have thrown an error 4");
-    } catch (e) {
-      T(e.error == "unauthorized");
-      T(userDb.last_req.status == 401);
-    }
-  
-    // Now delete document
-    T(user2Db.deleteDoc(doc).ok);
-  
-  
-    // Now test replication
-    var AuthHeaders = {"WWW-Authenticate": "X-Couch-Test-Auth Christopher Lenz:dog food"};
-    var host = CouchDB.host;
-    var dbPairs = [
-      {source:"test_suite_db_a",
-        target:"test_suite_db_b",
-        options:{}},
-      
-      {source:"test_suite_db_a",
-        target:"http://" + host + "/test_suite_db_b",
-        options: {target_headers: AuthHeaders}},
-          
-      {source:"http://" + host + "/test_suite_db_a",
-        target:"test_suite_db_b",
-        options: {source_headers: AuthHeaders}},
-          
-      {source:"http://" + host + "/test_suite_db_a",
-        target:"http://" + host + "/test_suite_db_b",
-        options:{source_headers: AuthHeaders, target_headers: AuthHeaders}},
-    ]
-    var adminDbA = new CouchDB("test_suite_db_a");
-    var adminDbB = new CouchDB("test_suite_db_b");
-    var dbA = new CouchDB("test_suite_db_a",
-        {"WWW-Authenticate": "X-Couch-Test-Auth Christopher Lenz:dog food"});
-    var dbB = new CouchDB("test_suite_db_b",
-        {"WWW-Authenticate": "X-Couch-Test-Auth Christopher Lenz:dog food"});
-    var xhr;
-    for (var testPair = 0; testPair < dbPairs.length; testPair++) {
-      var A = dbPairs[testPair].source
-      var B = dbPairs[testPair].target
-      var Options = dbPairs[testPair].options
-
-      adminDbA.deleteDb();
-      adminDbA.createDb();
-      adminDbB.deleteDb();
-      adminDbB.createDb();
-    
-      // save and replicate a documents that will and will not pass our design
-      // doc validation function.
-      dbA.save({_id:"foo1",value:"a",author:"Noah Slater"});
-      dbA.save({_id:"foo2",value:"a",author:"Christopher Lenz"});
-      dbA.save({_id:"bad1",value:"a"});
-
-      T(CouchDB.replicate(A, B).ok);
-      T(CouchDB.replicate(B, A).ok);
-
-      T(dbA.open("foo1"));
-      T(dbB.open("foo1"));
-      T(dbA.open("foo2"));
-      T(dbB.open("foo2"));
-    
-      // save the design doc to dbA
-      delete designDoc._rev; // clear rev from previous saves
-      adminDbA.save(designDoc);
-
-      // no affect on already saved docs
-      T(dbA.open("bad1"));
-    
-      // Update some docs on dbB. Since the design hasn't replicated, anything
-      // is allowed.
-    
-      // this edit will fail validation on replication to dbA (no author)
-      T(dbB.save({_id:"bad2",value:"a"}).ok);
-    
-      // this edit will fail security on replication to dbA (wrong author
-      //  replicating the change)
-      var foo1 = dbB.open("foo1");
-      foo1.value = "b";
-      dbB.save(foo1);
-    
-      // this is a legal edit
-      var foo2 = dbB.open("foo2");
-      foo2.value = "b";
-      dbB.save(foo2);
-    
-      var results = CouchDB.replicate(B, A);
-    
-      T(results.ok);
-    
-      T(results.history[0].docs_written == 2);
-      T(results.history[0].doc_write_failures == 1);
-    
-      // bad2 should not be on dbA
-      T(dbA.open("bad2") == null);
-    
-      // The edit to foo1 should not have replicated.
-      T(dbA.open("foo1").value == "a");
-    
-      // The edit to foo2 should have replicated.
-      T(dbA.open("foo2").value == "a");
-    }
+      // try saving document usin the wrong credentials
+      var wrongPasswordDb = new CouchDB("test_suite_db",
+        {"WWW-Authenticate": "X-Couch-Test-Auth Damien Katz:foo"}
+      );
+
+      try {
+        wrongPasswordDb.save({foo:1,author:"Damien Katz"});
+        T(false && "Can't get here. Should have thrown an error 1");
+      } catch (e) {
+        T(e.error == "unauthorized");
+        T(wrongPasswordDb.last_req.status == 401);
+      }
+
+
+      // Create the design doc that will run custom validation code
+      var designDoc = {
+        _id:"_design/test",
+        language: "javascript",
+        validate_doc_update: "(" + (function (newDoc, oldDoc, userCtx) {
+          // docs should have an author field.
+          if (!newDoc._deleted && !newDoc.author) {
+            throw {forbidden:
+                "Documents must have an author field"};
+          }
+          if (oldDoc && oldDoc.author != userCtx.name) {
+              throw {unauthorized:
+                  "You are not the author of this document. You jerk."};
+          }
+        }).toString() + ")"
+      }
+
+      // Save a document normally
+      var userDb = new CouchDB("test_suite_db",
+        {"WWW-Authenticate": "X-Couch-Test-Auth Damien Katz:pecan pie"}
+      );
+
+      T(userDb.save({_id:"testdoc", foo:1, author:"Damien Katz"}).ok);
+
+      // Attempt to save the design as a non-admin
+      try {
+        userDb.save(designDoc);
+        T(false && "Can't get here. Should have thrown an error on design doc");
+      } catch (e) {
+        T(e.error == "unauthorized");
+        T(userDb.last_req.status == 401);
+      }
+
+      // add user as admin
+      db.setAdmins(["Damien Katz"]);
+
+      T(userDb.save(designDoc).ok);
+
+      // update the document
+      var doc = userDb.open("testdoc");
+      doc.foo=2;
+      T(userDb.save(doc).ok);
+
+      // Save a document that's missing an author field.
+      try {
+        userDb.save({foo:1});
+        T(false && "Can't get here. Should have thrown an error 2");
+      } catch (e) {
+        T(e.error == "forbidden");
+        T(userDb.last_req.status == 403);
+      }
+
+      // Now attempt to update the document as a different user, Jan 
+      var user2Db = new CouchDB("test_suite_db",
+        {"WWW-Authenticate": "X-Couch-Test-Auth Jan Lehnardt:apple"}
+      );
+
+      var doc = user2Db.open("testdoc");
+      doc.foo=3;
+      try {
+        user2Db.save(doc);
+        T(false && "Can't get here. Should have thrown an error 3");
+      } catch (e) {
+        T(e.error == "unauthorized");
+        T(user2Db.last_req.status == 401);
+      }
+
+      // Now have Damien change the author to Jan
+      doc = userDb.open("testdoc");
+      doc.author="Jan Lehnardt";
+      T(userDb.save(doc).ok);
+
+      // Now update the document as Jan
+      doc = user2Db.open("testdoc");
+      doc.foo = 3;
+      T(user2Db.save(doc).ok);
+
+      // Damien can't delete it
+      try {
+        userDb.deleteDoc(doc);
+        T(false && "Can't get here. Should have thrown an error 4");
+      } catch (e) {
+        T(e.error == "unauthorized");
+        T(userDb.last_req.status == 401);
+      }
+
+      // Now delete document
+      T(user2Db.deleteDoc(doc).ok);
+
+
+      // Now test replication
+      var AuthHeaders = {"WWW-Authenticate": "X-Couch-Test-Auth Christopher Lenz:dog food"};
+      var host = CouchDB.host;
+      var dbPairs = [
+        {source:"test_suite_db_a",
+          target:"test_suite_db_b",
+          options:{}},
+    
+        {source:"test_suite_db_a",
+          target:"http://" + host + "/test_suite_db_b",
+          options: {target_headers: AuthHeaders}},
+        
+        {source:"http://" + host + "/test_suite_db_a",
+          target:"test_suite_db_b",
+          options: {source_headers: AuthHeaders}},
+        
+        {source:"http://" + host + "/test_suite_db_a",
+          target:"http://" + host + "/test_suite_db_b",
+          options:{source_headers: AuthHeaders, target_headers: AuthHeaders}},
+      ]
+      var adminDbA = new CouchDB("test_suite_db_a");
+      var adminDbB = new CouchDB("test_suite_db_b");
+      var dbA = new CouchDB("test_suite_db_a",
+          {"WWW-Authenticate": "X-Couch-Test-Auth Christopher Lenz:dog food"});
+      var dbB = new CouchDB("test_suite_db_b",
+          {"WWW-Authenticate": "X-Couch-Test-Auth Christopher Lenz:dog food"});
+      var xhr;
+      for (var testPair = 0; testPair < dbPairs.length; testPair++) {
+        var A = dbPairs[testPair].source
+        var B = dbPairs[testPair].target
+        var Options = dbPairs[testPair].options
+
+        adminDbA.deleteDb();
+        adminDbA.createDb();
+        adminDbB.deleteDb();
+        adminDbB.createDb();
+  
+        // save and replicate a documents that will and will not pass our design
+        // doc validation function.
+        dbA.save({_id:"foo1",value:"a",author:"Noah Slater"});
+        dbA.save({_id:"foo2",value:"a",author:"Christopher Lenz"});
+        dbA.save({_id:"bad1",value:"a"});
+
+        T(CouchDB.replicate(A, B).ok);
+        T(CouchDB.replicate(B, A).ok);
+
+        T(dbA.open("foo1"));
+        T(dbB.open("foo1"));
+        T(dbA.open("foo2"));
+        T(dbB.open("foo2"));
+  
+        // save the design doc to dbA
+        delete designDoc._rev; // clear rev from previous saves
+        adminDbA.save(designDoc);
+
+        // no affect on already saved docs
+        T(dbA.open("bad1"));
+  
+        // Update some docs on dbB. Since the design hasn't replicated, anything
+        // is allowed.
+  
+        // this edit will fail validation on replication to dbA (no author)
+        T(dbB.save({_id:"bad2",value:"a"}).ok);
+  
+        // this edit will fail security on replication to dbA (wrong author
+        //  replicating the change)
+        var foo1 = dbB.open("foo1");
+        foo1.value = "b";
+        dbB.save(foo1);
+  
+        // this is a legal edit
+        var foo2 = dbB.open("foo2");
+        foo2.value = "b";
+        dbB.save(foo2);
+  
+        var results = CouchDB.replicate(B, A);
+  
+        T(results.ok);
+  
+        T(results.history[0].docs_written == 2);
+        T(results.history[0].doc_write_failures == 1);
+  
+        // bad2 should not be on dbA
+        T(dbA.open("bad2") == null);
+  
+        // The edit to foo1 should not have replicated.
+        T(dbA.open("foo1").value == "a");
+  
+        // The edit to foo2 should have replicated.
+        T(dbA.open("foo2").value == "a");
+      }
     });
-};
\ No newline at end of file
+};

Modified: couchdb/branches/rep_security/share/www/script/test/uuids.js
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/www/script/test/uuids.js?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/share/www/script/test/uuids.js (original)
+++ couchdb/branches/rep_security/share/www/script/test/uuids.js Thu Mar  5 06:14:36 2009
@@ -11,20 +11,33 @@
 // the License.
 
 couchTests.uuids = function(debug) {
+  var testHashBustingHeaders = function(xhr) {
+    T(xhr.getResponseHeader("Cache-Control").match(/no-cache/));
+    T(xhr.getResponseHeader("Pragma") == "no-cache");
+    
+    var currentTime = new Date();
+    var expiresHeader = Date.parse(xhr.getResponseHeader("Expires"));
+    var dateHeader = Date.parse(xhr.getResponseHeader("Date")); 
+    
+    T(expiresHeader < currentTime);
+    T(currentTime - dateHeader < 3000);
+  };
+    
   var db = new CouchDB("test_suite_db");
   db.deleteDb();
   db.createDb();
   if (debug) debugger;
   
   // a single UUID without an explicit count
-  var xhr = CouchDB.request("POST", "/_uuids");
+  var xhr = CouchDB.request("GET", "/_uuids");
   T(xhr.status == 200);
   var result = JSON.parse(xhr.responseText);
   T(result.uuids.length == 1);
   var first = result.uuids[0];
+  testHashBustingHeaders(xhr);
 
   // a single UUID with an explicit count
-  xhr = CouchDB.request("POST", "/_uuids?count=1");
+  xhr = CouchDB.request("GET", "/_uuids?count=1");
   T(xhr.status == 200);
   result = JSON.parse(xhr.responseText);
   T(result.uuids.length == 1);
@@ -32,7 +45,7 @@
   T(first != second);
 
   // no collisions with 1,000 UUIDs
-  xhr = CouchDB.request("POST", "/_uuids?count=1000");
+  xhr = CouchDB.request("GET", "/_uuids?count=1000");
   T(xhr.status == 200);
   result = JSON.parse(xhr.responseText);
   T( result.uuids.length == 1000 );
@@ -43,5 +56,7 @@
     seen[id] = 1;
   }
   
-  // check our library
+  // ensure we return a 405 on POST
+  xhr = CouchDB.request("POST", "/_uuids?count=1000");
+  T(xhr.status == 405);
 };

Modified: couchdb/branches/rep_security/share/www/script/test/view_errors.js
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/www/script/test/view_errors.js?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/share/www/script/test/view_errors.js (original)
+++ couchdb/branches/rep_security/share/www/script/test/view_errors.js Thu Mar  5 06:14:36 2009
@@ -40,4 +40,13 @@
     emit([doc._id, doc.undef], null);
   });
   T(results.total_rows == 0);
+  
+  // querying a view with invalid params should give a resonable error message
+  var xhr = CouchDB.request("POST", "/test_suite_db/_temp_view?startkey=foo", {
+    headers: {"Content-Type": "application/json"},
+    body: JSON.stringify({language: "javascript", 
+      map : "function(doc){emit(doc.integer)}"
+    })
+  });
+  T(JSON.parse(xhr.responseText).error == "invalid_json");
 };

Modified: couchdb/branches/rep_security/share/www/style/layout.css
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/share/www/style/layout.css?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/share/www/style/layout.css (original)
+++ couchdb/branches/rep_security/share/www/style/layout.css Thu Mar  5 06:14:36 2009
@@ -336,8 +336,8 @@
 
 /* Database table */
 
-#databases thead th.size, #databases thead th.count, #databases thead th.seq,
-#databases tbody td.size, #databases tbody td.count, #databases tbody td.seq {
+#databases thead th.apps, #databases thead th.size, #databases thead th.count, #databases thead th.seq,
+#databases thead th.apps, #databases tbody td.size, #databases tbody td.count, #databases tbody td.seq {
   text-align: right;
 }
 

Modified: couchdb/branches/rep_security/src/couchdb/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/src/couchdb/Makefile.am?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/src/couchdb/Makefile.am (original)
+++ couchdb/branches/rep_security/src/couchdb/Makefile.am Thu Mar  5 06:14:36 2009
@@ -51,20 +51,23 @@
     couch_external_manager.erl \
     couch_external_server.erl \
     couch_file.erl \
-    couch_file_stats.erl \
     couch_httpd.erl \
     couch_httpd_db.erl \
     couch_httpd_external.erl \
     couch_httpd_show.erl \
     couch_httpd_view.erl \
     couch_httpd_misc_handlers.erl \
+    couch_httpd_stats_handlers.erl \
     couch_key_tree.erl \
     couch_log.erl \
     couch_os_process.erl \
     couch_query_servers.erl \
+    couch_ref_counter.erl \
     couch_rep.erl \
     couch_server.erl \
     couch_server_sup.erl \
+    couch_stats_aggregator.erl \
+    couch_stats_collector.erl \
     couch_stream.erl \
     couch_task_status.erl \
     couch_util.erl \
@@ -73,7 +76,7 @@
     couch_view_group.erl \
     couch_db_updater.erl
 
-EXTRA_DIST = $(source_files) couch_db.hrl
+EXTRA_DIST = $(source_files) couch_db.hrl couch_stats.hrl
 
 compiled_files = \
     couch.app \
@@ -88,20 +91,23 @@
     couch_external_manager.beam \
     couch_external_server.beam \
     couch_file.beam \
-    couch_file_stats.beam \
     couch_httpd.beam \
     couch_httpd_db.beam \
     couch_httpd_external.beam \
     couch_httpd_show.beam \
     couch_httpd_view.beam \
     couch_httpd_misc_handlers.beam \
+    couch_httpd_stats_handlers.beam \
     couch_key_tree.beam \
     couch_log.beam \
     couch_os_process.beam \
     couch_query_servers.beam \
+    couch_ref_counter.beam \
     couch_rep.beam \
     couch_server.beam \
     couch_server_sup.beam \
+    couch_stats_aggregator.beam \
+    couch_stats_collector.beam \
     couch_stream.beam \
     couch_task_status.beam \
     couch_util.beam \
@@ -150,7 +156,7 @@
 # $(ERL) -noshell -run edoc_run files [\"$<\"]
 
 %.beam: %.erl couch_db.hrl
-	$(ERLC) $<
+	$(ERLC) ${TEST} $<;
 
 install-data-hook:
 	if test -f "$(DESTDIR)/$(couchprivlibdir)/couch_erl_driver"; then \

Modified: couchdb/branches/rep_security/src/couchdb/couch_db.erl
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/src/couchdb/couch_db.erl?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/src/couchdb/couch_db.erl (original)
+++ couchdb/branches/rep_security/src/couchdb/couch_db.erl Thu Mar  5 06:14:36 2009
@@ -14,8 +14,8 @@
 -behaviour(gen_server).
 
 -export([open/2,close/1,create/2,start_compact/1,get_db_info/1]).
--export([open_ref_counted/2,num_refs/1,monitor/1,count_changes_since/2]).
--export([update_doc/3,update_docs/4,update_docs/2,update_docs/3]).
+-export([open_ref_counted/2,is_idle/1,monitor/1,count_changes_since/2]).
+-export([update_doc/3,update_docs/4,update_docs/2,update_docs/3,delete_doc/3]).
 -export([get_doc_info/2,open_doc/2,open_doc/3,open_doc_revs/4]).
 -export([get_missing_revs/2,name/1,doc_to_tree/1,get_update_seq/1,get_committed_update_seq/1]).
 -export([enum_docs/4,enum_docs/5,enum_docs_since/4,enum_docs_since/5]).
@@ -24,7 +24,6 @@
 -export([start_link/3,make_doc/2,set_admins/2,get_admins/1,ensure_full_commit/1]).
 -export([init/1,terminate/2,handle_call/3,handle_cast/2,code_change/3,handle_info/2]).
 
-
 -include("couch_db.hrl").
 
 
@@ -68,15 +67,15 @@
     ok = gen_server:call(UpdatePid, full_commit, infinity),
     {ok, StartTime}.
 
-close(#db{fd=Fd}) ->
-    couch_file:drop_ref(Fd).
+close(#db{fd_ref_counter=RefCntr}) ->
+    couch_ref_counter:drop(RefCntr).
 
 open_ref_counted(MainPid, UserCtx) ->
-    {ok, Db} = gen_server:call(MainPid, {open_ref_counted_instance, self()}),
+    {ok, Db} = gen_server:call(MainPid, {open_ref_count, self()}),
     {ok, Db#db{user_ctx=UserCtx}}.
 
-num_refs(MainPid) ->
-    gen_server:call(MainPid, num_refs).
+is_idle(MainPid) ->
+    gen_server:call(MainPid, is_idle).
 
 monitor(#db{main_pid=MainPid}) ->
     erlang:monitor(process, MainPid).
@@ -84,10 +83,16 @@
 start_compact(#db{update_pid=Pid}) ->
     gen_server:cast(Pid, start_compact).
 
+delete_doc(Db, Id, Revisions) ->
+    DeletedDocs = [#doc{id=Id, revs=[Rev], deleted=true} || Rev <- Revisions],
+    {ok, [Result]} = update_docs(Db, DeletedDocs, []),
+    {ok, Result}.
+
 open_doc(Db, IdOrDocInfo) ->
     open_doc(Db, IdOrDocInfo, []).
 
 open_doc(Db, Id, Options) ->
+    couch_stats_collector:increment({couchdb, database_reads}),
     case open_doc_int(Db, Id, Options) of
     {ok, #doc{deleted=true}=Doc} ->
         case lists:member(deleted, Options) of
@@ -101,6 +106,7 @@
     end.
 
 open_doc_revs(Db, Id, Revs, Options) ->
+    couch_stats_collector:increment({couchdb, database_reads}),
     [Result] = open_doc_revs_int(Db, [{Id, Revs}], Options),
     Result.
 
@@ -398,6 +404,7 @@
     end.
 
 update_docs(Db, Docs, Options, replicated_changes) ->
+    couch_stats_collector:increment({couchdb, database_writes}),
     DocBuckets = group_alike_docs(Docs),
     
     case should_validate(Db, Docs) of
@@ -416,6 +423,7 @@
     {ok, DocErrors};
     
 update_docs(Db, Docs, Options, interactive_edit) ->
+    couch_stats_collector:increment({couchdb, database_writes}),
     % go ahead and generate the new revision ids for the documents.
     Docs2 = lists:map(
         fun(#doc{id=Id,revs={Start, RevIds}}=Doc) ->
@@ -501,8 +509,14 @@
                 % written to a different file
                 SizeAcc + Len;
             {_Key, {_Type, Bin}} when is_binary(Bin) ->
+                % we have a new binary to write
                 SizeAcc + size(Bin);
+            {_Key, {_Type, {Fun, undefined}}} when is_function(Fun) ->
+                % function without a known length
+                % we'll have to alloc as we go with this one, for now, nothing
+                SizeAcc;
             {_Key, {_Type, {Fun, Len}}} when is_function(Fun) ->
+                % function to yield binary data with known length
                 SizeAcc + Len
             end
         end,
@@ -510,7 +524,6 @@
 
     {ok, OutputStream} = couch_stream:open(Fd),
     ok = couch_stream:ensure_buffer(OutputStream, PreAllocSize),
-
     NewBins = lists:map(
         fun({Key, {Type, BinValue}}) ->
             NewBinValue =
@@ -537,6 +550,17 @@
             Bin when is_binary(Bin) ->
                 {ok, StreamPointer} = couch_stream:write(OutputStream, Bin),
                 {Fd, StreamPointer, size(Bin)};
+            {StreamFun, undefined} when is_function(StreamFun) ->
+                % max_attachment_chunk_size control the max we buffer in memory
+                MaxChunkSize = list_to_integer(couch_config:get("couchdb", 
+                    "max_attachment_chunk_size","4294967296")),
+                WriterFun = make_writer_fun(OutputStream),
+                % StreamFun(MaxChunkSize, WriterFun) 
+                % will call our WriterFun
+                % once for each chunk of the attachment.
+                {ok, {TotalLength, NewStreamPointer}} = 
+                    StreamFun(MaxChunkSize, WriterFun, {0, nil}),
+                {Fd, NewStreamPointer, TotalLength};                
             {Fun, Len} when is_function(Fun) ->
                 {ok, StreamPointer} =
                         write_streamed_attachment(OutputStream, Fun, Len, nil),
@@ -546,8 +570,27 @@
         end, Bins),
 
     {ok, _FinalPos} = couch_stream:close(OutputStream),
-
     Doc#doc{attachments = NewBins}.
+
+
+make_writer_fun(Stream) ->
+    % WriterFun({Length, Binary}, State)
+    % WriterFun({0, _Footers}, State)
+    % Called with Length == 0 on the last time.
+    % WriterFun returns NewState.
+    fun
+        ({0, _Footers}, {FinalLen, SpFin}) ->
+            % last block, return the final tuple
+            {ok, {FinalLen, SpFin}};
+        ({Length, Bin}, {Total, nil}) ->
+            % save StreamPointer 
+            {ok, StreamPointer} = couch_stream:write(Stream, Bin),
+            {Total+Length, StreamPointer};
+        ({Length, Bin}, {Total, SpAcc}) ->
+            % write the Bin to disk 
+            {ok, _Sp} = couch_stream:write(Stream, Bin),
+            {Total+Length, SpAcc}
+    end.
     
 write_streamed_attachment(_Stream, _F, 0, SpAcc) ->
     {ok, SpAcc};
@@ -597,23 +640,28 @@
 
 init({DbName, Filepath, Fd, Options}) ->
     {ok, UpdaterPid} = gen_server:start_link(couch_db_updater, {self(), DbName, Filepath, Fd, Options}, []),
-    ok = couch_file:add_ref(Fd),
-    gen_server:call(UpdaterPid, get_db).
+    {ok, #db{fd_ref_counter=RefCntr}=Db} = gen_server:call(UpdaterPid, get_db),
+    couch_ref_counter:add(RefCntr),
+    {ok, Db}.
 
 terminate(_Reason, _Db) ->
     ok.
     
-handle_call({open_ref_counted_instance, OpenerPid}, _From, #db{fd=Fd}=Db) ->
-    ok = couch_file:add_ref(Fd, OpenerPid),
+handle_call({open_ref_count, OpenerPid}, _, #db{fd_ref_counter=RefCntr}=Db) ->
+    ok = couch_ref_counter:add(RefCntr, OpenerPid),
     {reply, {ok, Db}, Db};
-handle_call(num_refs, _From, #db{fd=Fd}=Db) ->
-    {reply, couch_file:num_refs(Fd) - 1, Db};
-handle_call({db_updated, #db{fd=NewFd}=NewDb}, _From, #db{fd=OldFd}) ->
-    case NewFd == OldFd of
+handle_call(is_idle, _From,
+        #db{fd_ref_counter=RefCntr, compactor_pid=Compact}=Db) ->
+    % Idle means no referrers. Unless in the middle of a compaction file switch, 
+    % there are always at least 2 referrers, couch_db_updater and us.
+    {reply, (Compact == nil) and (couch_ref_counter:count(RefCntr) == 2), Db};
+handle_call({db_updated, #db{fd_ref_counter=NewRefCntr}=NewDb}, _From,
+        #db{fd_ref_counter=OldRefCntr}) ->
+    case NewRefCntr == OldRefCntr of
     true -> ok;
     false ->
-        couch_file:add_ref(NewFd),
-        couch_file:drop_ref(OldFd)
+        couch_ref_counter:add(NewRefCntr),
+        couch_ref_counter:drop(OldRefCntr)
     end,
     {reply, ok, NewDb}.
 
@@ -763,4 +811,4 @@
         }.
     
     
-    
\ No newline at end of file
+    

Modified: couchdb/branches/rep_security/src/couchdb/couch_db.hrl
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/src/couchdb/couch_db.hrl?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/src/couchdb/couch_db.hrl (original)
+++ couchdb/branches/rep_security/src/couchdb/couch_db.hrl Thu Mar  5 06:14:36 2009
@@ -122,6 +122,7 @@
     compactor_pid=nil,
     instance_start_time, % number of microsecs since jan 1 1970 as a binary string
     fd,
+    fd_ref_counter,
     header = #db_header{},
     summary_stream,
     fulldocinfo_by_id_btree,
@@ -160,15 +161,21 @@
     send_row
 }).
 
+-record(reduce_fold_helper_funs, {
+    start_response,
+    send_row
+}).
+
 -record(extern_resp_args, {
     code = 200,
+    stop = false,
     data = <<>>,
     ctype = "application/json",
     headers = []
 }).
 
 -record(group,
-    {type=view, % can also be slow_view
+    {type=view, % can also be temp_view
     sig=nil,
     db=nil,
     fd=nil,

Modified: couchdb/branches/rep_security/src/couchdb/couch_db_updater.erl
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/src/couchdb/couch_db_updater.erl?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/src/couchdb/couch_db_updater.erl (original)
+++ couchdb/branches/rep_security/src/couchdb/couch_db_updater.erl Thu Mar  5 06:14:36 2009
@@ -22,7 +22,6 @@
 -define(HEADER_SIG, <<$g, $m, $k, 0>>).
 
 init({MainPid, DbName, Filepath, Fd, Options}) ->
-    link(Fd),
     case lists:member(create, Options) of
     true ->
         % create a new header and writes it to the file
@@ -38,8 +37,8 @@
     Db2 = refresh_validate_doc_funs(Db),
     {ok, Db2#db{main_pid=MainPid}}.
 
-terminate(_Reason, Db) ->
-    close_db(Db).
+terminate(_Reason, _Db) ->
+    ok.
 
 handle_call(get_db, _From, Db) ->
     {reply, {ok, Db}, Db};
@@ -173,7 +172,7 @@
         ok = file:rename(CompactFilepath, Filepath),
         
         couch_stream:close(Db#db.summary_stream),
-        couch_file:close_maybe(Db#db.fd),
+        couch_ref_counter:drop(Db#db.fd_ref_counter),
             
         ok = gen_server:call(Db#db.main_pid, {db_updated, NewDb2}),
         ?LOG_INFO("Compaction for db \"~s\" completed.", [Db#db.name]),
@@ -282,10 +281,11 @@
     {MegaSecs, Secs, MicroSecs} = now(),
     StartTime = ?l2b(io_lib:format("~p",
             [(MegaSecs*1000000*1000000) + (Secs*1000000) + MicroSecs])),
-    
+    {ok, RefCntr} = couch_ref_counter:start([Fd]),
     #db{
         update_pid=self(),
         fd=Fd,
+        fd_ref_counter = RefCntr,
         header=Header,
         summary_stream = SummaryStream,
         fulldocinfo_by_id_btree = IdBtree,
@@ -300,10 +300,6 @@
         }.
 
 
-close_db(#db{fd=Fd,summary_stream=Ss}) ->
-    couch_file:close(Fd),
-    couch_stream:close(Ss).
-
 refresh_validate_doc_funs(Db) ->
     {ok, DesignDocs} = get_design_docs(Db),
     ProcessDocFuns = lists:flatmap(
@@ -632,8 +628,7 @@
         ok = couch_file:write_header(Fd, ?HEADER_SIG, Header=#db_header{})
     end,
     NewDb = init_db(Name, CompactFile, Fd, Header),
-    NewDb2 = copy_compact(Db, NewDb, Retry),
-    close_db(NewDb2),
+    _NewDb2 = copy_compact(Db, NewDb, Retry),
     
     gen_server:cast(Db#db.update_pid, {compact_done, CompactFile}).
     

Modified: couchdb/branches/rep_security/src/couchdb/couch_doc.erl
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/src/couchdb/couch_doc.erl?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/src/couchdb/couch_doc.erl (original)
+++ couchdb/branches/rep_security/src/couchdb/couch_doc.erl Thu Mar  5 06:14:36 2009
@@ -112,7 +112,7 @@
     transfer_fields(Props, #doc{body=[]});
 
 from_json_obj(_Other) ->
-    throw({invalid_json_object, "Document must be a JSON object"}).
+    throw({bad_request, "Document must be a JSON object"}).
 
 parse_rev(Rev) when is_binary(Rev) ->
     parse_rev(?b2l(Rev));
@@ -131,11 +131,18 @@
     Doc#doc{body={lists:reverse(Fields)}};
     
 transfer_fields([{<<"_id">>, Id} | Rest], Doc) when is_binary(Id) ->
+    case Id of
+    <<"_design/", _/binary>> -> ok;
+    <<"_local/", _/binary>> -> ok;
+    <<"_", _/binary>> ->
+        throw({bad_request, <<"Only reserved document ids may start with underscore.">>});
+    _Else -> ok
+    end,
     transfer_fields(Rest, Doc#doc{id=Id});
     
 transfer_fields([{<<"_id">>, Id} | _Rest], _Doc) ->
     ?LOG_DEBUG("Document id is not a string: ~p", [Id]),
-    throw({invalid_document_id, "Document id is not a string"});
+    throw({bad_request, <<"Document id must be a string">>});
     
 transfer_fields([{<<"_rev">>, Rev} | Rest], #doc{revs={0, []}}=Doc) ->
     {Pos, RevId} = parse_rev(Rev),

Modified: couchdb/branches/rep_security/src/couchdb/couch_file.erl
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/src/couchdb/couch_file.erl?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/src/couchdb/couch_file.erl (original)
+++ couchdb/branches/rep_security/src/couchdb/couch_file.erl Thu Mar  5 06:14:36 2009
@@ -20,7 +20,6 @@
 -export([open/1, open/2, close/1, pread/3, pwrite/3, expand/2, bytes/1, sync/1]).
 -export([append_term/2, pread_term/2,write_header/3, read_header/2, truncate/2]).
 -export([init/1, terminate/2, handle_call/3, handle_cast/2, code_change/3, handle_info/2]).
--export([close_maybe/1,drop_ref/1,drop_ref/2,add_ref/1,add_ref/2,num_refs/1]).
 
 %%----------------------------------------------------------------------
 %% Args:   Valid Options are [create] and [create,overwrite].
@@ -36,7 +35,6 @@
     case gen_server:start_link(couch_file,
             {Filepath, Options, self(), Ref = make_ref()}, []) of
     {ok, Fd} ->
-        couch_file_stats:track_file(Fd),
         {ok, Fd};
     ignore ->
         % get the error
@@ -168,26 +166,6 @@
     Result = gen_server:cast(Fd, close),
     catch unlink(Fd),
     Result.
-    
-close_maybe(Fd) ->
-    catch unlink(Fd),
-    catch gen_server:cast(Fd, close_maybe).
-
-drop_ref(Fd) ->
-    drop_ref(Fd, self()).
-    
-drop_ref(Fd, Pid) ->
-    gen_server:cast(Fd, {drop_ref, Pid}).
-
-
-add_ref(Fd) ->
-    add_ref(Fd, self()).
-
-add_ref(Fd, Pid) ->
-    gen_server:call(Fd, {add_ref, Pid}).
-
-num_refs(Fd) ->
-    gen_server:call(Fd, num_refs).
 
 
 write_header(Fd, Prefix, Data) ->
@@ -307,12 +285,14 @@
                 true ->
                     {ok, 0} = file:position(Fd, 0),
                     ok = file:truncate(Fd),
+                    couch_stats_collector:increment({couchdb, open_os_files}),
                     {ok, Fd};
                 false ->
                     ok = file:close(Fd),
                     init_status_error(ReturnPid, Ref, file_exists)
                 end;
             false ->
+                couch_stats_collector:increment({couchdb, open_os_files}),
                 {ok, Fd}
             end;
         Error ->
@@ -324,6 +304,7 @@
         {ok, Fd_Read} ->
             {ok, Fd} = file:open(Filepath, [read, write, raw, binary]),
             ok = file:close(Fd_Read),
+            couch_stats_collector:increment({couchdb, open_os_files}),
             {ok, Fd};
         Error ->
             init_status_error(ReturnPid, Ref, Error)
@@ -332,6 +313,7 @@
 
 
 terminate(_Reason, _Fd) ->
+    couch_stats_collector:decrement({couchdb, open_os_files}),
     ok.
 
 
@@ -357,66 +339,15 @@
 handle_call({pread_bin, Pos}, _From, Fd) ->
     {ok, <<TermLen:32>>} = file:pread(Fd, Pos, 4),
     {ok, Bin} = file:pread(Fd, Pos + 4, TermLen),
-    {reply, {ok, Bin}, Fd};
-handle_call({add_ref, Pid},_From, Fd) ->
-    case get(Pid) of
-    undefined ->
-        put(Pid, {erlang:monitor(process, Pid), 1});
-    {MonRef, RefCnt} ->
-        put(Pid, {MonRef, RefCnt + 1})
-    end,
-    {reply, ok, Fd};
-handle_call(num_refs, _From, Fd) ->
-    {monitors, Monitors} =  process_info(self(), monitors),
-    {reply, length(Monitors), Fd}.
+    {reply, {ok, Bin}, Fd}.
 
 
 handle_cast(close, Fd) ->
-    {stop,normal,Fd};
-handle_cast(close_maybe, Fd) ->
-    maybe_close_async(Fd);
-handle_cast({drop_ref, Pid}, Fd) ->
-    case get(Pid) of
-    {MonRef, 1} ->
-        erase(Pid),
-        % don't check return of demonitor. The process could haved crashed causing
-        % the {'DOWN', ...} message to be sent and the process unmonitored.
-        erlang:demonitor(MonRef, [flush]);
-    {MonRef, Num} ->
-        put(Pid, {MonRef, Num-1})
-    end,
-    maybe_close_async(Fd).
+    {stop,normal,Fd}.
 
 
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
-handle_info({'EXIT', _Pid, Reason}, Fd) ->
-    {stop, Reason, Fd};
-handle_info({'DOWN', MonitorRef, _Type, Pid, _Info}, Fd) ->
-    {MonitorRef, _RefCount} = erase(Pid),
-    maybe_close_async(Fd).
-
-
-
-should_close(_Fd) ->
-    case process_info(self(), links) of
-    {links, [_]} ->
-        % no linkers left (except our fd port). What about monitors?
-        case process_info(self(), monitors) of
-        {monitors, []} ->
-            true;
-        _ ->
-            false
-        end;
-    {links,  [_|_]} ->
-        false
-    end.
-
-maybe_close_async(Fd) ->
-    case should_close(Fd) of
-    true ->
-        {stop,normal,Fd};
-    false ->
-        {noreply,Fd}
-    end.
+handle_info({'EXIT', _, Reason}, Fd) ->
+    {stop, Reason, Fd}.

Modified: couchdb/branches/rep_security/src/couchdb/couch_httpd.erl
URL: http://svn.apache.org/viewvc/couchdb/branches/rep_security/src/couchdb/couch_httpd.erl?rev=750332&r1=750331&r2=750332&view=diff
==============================================================================
--- couchdb/branches/rep_security/src/couchdb/couch_httpd.erl (original)
+++ couchdb/branches/rep_security/src/couchdb/couch_httpd.erl Thu Mar  5 06:14:36 2009
@@ -16,12 +16,12 @@
 -export([start_link/0, stop/0, handle_request/3]).
 
 -export([header_value/2,header_value/3,qs_value/2,qs_value/3,qs/1,path/1,absolute_uri/2]).
--export([verify_is_server_admin/1,unquote/1,quote/1,recv/2,error_info/1]).
+-export([verify_is_server_admin/1,unquote/1,quote/1,recv/2,recv_chunked/4,error_info/1]).
 -export([parse_form/1,json_body/1,body/1,doc_etag/1, make_etag/1, etag_respond/3]).
 -export([primary_header_value/2,partition/1,serve_file/3]).
 -export([start_chunked_response/3,send_chunk/2]).
 -export([start_json_response/2, start_json_response/3, end_json_response/1]).
--export([send_response/4,send_method_not_allowed/2,send_error/4]).
+-export([send_response/4,send_method_not_allowed/2,send_error/4, send_redirect/2]).
 -export([send_json/2,send_json/3,send_json/4]).
 -export([default_authentication_handler/1,special_test_authentication_handler/1]).
 
@@ -102,6 +102,7 @@
     
 
 handle_request(MochiReq, UrlHandlers, DbUrlHandlers) ->
+    statistics(runtime), % prepare request_time counter, see end of function
     AuthenticationFun = make_arity_1_fun(
             couch_config:get("httpd", "authentication_handler")),
     % for the path, use the raw path with the query string and fragment
@@ -123,11 +124,8 @@
         mochiweb_headers:to_list(MochiReq:get(headers))
     ]),
     
-    Method =
+    Method1 =
     case MochiReq:get(method) of
-        % alias HEAD to GET as mochiweb takes care of stripping the body
-        'HEAD' -> 'GET';
-        
         % already an atom
         Meth when is_atom(Meth) -> Meth;
         
@@ -135,6 +133,15 @@
         % possible (if any module references the atom, then it's existing).
         Meth -> couch_util:to_existing_atom(Meth)
     end,
+    
+    increment_method_stats(Method1),
+    
+    % alias HEAD to GET as mochiweb takes care of stripping the body
+    Method = case Method1 of
+        'HEAD' -> 'GET';
+        Other -> Other
+    end,
+
     HttpReq = #httpd{
         mochi_req = MochiReq,
         method = Method,
@@ -151,7 +158,7 @@
     catch
         throw:Error ->
             send_error(HttpReq, Error);
-        Tag:Error when Error == foo ->
+        Tag:Error ->
             ?LOG_ERROR("Uncaught error in HTTP request: ~p",[{Tag, Error}]),
             ?LOG_DEBUG("Stacktrace: ~p",[erlang:get_stacktrace()]),
             send_error(HttpReq, Error)
@@ -163,8 +170,13 @@
         RawUri,
         Resp:get(code)
     ]),
+    {_TotalRuntime, RequestTime} = statistics(runtime),
+    couch_stats_collector:record({couchdb, request_time}, RequestTime),
+    couch_stats_collector:increment({httpd, requests}),
     {ok, Resp}.
 
+increment_method_stats(Method) ->
+    couch_stats_collector:increment({httpd_request_methods, Method}).
 
 special_test_authentication_handler(Req) ->
     case header_value(Req, "WWW-Authenticate") of
@@ -260,6 +272,12 @@
 recv(#httpd{mochi_req=MochiReq}, Len) ->
     MochiReq:recv(Len).
 
+recv_chunked(#httpd{mochi_req=MochiReq}, MaxChunkSize, ChunkFun, InitState) ->
+    % Fun is called once with each chunk
+    % Fun({Length, Binary}, State)
+    % called with Length == 0 on the last time.
+    MochiReq:stream_body(MaxChunkSize, ChunkFun, InitState).
+
 body(#httpd{mochi_req=MochiReq}) ->
     % Maximum size of document PUT request body (4GB)
     MaxSize = list_to_integer(
@@ -319,6 +337,7 @@
 
 
 start_chunked_response(#httpd{mochi_req=MochiReq}, Code, Headers) ->
+    couch_stats_collector:increment({httpd_status_codes, Code}),
     {ok, MochiReq:respond({Code, Headers ++ server_header(), chunked})}.
 
 send_chunk(Resp, Data) ->
@@ -326,6 +345,7 @@
     {ok, Resp}.
 
 send_response(#httpd{mochi_req=MochiReq}, Code, Headers, Body) ->
+    couch_stats_collector:increment({httpd_status_codes, Code}),
     if Code >= 400 ->
         ?LOG_DEBUG("HTTPd ~p error response:~n ~s", [Code, Body]);
     true -> ok
@@ -346,7 +366,8 @@
         {"Content-Type", negotiate_content_type(Req)},
         {"Cache-Control", "must-revalidate"}
     ],
-    send_response(Req, Code, DefaultHeaders ++ Headers, ?JSON_ENCODE(Value)).
+    send_response(Req, Code, DefaultHeaders ++ Headers,
+                  list_to_binary([?JSON_ENCODE(Value), $\n])).
 
 start_json_response(Req, Code) ->
     start_json_response(Req, Code, []).
@@ -359,6 +380,7 @@
     start_chunked_response(Req, Code, DefaultHeaders ++ Headers).
 
 end_json_response(Resp) ->
+    send_chunk(Resp, [$\n]),
     send_chunk(Resp, []).
 
 
@@ -407,6 +429,9 @@
         {[{<<"error">>,  ErrorStr},
          {<<"reason">>, ReasonStr}]}).
 
+ send_redirect(Req, Path) ->
+     Headers = [{"Location", couch_httpd:absolute_uri(Req, Path)}],
+     send_response(Req, 301, Headers, <<>>).
 
 negotiate_content_type(#httpd{mochi_req=MochiReq}) ->
     %% Determine the appropriate Content-Type header for a JSON response