<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -10,6 +10,7 @@ erl_crash.dump
 configure
 autom4te.cache
 build-aux
+*.diff
 
 # ./configure
 </diff>
      <filename>.gitignore</filename>
    </modified>
    <modified>
      <diff>@@ -21,6 +21,9 @@ try {
   sandbox.toJSON = toJSON;
   sandbox.respondWith = respondWith;
   sandbox.registerType = registerType;
+  sandbox.start = start;
+  sandbox.send = send;
+  sandbox.getRow = getRow;
 } catch (e) {}
 
 // Commands are in the form of json arrays:
@@ -31,21 +34,19 @@ try {
 var line, cmd, cmdkey;
 
 var dispatch = {
-  &quot;reset&quot;      : State.reset,
-  &quot;add_fun&quot;    : State.addFun,
-  &quot;map_doc&quot;    : Views.mapDoc,
-  &quot;reduce&quot;     : Views.reduce,
-  &quot;rereduce&quot;   : Views.rereduce,
-  &quot;validate&quot;   : Validate.validate,
-  &quot;show_doc&quot;   : Render.showDoc,
-  &quot;list_begin&quot; : Render.listBegin,
-  &quot;list_row&quot;   : Render.listRow,
-  &quot;list_tail&quot;  : Render.listTail 
+  &quot;reset&quot;    : State.reset,
+  &quot;add_fun&quot;  : State.addFun,
+  &quot;map_doc&quot;  : Views.mapDoc,
+  &quot;reduce&quot;   : Views.reduce,
+  &quot;rereduce&quot; : Views.rereduce,
+  &quot;validate&quot; : Validate.validate,
+  &quot;show&quot;     : Render.show,
+  &quot;list&quot;     : Render.list
 };
 
 while (line = eval(readline())) {
-  cmd = eval(line)
-  line_length = line.length
+  cmd = eval(line);
+  line_length = line.length;
   try {
     cmdkey = cmd.shift();
     if (dispatch[cmdkey]) {</diff>
      <filename>share/server/loop.js</filename>
    </modified>
    <modified>
      <diff>@@ -12,9 +12,10 @@
 
 // mimeparse.js
 // http://code.google.com/p/mimeparse/
+// MIT Licensed http://www.opensource.org/licenses/mit-license.php
 // Code with comments: http://mimeparse.googlecode.com/svn/trunk/mimeparse.js
 // Tests: http://mimeparse.googlecode.com/svn/trunk/mimeparse-js-test.html
-// Ported from version 0.1.2
+// Ported by Chris Anderson from version 0.1.2
 
 var Mimeparse = (function() {
   function strip(string) {
@@ -111,6 +112,8 @@ var Mimeparse = (function() {
   return publicMethods;
 })();
 
+var respCT;
+var respTail;
 // this function provides a shortcut for managing responses by Accept header
 respondWith = function(req, responders) {
   var bestKey = null, accept = req.headers[&quot;Accept&quot;];
@@ -127,11 +130,16 @@ respondWith = function(req, responders) {
     bestKey = req.query.format;
   }
   var rFunc = responders[bestKey || responders.fallback || &quot;html&quot;];
-  if (rFunc) {      
-    var resp = maybeWrapResponse(rFunc());
-    resp[&quot;headers&quot;] = resp[&quot;headers&quot;] || {};
-    resp[&quot;headers&quot;][&quot;Content-Type&quot;] = bestMime;
-    respond(resp);
+  if (rFunc) {    
+    if (isShow) {
+      var resp = maybeWrapResponse(rFunc());
+      resp[&quot;headers&quot;] = resp[&quot;headers&quot;] || {};
+      resp[&quot;headers&quot;][&quot;Content-Type&quot;] = bestMime;
+      respond([&quot;resp&quot;, resp]);
+    } else {
+      respCT = bestMime;
+      respTail = rFunc();
+    }
   } else {
     throw({code:406, body:&quot;Not Acceptable: &quot;+accept});    
   }
@@ -162,8 +170,6 @@ registerType(&quot;text&quot;, &quot;text/plain&quot;, &quot;txt&quot;);
 registerType(&quot;html&quot;, &quot;text/html&quot;);
 registerType(&quot;xhtml&quot;, &quot;application/xhtml+xml&quot;, &quot;xhtml&quot;);
 registerType(&quot;xml&quot;, &quot;application/xml&quot;, &quot;text/xml&quot;, &quot;application/x-xml&quot;);
-// http://www.ietf.org/rfc/rfc4627.txt
-registerType(&quot;json&quot;, &quot;application/json&quot;, &quot;text/x-json&quot;);
 registerType(&quot;js&quot;, &quot;text/javascript&quot;, &quot;application/javascript&quot;, &quot;application/x-javascript&quot;);
 registerType(&quot;css&quot;, &quot;text/css&quot;);
 registerType(&quot;ics&quot;, &quot;text/calendar&quot;);
@@ -171,57 +177,148 @@ registerType(&quot;csv&quot;, &quot;text/csv&quot;);
 registerType(&quot;rss&quot;, &quot;application/rss+xml&quot;);
 registerType(&quot;atom&quot;, &quot;application/atom+xml&quot;);
 registerType(&quot;yaml&quot;, &quot;application/x-yaml&quot;, &quot;text/yaml&quot;);
+
 // just like Rails
 registerType(&quot;multipart_form&quot;, &quot;multipart/form-data&quot;);
 registerType(&quot;url_encoded_form&quot;, &quot;application/x-www-form-urlencoded&quot;);
 
+// http://www.ietf.org/rfc/rfc4627.txt
+registerType(&quot;json&quot;, &quot;application/json&quot;, &quot;text/x-json&quot;);
+
+
 
+
+//  Start chunks
+var startResp = {};
+function start(resp) {
+  startResp = resp || {};
+};
+
+function sendStart(label) {
+  startResp = startResp || {};
+  startResp[&quot;headers&quot;] = startResp[&quot;headers&quot;] || {};
+  startResp[&quot;headers&quot;][&quot;Content-Type&quot;] = startResp[&quot;headers&quot;][&quot;Content-Type&quot;] || respCT;
+  
+  respond([&quot;start&quot;, chunks, startResp]);
+  chunks = [];
+  startResp = {};
+}
+//  Send chunk
+var chunks = [];
+function send(chunk) {
+  chunks.push(chunk.toString());
+};
+
+function blowChunks(label) {
+  respond([label||&quot;chunks&quot;, chunks]);
+  chunks = [];
+};
+
+var gotRow = false, lastRow = false;
+function getRow() {
+  if (lastRow) return null;
+  if (!gotRow) {
+    gotRow = true;
+    sendStart();
+  } else {
+    blowChunks()  
+  }
+  var line = readline();
+  var json = eval(line);
+  if (json[0] == &quot;list_end&quot;) {
+    lastRow = true
+    return null;
+  }
+  if (json[0] != &quot;list_row&quot;) {
+    respond({
+      error: &quot;query_server_error&quot;,
+      reason: &quot;not a row '&quot; + json[0] + &quot;'&quot;});
+    quit();
+  }
+  return json[1];
+};
+
+////
+////  Render dispatcher
+////
+////
+////
+////
+var isShow = false;
 var Render = (function() {
   var row_info;
+  
   return {
-    showDoc : function(funSrc, doc, req) {
+    show : function(funSrc, doc, req) {
+      isShow = true;
       var formFun = compileFunction(funSrc);
-      runRenderFunction(formFun, [doc, req], funSrc);
-    },
-    listBegin : function(head, req) {
-      row_info = { first_key: null, row_number: 0, prev_key: null };
-      runRenderFunction(funs[0], [head, null, req, null], funsrc[0]);
-    },
-    listRow : function(row, req) {
-      if (row_info.first_key == null) {
-        row_info.first_key = row.key;
-      }
-      runRenderFunction(funs[0], [null, row, req, row_info], funsrc[0], true);
-      row_info.prev_key = row.key;
-      row_info.row_number++;
+      runShowRenderFunction(formFun, [doc, req], funSrc, true);
     },
-    listTail : function(req) {
-      runRenderFunction(funs[0], [null, null, req, row_info], funsrc[0]);
+    list : function(head, req) {
+      isShow = false;
+      runListRenderFunction(funs[0], [head, req], funsrc[0], false);
     }
   }
 })();
 
-function runRenderFunction(renderFun, args, funSrc, htmlErrors) {
-  responseSent = false;
+function maybeWrapResponse(resp) {
+  var type = typeof resp;
+  if ((type == &quot;string&quot;) || (type == &quot;xml&quot;)) {
+    return {body:resp};
+  } else {
+    return resp;
+  }
+};
+
+function runShowRenderFunction(renderFun, args, funSrc, htmlErrors) {
   try {
     var resp = renderFun.apply(null, args);
-    if (!responseSent) {
-      if (resp) {
-        respond(maybeWrapResponse(resp));       
-      } else {
-        respond({error:&quot;render_error&quot;,reason:&quot;undefined response from render function&quot;});
-      }      
+    if (resp) {
+      respond([&quot;resp&quot;, maybeWrapResponse(resp)]);
+    } else {
+      renderError(&quot;undefined response from render function&quot;);
     }
   } catch(e) {
-    var logMessage = &quot;function raised error: &quot;+e.toString();
-    log(logMessage);
-    // log(&quot;stacktrace: &quot;+e.stack);
-    var errorMessage = htmlErrors ? htmlRenderError(e, funSrc) : logMessage;
-    respond({
-      error:&quot;render_error&quot;,
-      reason:errorMessage});
+    respondError(e, funSrc, htmlErrors);
   }
 };
+function runListRenderFunction(renderFun, args, funSrc, htmlErrors) {
+  try {
+    gotRow = false;
+    lastRow = false;
+    respTail = &quot;&quot;;
+    if (renderFun.arity &gt; 2) {
+      throw(&quot;the list API has changed for CouchDB 0.10, please upgrade your code&quot;);
+    }
+    var resp = renderFun.apply(null, args);
+    if (!gotRow) {
+      getRow();
+    }
+    if (typeof resp != &quot;undefined&quot;) {
+      chunks.push(resp);      
+    } else if (respTail) {
+      chunks.push(respTail);      
+    }
+    blowChunks(&quot;end&quot;);      
+  } catch(e) {
+    respondError(e, funSrc, htmlErrors);
+  }
+};
+
+function renderError(m) {
+  respond({error : &quot;render_error&quot;, reason : m});
+}
+
+
+function respondError(e, funSrc, htmlErrors) {
+  var logMessage = &quot;function raised error: &quot;+e.toString();
+  log(logMessage);
+  log(&quot;stacktrace: &quot;+e.stack);
+  var errorMessage = htmlErrors ? htmlRenderError(e, funSrc) : logMessage;
+  respond({
+    error:&quot;render_error&quot;,
+    reason:errorMessage});
+}
 
 function escapeHTML(string) {
   return string.replace(/&amp;/g, &quot;&amp;amp;&quot;)
@@ -241,11 +338,3 @@ function htmlRenderError(e, funSrc) {
   return {body:msg};
 };
 
-function maybeWrapResponse(resp) {
-  var type = typeof resp;
-  if ((type == &quot;string&quot;) || (type == &quot;xml&quot;)) {
-    return {body:resp};
-  } else {
-    return resp;
-  }
-};</diff>
      <filename>share/server/render.js</filename>
    </modified>
    <modified>
      <diff>@@ -91,10 +91,8 @@ function recursivelySeal(obj) {
   }
 }
 
-var responseSent;
 // prints the object as JSON, and rescues and logs any toJSON() related errors
 function respond(obj) {
-  responseSent = true;
   try {
     print(toJSON(obj));  
   } catch(e) {
@@ -103,10 +101,11 @@ function respond(obj) {
 };
 
 log = function(message) {
+  // return;
   if (typeof message == &quot;undefined&quot;) {
     message = &quot;Error: attempting to log message of 'undefined'.&quot;;
   } else if (typeof message != &quot;string&quot;) {
     message = toJSON(message);
   }
-  print(toJSON({log: message}));
+  respond([&quot;log&quot;, message]);
 };</diff>
      <filename>share/server/util.js</filename>
    </modified>
    <modified>
      <diff>@@ -39,106 +39,127 @@ couchTests.list_views = function(debug) {
       }
     },
     lists: {
-      simpleForm: stringFun(function(head, row, req, row_info) {
-        if (row) {
-          // we ignore headers on rows and tail
-          return {
-                  body : '\n&lt;li&gt;Key: '+row.key
-                  +' Value: '+row.value
-                  +' LineNo: '+row_info.row_number+'&lt;/li&gt;'
-          };
-        } else if (head) {
-          // we return an object (like those used by external and show)
-          // so that we can specify headers
-          return {
-            body : '&lt;h1&gt;Total Rows: '
-              + head.total_rows
-              + ' Offset: ' + head.offset
-              + '&lt;/h1&gt;&lt;ul&gt;'
-          };
-        } else {
-          // tail
-          return {body : '&lt;/ul&gt;'+
-              '&lt;p&gt;FirstKey: '+(row_info ? row_info.first_key : '')+ 
-              ' LastKey: '+(row_info ? row_info.prev_key : '')+'&lt;/p&gt;'};
+      basicBasic : stringFun(function(head, req) {
+        send(&quot;head&quot;);
+        var row;
+        while(row = getRow()) {
+          log(&quot;row: &quot;+toJSON(row));
+          send(row.key);        
+        };
+        return &quot;tail&quot;;
+      }),
+      basicJSON : stringFun(function(head, req) {
+        start({&quot;headers&quot;:{&quot;Content-Type&quot; : &quot;application/json&quot;}}); 
+        send('{&quot;head&quot;:'+toJSON(head)+', ');
+        send('&quot;req&quot;:'+toJSON(req)+', ');
+        send('&quot;rows&quot;:[');
+        var row, sep = '';
+        while (row = getRow()) {
+          send(sep + toJSON(row));
+          sep = ', ';
         }
+        return &quot;]}&quot;;
       }),
-      acceptSwitch: stringFun(function(head, row, req, row_info) {
-        return respondWith(req, {
+      simpleForm: stringFun(function(head, req) {
+        log(&quot;simpleForm&quot;);
+        send('&lt;h1&gt;Total Rows: '
+              // + head.total_rows
+              // + ' Offset: ' + head.offset
+              + '&lt;/h1&gt;&lt;ul&gt;');
+
+        // rows
+        var row, row_number = 0, prevKey, firstKey = null;
+        while (row = getRow()) {
+          row_number += 1;
+          if (!firstKey) firstKey = row.key;
+          prevKey = row.key;
+          send('\n&lt;li&gt;Key: '+row.key
+          +' Value: '+row.value
+          +' LineNo: '+row_number+'&lt;/li&gt;');
+        }
+
+        // tail
+        return '&lt;/ul&gt;&lt;p&gt;FirstKey: '+ firstKey + ' LastKey: '+ prevKey+'&lt;/p&gt;';
+      }),
+      acceptSwitch: stringFun(function(head, req) {
+        // respondWith takes care of setting the proper headers
+        respondWith(req, {
           html : function() {
-            // If you're outputting text and you're not setting
-            // any headers, you can just return a string.
-            if (head) {
-              return &quot;HTML &lt;ul&gt;&quot;;
-            } else if (row) {
-              return '\n&lt;li&gt;Key: '
-                +row.key+' Value: '+row.value
-                +' LineNo: '+row_info.row_number+'&lt;/li&gt;';
-            } else { // tail
-              return '&lt;/ul&gt;';
+            send(&quot;HTML &lt;ul&gt;&quot;);
 
+            var row, num = 0;
+            while (row = getRow()) {
+              num ++;
+              send('\n&lt;li&gt;Key: '
+                +row.key+' Value: '+row.value
+                +' LineNo: '+num+'&lt;/li&gt;');
             }
+
+            // tail
+            return '&lt;/ul&gt;';
           },
           xml : function() {
-            if (head) {
-              return '&lt;feed xmlns=&quot;http://www.w3.org/2005/Atom&quot;&gt;'
-                +'&lt;title&gt;Test XML Feed&lt;/title&gt;';
-            } else if (row) {
-              // Becase Safari can't stand to see that dastardly
-              // E4X outside of a string. Outside of tests you
-              // can just use E4X literals.
+            send('&lt;feed xmlns=&quot;http://www.w3.org/2005/Atom&quot;&gt;'
+              +'&lt;title&gt;Test XML Feed&lt;/title&gt;');
+
+            while (row = getRow()) {
               var entry = new XML('&lt;entry/&gt;');
               entry.id = row.id;
               entry.title = row.key;
               entry.content = row.value;
-              // We'll also let you return just an E4X object
-              // if you aren't setting headers.
-              return entry;
-            } else {
-              return &quot;&lt;/feed&gt;&quot;;
+              send(entry);
             }
+            return &quot;&lt;/feed&gt;&quot;;
           }
-        })
+        });
       }),
-      qsParams: stringFun(function(head, row, req, row_info) {
-        if(head) return {body: req.query.foo};
-        else return {body: &quot;\n&quot;};
+      qsParams: stringFun(function(head, req) {
+        return toJSON(req.query) + &quot;\n&quot;;
       }),
-      stopIter: stringFun(function(head, row, req, row_info) {
-        if(head) {
-          return {body: &quot;head&quot;};
-        } else if(row) {
-          if(row_info.row_number &gt; 2) return {stop: true};
-          return {body: &quot; &quot; + row_info.row_number};
-        } else {
-          return {body: &quot; tail&quot;};
-        }
+      stopIter: stringFun(function(req) {
+        send(&quot;head&quot;);
+        var row, row_number = 0;
+        while(row = getRow()) {
+          if(row_number &gt; 2) break;
+          send(&quot; &quot; + row_number);
+          row_number += 1;
+        };
+        return &quot; tail&quot;;
       }),
-      stopIter2: stringFun(function(head, row, req, row_info) {
-        return respondWith(req, {
+      stopIter2: stringFun(function(head, req) {
+        respondWith(req, {
           html: function() {
-            if(head) {
-              return &quot;head&quot;;
-            } else if(row) {
-              if(row_info.row_number &gt; 2) return {stop: true};
-              return &quot; &quot; + row_info.row_number;
-            } else {
-              return &quot; tail&quot;;
-            }
+            send(&quot;head&quot;);
+            var row, row_number = 0;
+            while(row = getRow()) {
+              if(row_number &gt; 2) break;
+              send(&quot; &quot; + row_number);
+              row_number += 1;
+            };
+            return &quot; tail&quot;;
           }
         });
       }),
-      emptyList: stringFun(function(head, row, req, row_info) {
-        return { body: &quot;&quot; };
+      tooManyGetRows : stringFun(function() {
+        send(&quot;head&quot;);
+        var row;
+        while(row = getRow()) {
+          send(row.key);        
+        };
+        getRow();
+        getRow();
+        getRow();
+        row = getRow();
+        return &quot;after row: &quot;+toJSON(row);
       }),
-      rowError : stringFun(function(head, row, req, row_info) {
-        if (head) {
-          return &quot;head&quot;;
-        } else if(row) {
-          return missingValue;
-        } else {
-          return &quot;tail&quot;
-        }
+      emptyList: stringFun(function() {
+        return &quot; &quot;;
+      }),
+      rowError : stringFun(function(head, req) {
+        send(&quot;head&quot;);
+        var row = getRow();
+        send(fooBarBam); // intentional error
+        return &quot;tail&quot;;
       })
     }
   };
@@ -152,26 +173,40 @@ couchTests.list_views = function(debug) {
   T(view.total_rows == 10);
   
   // standard get
-  var xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/simpleForm/basicView&quot;);
+  var xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/basicBasic/basicView&quot;);
   T(xhr.status == 200, &quot;standard get should be 200&quot;);
-  T(/Total Rows/.test(xhr.responseText));
-  T(/Key: 1/.test(xhr.responseText));
-  T(/LineNo: 0/.test(xhr.responseText));
-  T(/LineNo: 5/.test(xhr.responseText));
-  T(/FirstKey: 0/.test(xhr.responseText));
-  T(/LastKey: 9/.test(xhr.responseText));
-
-
-  var lines = xhr.responseText.split('\n');
-  T(/LineNo: 5/.test(lines[6]));
+  T(/head0123456789tail/.test(xhr.responseText));
 
   // test that etags are available
   var etag = xhr.getResponseHeader(&quot;etag&quot;);
-  xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/simpleForm/basicView&quot;, {
+  xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/basicBasic/basicView&quot;, {
     headers: {&quot;if-none-match&quot;: etag}
   });
   T(xhr.status == 304);
 
+  // test the richness of the arguments
+  xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/basicJSON/basicView&quot;);
+  T(xhr.status == 200, &quot;standard get should be 200&quot;);
+  var resp = JSON.parse(xhr.responseText);
+  TEquals(resp.head, {total_rows:10, offset:0});
+  T(resp.rows.length == 10);
+  TEquals(resp.rows[0], {&quot;id&quot;: &quot;0&quot;,&quot;key&quot;: 0,&quot;value&quot;: &quot;0&quot;});
+
+  TEquals(resp.req.info.db_name, &quot;test_suite_db&quot;);
+  TEquals(resp.req.verb, &quot;GET&quot;);
+  TEquals(resp.req.path, [
+      &quot;test_suite_db&quot;,
+      &quot;_design&quot;,
+      &quot;lists&quot;,
+      &quot;_list&quot;,
+      &quot;basicJSON&quot;,
+      &quot;basicView&quot;
+  ]);
+  T(resp.req.headers.Accept);
+  T(resp.req.headers.Host);
+  T(resp.req.headers[&quot;User-Agent&quot;]);
+  T(resp.req.cookie);
+
   // get with query params
   var xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/simpleForm/basicView?startkey=3&quot;);
   T(xhr.status == 200, &quot;with query params&quot;);
@@ -179,26 +214,30 @@ couchTests.list_views = function(debug) {
   T(!(/Key: 1/.test(xhr.responseText)));
   T(/FirstKey: 3/.test(xhr.responseText));
   T(/LastKey: 9/.test(xhr.responseText));
-
   
   // with 0 rows
   var xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/simpleForm/basicView?startkey=30&quot;);
   T(xhr.status == 200, &quot;0 rows&quot;);
   T(/Total Rows/.test(xhr.responseText));
-  T(/Offset: null/.test(xhr.responseText));
+
+  //too many Get Rows
+  var xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/tooManyGetRows/basicView&quot;);
+  T(xhr.status == 200, &quot;tooManyGetRows&quot;);
+  T(/9after row: null/.test(xhr.responseText));
+
 
   // reduce with 0 rows
   var xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/simpleForm/withReduce?startkey=30&quot;);
   T(xhr.status == 200, &quot;reduce 0 rows&quot;);
   T(/Total Rows/.test(xhr.responseText));
-  T(/Offset: undefined/.test(xhr.responseText));
-
+  T(/LastKey: undefined/.test(xhr.responseText));
   
   // when there is a reduce present, but not used
   var xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/simpleForm/withReduce?reduce=false&quot;);
   T(xhr.status == 200, &quot;reduce false&quot;);
   T(/Total Rows/.test(xhr.responseText));
   T(/Key: 1/.test(xhr.responseText));
+
   
   // when there is a reduce present, and used
   xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true&quot;);
@@ -221,48 +260,12 @@ couchTests.list_views = function(debug) {
     headers: {&quot;if-none-match&quot;: etag}
   });
   T(xhr.status == 200, &quot;reduce etag&quot;);
-  
-  // with accept headers for HTML
-  xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/acceptSwitch/basicView&quot;, {
-    headers: {
-      &quot;Accept&quot;: 'text/html'
-    }
-  });
-  T(xhr.getResponseHeader(&quot;Content-Type&quot;) == &quot;text/html&quot;);
-  T(xhr.responseText.match(/HTML/));
-  T(xhr.responseText.match(/Value/));
-
-  // now with xml
-  xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/acceptSwitch/basicView&quot;, {
-    headers: {
-      &quot;Accept&quot;: 'application/xml'
-    }
-  });
-  T(xhr.getResponseHeader(&quot;Content-Type&quot;) == &quot;application/xml&quot;);
-  T(xhr.responseText.match(/XML/));
-  T(xhr.responseText.match(/entry/));
-
-  // now with extra qs params
-  var xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/qsParams/basicView?foo=blam&quot;);
-  T(xhr.responseText.match(/blam/));
-  
-  // aborting iteration
-  var xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/stopIter/basicView&quot;);
-  T(xhr.responseText.match(/^head 0 1 2 tail$/) &amp;&amp; &quot;basic stop&quot;);
-  xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/stopIter2/basicView&quot;);
-  T(xhr.responseText.match(/^head 0 1 2 tail$/) &amp;&amp; &quot;stop 2&quot;);
-
-  // aborting iteration with reduce
-  var xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/stopIter/withReduce?group=true&quot;);
-  T(xhr.responseText.match(/^head 0 1 2 tail$/) &amp;&amp; &quot;reduce stop&quot;);
-  xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/stopIter2/withReduce?group=true&quot;);
-  T(xhr.responseText.match(/^head 0 1 2 tail$/) &amp;&amp; &quot;reduce stop 2&quot;);
 
   // empty list
   var xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/emptyList/basicView&quot;);
-  T(xhr.responseText.match(/^$/));
+  T(xhr.responseText.match(/^ $/));
   xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/emptyList/withReduce?group=true&quot;);
-  T(xhr.responseText.match(/^$/));
+  T(xhr.responseText.match(/^ $/));
 
   // multi-key fetch
   var xhr = CouchDB.request(&quot;POST&quot;, &quot;/test_suite_db/_design/lists/_list/simpleForm/basicView&quot;, {
@@ -281,7 +284,45 @@ couchTests.list_views = function(debug) {
   });
   T(xhr.status == 400);
   T(/query_parse_error/.test(xhr.responseText));
-  
+    
   var xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/rowError/basicView&quot;);
-  T(/&lt;h1&gt;Render Error&lt;\/h1&gt;/.test(xhr.responseText));
+  T(/ReferenceError/.test(xhr.responseText));
+
+
+  // now with extra qs params
+  var xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/qsParams/basicView?foo=blam&quot;);
+  T(xhr.responseText.match(/blam/));
+  
+  var xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/stopIter/basicView&quot;);
+  // T(xhr.getResponseHeader(&quot;Content-Type&quot;) == &quot;text/plain&quot;);
+  T(xhr.responseText.match(/^head 0 1 2 tail$/) &amp;&amp; &quot;basic stop&quot;);
+
+  xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/stopIter2/basicView&quot;);
+  T(xhr.responseText.match(/^head 0 1 2 tail$/) &amp;&amp; &quot;stop 2&quot;);
+
+  // aborting iteration with reduce
+  var xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/stopIter/withReduce?group=true&quot;);
+  T(xhr.responseText.match(/^head 0 1 2 tail$/) &amp;&amp; &quot;reduce stop&quot;);
+  xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/stopIter2/withReduce?group=true&quot;);
+  T(xhr.responseText.match(/^head 0 1 2 tail$/) &amp;&amp; &quot;reduce stop 2&quot;);
+  
+  // with accept headers for HTML
+  xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/acceptSwitch/basicView&quot;, {
+    headers: {
+      &quot;Accept&quot;: 'text/html'
+    }
+  });
+  T(xhr.getResponseHeader(&quot;Content-Type&quot;) == &quot;text/html&quot;);
+  T(xhr.responseText.match(/HTML/));
+  T(xhr.responseText.match(/Value/));
+
+  // now with xml
+  xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/lists/_list/acceptSwitch/basicView&quot;, {
+    headers: {
+      &quot;Accept&quot;: 'application/xml'
+    }
+  });
+  T(xhr.getResponseHeader(&quot;Content-Type&quot;) == &quot;application/xml&quot;);
+  T(xhr.responseText.match(/XML/));
+  T(xhr.responseText.match(/entry/));
 };</diff>
      <filename>share/www/script/test/list_views.js</filename>
    </modified>
    <modified>
      <diff>@@ -106,9 +106,7 @@ couchTests.show_documents = function(debug) {
         registerType(&quot;foo&quot;, &quot;application/foo&quot;,&quot;application/x-foo&quot;);
         return respondWith(req, {
           html : function() {
-            return {
-              body:&quot;Ha ha, you said \&quot;&quot; + doc.word + &quot;\&quot;.&quot;
-            };
+            return &quot;Ha ha, you said \&quot;&quot; + doc.word + &quot;\&quot;.&quot;;
           },
           xml : function() {
             var xml = new XML('&lt;xml&gt;&lt;node/&gt;&lt;/xml&gt;');
@@ -145,10 +143,14 @@ couchTests.show_documents = function(debug) {
   // hello template world
   xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/template/_show/hello/&quot;+docid);
   T(xhr.responseText == &quot;Hello World&quot;);
+// 
+// };
+// 
+// function foo() {
 
-  // error stacktraces
-  xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/template/_show/render-error/&quot;+docid);
-  T(JSON.parse(xhr.responseText).error == &quot;render_error&quot;);
+  // // error stacktraces
+  // xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/template/_show/render-error/&quot;+docid);
+  // T(JSON.parse(xhr.responseText).error == &quot;render_error&quot;);
  
   // hello template world (no docid)
   xhr = CouchDB.request(&quot;GET&quot;, &quot;/test_suite_db/_design/template/_show/hello&quot;);</diff>
      <filename>share/www/script/test/show_documents.js</filename>
    </modified>
    <modified>
      <diff>@@ -177,6 +177,14 @@ handle_request(MochiReq, DefaultFun,
             % ?LOG_DEBUG(&quot;Minor error in HTTP request: ~p&quot;,[Error]),
             % ?LOG_DEBUG(&quot;Stacktrace: ~p&quot;,[erlang:get_stacktrace()]),
             send_error(HttpReq, Error);
+        error:badarg -&gt;
+            ?LOG_ERROR(&quot;Badarg error in HTTP request&quot;,[]),
+            ?LOG_INFO(&quot;Stacktrace: ~p&quot;,[erlang:get_stacktrace()]),
+            send_error(HttpReq, badarg);
+        error:function_clause -&gt;
+            ?LOG_ERROR(&quot;function_clause error in HTTP request&quot;,[]),
+            ?LOG_INFO(&quot;Stacktrace: ~p&quot;,[erlang:get_stacktrace()]),
+            send_error(HttpReq, function_clause);
         Tag:Error -&gt;
             ?LOG_ERROR(&quot;Uncaught error in HTTP request: ~p&quot;,[{Tag, Error}]),
             ?LOG_INFO(&quot;Stacktrace: ~p&quot;,[erlang:get_stacktrace()]),</diff>
      <filename>src/couchdb/couch_httpd.erl</filename>
    </modified>
    <modified>
      <diff>@@ -117,47 +117,6 @@ send_view_list_response(Lang, ListSrc, ViewName, DesignId, Req, Db, Keys) -&gt;
         end
     end.
 
-make_map_start_resp_fun(QueryServer, Db) -&gt;
-    fun(Req, CurrentEtag, TotalViewCount, Offset, _Acc) -&gt;
-        ExternalResp = couch_query_servers:render_list_head(QueryServer, 
-            Req, Db, TotalViewCount, Offset),
-        JsonResp = apply_etag(ExternalResp, CurrentEtag),
-        #extern_resp_args{
-            code = Code,
-            data = BeginBody,
-            ctype = CType,
-            headers = ExtHeaders
-        } = couch_httpd_external:parse_external_response(JsonResp),
-        JsonHeaders = couch_httpd_external:default_or_content_type(CType, ExtHeaders),
-        {ok, Resp} = start_chunked_response(Req, Code, JsonHeaders),
-        {ok, Resp, binary_to_list(BeginBody)}
-    end.
-
-make_map_send_row_fun(QueryServer, Req) -&gt;
-    fun(Resp, Db2, {{Key, DocId}, Value}, _IncludeDocs, RowFront) -&gt;
-        try
-            JsonResp = couch_query_servers:render_list_row(QueryServer, 
-                Req, Db2, {{Key, DocId}, Value}),
-            #extern_resp_args{
-                stop = StopIter,
-                data = RowBody
-            } = couch_httpd_external:parse_external_response(JsonResp),
-            case StopIter of
-            true -&gt; {stop, &quot;&quot;};
-            _ -&gt;
-                Chunk = RowFront ++ binary_to_list(RowBody),
-                case Chunk of
-                    [] -&gt; ok;
-                    _ -&gt; send_chunk(Resp, Chunk)
-                end,
-                {ok, &quot;&quot;}
-            end
-        catch
-            throw:Error -&gt;
-                send_chunked_error(Resp, Error),
-                throw({already_sent, Resp, Error})
-        end
-    end.
 
 output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, nil) -&gt;
     #view_query_args{
@@ -179,7 +138,7 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer
         {ok, QueryServer} = couch_query_servers:start_view_list(Lang, ListSrc),
 
         StartListRespFun = make_map_start_resp_fun(QueryServer, Db),
-        SendListRowFun = make_map_send_row_fun(QueryServer, Req),
+        SendListRowFun = make_map_send_row_fun(QueryServer),
     
         FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db, RowCount,
             #view_fold_helper_funs{
@@ -189,7 +148,7 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer
             }),
         FoldAccInit = {Limit, SkipCount, undefined, []},
         {ok, FoldResult} = couch_view:fold(View, Start, Dir, FoldlFun, FoldAccInit),
-        finish_list(Req, Db, QueryServer, CurrentEtag, FoldResult, StartListRespFun, RowCount)
+        finish_list(Req, QueryServer, CurrentEtag, FoldResult, StartListRespFun, RowCount)
     end);
 
 output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, Keys) -&gt;
@@ -210,7 +169,7 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer
         {ok, QueryServer} = couch_query_servers:start_view_list(Lang, ListSrc),
 
         StartListRespFun = make_map_start_resp_fun(QueryServer, Db),
-        SendListRowFun = make_map_send_row_fun(QueryServer, Req),
+        SendListRowFun = make_map_send_row_fun(QueryServer),
 
         FoldAccInit = {Limit, SkipCount, undefined, []},
         {ok, FoldResult} = lists:foldl(
@@ -226,49 +185,64 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer
                     }),
                 couch_view:fold(View, {Key, StartDocId}, Dir, FoldlFun, FoldAcc)
             end, {ok, FoldAccInit}, Keys),
-        finish_list(Req, Db, QueryServer, CurrentEtag, FoldResult, StartListRespFun, RowCount)
+        finish_list(Req, QueryServer, CurrentEtag, FoldResult, StartListRespFun, RowCount)
     end).
 
-make_reduce_start_resp_fun(QueryServer, Req, Db, CurrentEtag) -&gt;
-    fun(Req2, _Etag, _Acc) -&gt;
-        JsonResp = couch_query_servers:render_reduce_head(QueryServer, 
-            Req2, Db),
-        JsonResp2 = apply_etag(JsonResp, CurrentEtag),
-        #extern_resp_args{
-            code = Code,
-            data = BeginBody,
-            ctype = CType,
-            headers = ExtHeaders
-        } = couch_httpd_external:parse_external_response(JsonResp2),
-        JsonHeaders = couch_httpd_external:default_or_content_type(CType, ExtHeaders),
-        {ok, Resp} = start_chunked_response(Req, Code, JsonHeaders),
-        {ok, Resp, binary_to_list(BeginBody)}
+make_map_start_resp_fun(QueryServer, Db) -&gt;
+    fun(Req, Etag, TotalRows, Offset, _Acc) -&gt;
+        Head = {[{&lt;&lt;&quot;total_rows&quot;&gt;&gt;, TotalRows}, {&lt;&lt;&quot;offset&quot;&gt;&gt;, Offset}]},
+        start_list_resp(QueryServer, Req, Db, Head, Etag)
     end.
 
-make_reduce_send_row_fun(QueryServer, Req, Db) -&gt;
-    fun(Resp, {Key, Value}, RowFront) -&gt;
-        try
-            JsonResp = couch_query_servers:render_reduce_row(QueryServer, 
-                Req, Db, {Key, Value}),
-            #extern_resp_args{
-                stop = StopIter,
-                data = RowBody
-            } = couch_httpd_external:parse_external_response(JsonResp),
-            case StopIter of
-            true -&gt; {stop, &quot;&quot;};
-            _ -&gt;
-                Chunk = RowFront ++ binary_to_list(RowBody),
-                case Chunk of
-                    [] -&gt; ok;
-                    _ -&gt; send_chunk(Resp, Chunk)
-                end,
-                {ok, &quot;&quot;}
-            end
-        catch
-            throw:Error -&gt;
-                send_chunked_error(Resp, Error),
-                throw({already_sent, Resp, Error})
+make_reduce_start_resp_fun(QueryServer, _Req, Db, _CurrentEtag) -&gt;
+    fun(Req2, Etag, _Acc) -&gt;
+        start_list_resp(QueryServer, Req2, Db, {[]}, Etag)
+    end.
+
+start_list_resp(QueryServer, Req, Db, Head, Etag) -&gt;
+    [&lt;&lt;&quot;start&quot;&gt;&gt;,Chunks,JsonResp] = couch_query_servers:render_list_head(QueryServer, 
+        Req, Db, Head),
+    JsonResp2 = apply_etag(JsonResp, Etag),
+    #extern_resp_args{
+        code = Code,
+        ctype = CType,
+        headers = ExtHeaders
+    } = couch_httpd_external:parse_external_response(JsonResp2),
+    JsonHeaders = couch_httpd_external:default_or_content_type(CType, ExtHeaders),
+    {ok, Resp} = start_chunked_response(Req, Code, JsonHeaders),
+    {ok, Resp, ?b2l(?l2b(Chunks))}.
+
+make_map_send_row_fun(QueryServer) -&gt;
+    fun(Resp, Db, Row, _IncludeDocs, RowFront) -&gt;
+        send_list_row(Resp, QueryServer, Db, Row, RowFront)
+    end.
+
+make_reduce_send_row_fun(QueryServer, Db) -&gt;
+    fun(Resp, Row, RowFront) -&gt;
+        send_list_row(Resp, QueryServer, Db, Row, RowFront)
+    end.
+
+send_list_row(Resp, QueryServer, Db, Row, RowFront) -&gt;
+    try
+        [Go,Chunks] = couch_query_servers:render_list_row(QueryServer, Db, Row),
+        Chunk = RowFront ++ ?b2l(?l2b(Chunks)),
+        send_non_empty_chunk(Resp, Chunk),
+        case Go of
+            &lt;&lt;&quot;chunks&quot;&gt;&gt; -&gt;
+                {ok, &quot;&quot;};
+            &lt;&lt;&quot;end&quot;&gt;&gt; -&gt;
+                {stop, stop}
         end
+    catch
+        throw:Error -&gt;
+            send_chunked_error(Resp, Error),
+            throw({already_sent, Resp, Error})
+    end.
+
+send_non_empty_chunk(Resp, Chunk) -&gt;
+    case Chunk of
+        [] -&gt; ok;
+        _ -&gt; send_chunk(Resp, Chunk)
     end.
 
 output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, nil) -&gt;
@@ -291,7 +265,7 @@ output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Q
     CurrentEtag = couch_httpd_view:view_group_etag(Group, {Lang, ListSrc, Accept}),
     couch_httpd:etag_respond(Req, CurrentEtag, fun() -&gt;
         StartListRespFun = make_reduce_start_resp_fun(QueryServer, Req, Db, CurrentEtag),
-        SendListRowFun = make_reduce_send_row_fun(QueryServer, Req, Db),
+        SendListRowFun = make_reduce_send_row_fun(QueryServer, Db),
     
         {ok, GroupRowsFun, RespFun} = couch_httpd_view:make_reduce_fold_funs(Req, 
             GroupLevel, QueryArgs, CurrentEtag, 
@@ -303,7 +277,7 @@ output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Q
         {ok, FoldResult} = couch_view:fold_reduce(View, Dir, {StartKey, StartDocId},
             {EndKey, EndDocId}, GroupRowsFun, RespFun,
             FoldAccInit),
-        finish_list(Req, Db, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null)
+        finish_list(Req, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null)
     end);
 
 output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, Keys) -&gt;
@@ -325,7 +299,7 @@ output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Q
 
     couch_httpd:etag_respond(Req, CurrentEtag, fun() -&gt;
         StartListRespFun = make_reduce_start_resp_fun(QueryServer, Req, Db, CurrentEtag),
-        SendListRowFun = make_reduce_send_row_fun(QueryServer, Req, Db),
+        SendListRowFun = make_reduce_send_row_fun(QueryServer, Db),
     
         {ok, GroupRowsFun, RespFun} = couch_httpd_view:make_reduce_fold_funs(Req,
             GroupLevel, QueryArgs, CurrentEtag, 
@@ -339,33 +313,30 @@ output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Q
                 couch_view:fold_reduce(View, Dir, {Key, StartDocId},
                     {Key, EndDocId}, GroupRowsFun, RespFun, FoldAcc)
             end, {ok, FoldAccInit}, Keys),
-        finish_list(Req, Db, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null)
+        finish_list(Req, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null)
     end).
 
-finish_list(Req, Db, QueryServer, Etag, FoldResult, StartListRespFun, TotalRows) -&gt;
-    {Resp, BeginBody} = case FoldResult of
+finish_list(Req, QueryServer, Etag, FoldResult, StartFun, TotalRows) -&gt;
+    case FoldResult of
         {_, _, undefined, _} -&gt;
-            {ok, Resp2, BeginBody2} = render_head_for_empty_list(StartListRespFun, Req, Etag, TotalRows),
-            {Resp2, BeginBody2};
-        {_, _, Resp0, _} -&gt;
-            {Resp0, &quot;&quot;}
-    end,
-    JsonTail = couch_query_servers:render_list_tail(QueryServer, Req, Db),
-    #extern_resp_args{
-        data = Tail
-    } = couch_httpd_external:parse_external_response(JsonTail),
-    Chunk = BeginBody ++ binary_to_list(Tail),
-    case Chunk of
-        [] -&gt; ok;
-        _ -&gt; send_chunk(Resp, Chunk)
+            {ok, Resp, BeginBody} = 
+                render_head_for_empty_list(StartFun, Req, Etag, TotalRows),
+            [&lt;&lt;&quot;end&quot;&gt;&gt;, Chunks] = couch_query_servers:render_list_tail(QueryServer),
+            Chunk = BeginBody ++ ?b2l(?l2b(Chunks)),
+            send_non_empty_chunk(Resp, Chunk);
+        {_, _, Resp, stop} -&gt;
+            ok;
+        {_, _, Resp, _} -&gt;
+            [&lt;&lt;&quot;end&quot;&gt;&gt;, Chunks] = couch_query_servers:render_list_tail(QueryServer),
+            send_non_empty_chunk(Resp, ?b2l(?l2b(Chunks)))
     end,
     send_chunk(Resp, []).
 
 render_head_for_empty_list(StartListRespFun, Req, Etag, null) -&gt;
-    StartListRespFun(Req, Etag, []);
+    StartListRespFun(Req, Etag, []); % for reduce
 render_head_for_empty_list(StartListRespFun, Req, Etag, TotalRows) -&gt;
     StartListRespFun(Req, Etag, TotalRows, null, []).
-    
+
 send_doc_show_response(Lang, ShowSrc, DocId, nil, #httpd{mochi_req=MReq}=Req, Db) -&gt;
     % compute etag with no doc
     Headers = MReq:get(headers),
@@ -373,7 +344,7 @@ send_doc_show_response(Lang, ShowSrc, DocId, nil, #httpd{mochi_req=MReq}=Req, Db
     Accept = proplists:get_value('Accept', Hlist),
     CurrentEtag = couch_httpd:make_etag({Lang, ShowSrc, nil, Accept}),
     couch_httpd:etag_respond(Req, CurrentEtag, fun() -&gt; 
-        ExternalResp = couch_query_servers:render_doc_show(Lang, ShowSrc, 
+        [&lt;&lt;&quot;resp&quot;&gt;&gt;, ExternalResp] = couch_query_servers:render_doc_show(Lang, ShowSrc, 
             DocId, nil, Req, Db),
         JsonResp = apply_etag(ExternalResp, CurrentEtag),
         couch_httpd_external:send_external_response(Req, JsonResp)
@@ -387,7 +358,7 @@ send_doc_show_response(Lang, ShowSrc, DocId, #doc{revs=Revs}=Doc, #httpd{mochi_r
     CurrentEtag = couch_httpd:make_etag({Lang, ShowSrc, Revs, Accept}),
     % We know our etag now    
     couch_httpd:etag_respond(Req, CurrentEtag, fun() -&gt; 
-        ExternalResp = couch_query_servers:render_doc_show(Lang, ShowSrc, 
+        [&lt;&lt;&quot;resp&quot;&gt;&gt;, ExternalResp] = couch_query_servers:render_doc_show(Lang, ShowSrc, 
             DocId, Doc, Req, Db),
         JsonResp = apply_etag(ExternalResp, CurrentEtag),
         couch_httpd_external:send_external_response(Req, JsonResp)</diff>
      <filename>src/couchdb/couch_httpd_show.erl</filename>
    </modified>
    <modified>
      <diff>@@ -247,13 +247,13 @@ GC(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
 
 static JSBool
 Print(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
-    uintN i, n;
+    uintN i;
     size_t cl, bl;
     JSString *str;
     jschar *chars;
     char *bytes;
 
-    for (i = n = 0; i &lt; argc; i++) {
+    for (i = 0; i &lt; argc; i++) {
         str = JS_ValueToString(context, argv[i]);
         if (!str)
             return JS_FALSE;
@@ -270,9 +270,8 @@ Print(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
         fprintf(stdout, &quot;%s%s&quot;, i ? &quot; &quot; : &quot;&quot;, bytes);
         JS_free(context, bytes);
     }
-    n++;
-    if (n)
-        fputc('\n', stdout);
+
+    fputc('\n', stdout);
     fflush(stdout);
     return JS_TRUE;
 }</diff>
      <filename>src/couchdb/couch_js.c</filename>
    </modified>
    <modified>
      <diff>@@ -53,7 +53,7 @@ prompt(Pid, Data) -&gt;
         {ok, Result} -&gt;
             Result;
         Error -&gt;
-            ?LOG_DEBUG(&quot;OS Process Error ~p&quot;,[Error]),
+            ?LOG_ERROR(&quot;OS Process Error :: ~p&quot;,[Error]),
             throw(Error)
     end.
 
@@ -81,20 +81,22 @@ readline(OsProc, Acc) when is_record(OsProc, os_proc) -&gt;
 
 % Standard JSON functions
 writejson(OsProc, Data) when is_record(OsProc, os_proc) -&gt;
+    % ?LOG_DEBUG(&quot;OS Process Input :: ~p&quot;, [Data]),
     true = writeline(OsProc, ?JSON_ENCODE(Data)).
 
 readjson(OsProc) when is_record(OsProc, os_proc) -&gt;
     Line = readline(OsProc),
     case ?JSON_DECODE(Line) of
-    {[{&lt;&lt;&quot;log&quot;&gt;&gt;,Msg}]} when is_binary(Msg) -&gt;
+    [&lt;&lt;&quot;log&quot;&gt;&gt;, Msg] when is_binary(Msg) -&gt;
         % we got a message to log. Log it and continue
-        ?LOG_INFO(&quot;OS Process Log Message: ~s&quot;, [Msg]),
+        ?LOG_INFO(&quot;OS Process :: ~s&quot;, [Msg]),
         readjson(OsProc);
     {[{&lt;&lt;&quot;error&quot;&gt;&gt;, Id}, {&lt;&lt;&quot;reason&quot;&gt;&gt;, Reason}]} -&gt;
         throw({list_to_atom(binary_to_list(Id)),Reason});
     {[{&lt;&lt;&quot;reason&quot;&gt;&gt;, Reason}, {&lt;&lt;&quot;error&quot;&gt;&gt;, Id}]} -&gt;
         throw({list_to_atom(binary_to_list(Id)),Reason});
     Result -&gt;
+        % ?LOG_DEBUG(&quot;OS Process Output :: ~p&quot;, [Result]),
         Result
     end.
 </diff>
      <filename>src/couchdb/couch_os_process.erl</filename>
    </modified>
    <modified>
      <diff>@@ -18,9 +18,8 @@
 -export([init/1, terminate/2, handle_call/3, handle_cast/2, handle_info/2,code_change/3,stop/0]).
 -export([start_doc_map/2, map_docs/2, stop_doc_map/1]).
 -export([reduce/3, rereduce/3,validate_doc_update/5]).
--export([render_doc_show/6,start_view_list/2,render_list_head/5, 
-        render_list_row/4, render_list_tail/3, render_reduce_head/3, 
-        render_reduce_row/4]).
+-export([render_doc_show/6, start_view_list/2, 
+        render_list_head/4, render_list_row/3, render_list_tail/1]).
 % -export([test/0]).
 
 -include(&quot;couch_db.hrl&quot;).
@@ -183,7 +182,7 @@ render_doc_show(Lang, ShowSrc, DocId, Doc, Req, Db) -&gt;
         _ -&gt; {{append_docid(DocId, JsonReqIn)}, couch_doc:to_json_obj(Doc, [revs])}
     end,
     try couch_os_process:prompt(Pid, 
-        [&lt;&lt;&quot;show_doc&quot;&gt;&gt;, ShowSrc, JsonDoc, JsonReq]) of
+        [&lt;&lt;&quot;show&quot;&gt;&gt;, ShowSrc, JsonDoc, JsonReq]) of
     FormResp -&gt;
         FormResp
     after
@@ -195,32 +194,24 @@ start_view_list(Lang, ListSrc) -&gt;
     true = couch_os_process:prompt(Pid, [&lt;&lt;&quot;add_fun&quot;&gt;&gt;, ListSrc]),
     {ok, {Lang, Pid}}.
 
-render_list_head({_Lang, Pid}, Req, Db, TotalRows, Offset) -&gt;
-    Head = {[{&lt;&lt;&quot;total_rows&quot;&gt;&gt;, TotalRows}, {&lt;&lt;&quot;offset&quot;&gt;&gt;, Offset}]},
+render_list_head({_Lang, Pid}, Req, Db, Head) -&gt;
     JsonReq = couch_httpd_external:json_req_obj(Req, Db),
-    couch_os_process:prompt(Pid, [&lt;&lt;&quot;list_begin&quot;&gt;&gt;, Head, JsonReq]).
+    couch_os_process:prompt(Pid, [&lt;&lt;&quot;list&quot;&gt;&gt;, Head, JsonReq]).
 
-render_list_row({_Lang, Pid}, Req, Db, {{Key, DocId}, Value}) -&gt;
+render_list_row({_Lang, Pid}, Db, {{Key, DocId}, Value}) -&gt;
     JsonRow = couch_httpd_view:view_row_obj(Db, {{Key, DocId}, Value}, false),
-    JsonReq = couch_httpd_external:json_req_obj(Req, Db),
-    couch_os_process:prompt(Pid, [&lt;&lt;&quot;list_row&quot;&gt;&gt;, JsonRow, JsonReq]).
+    couch_os_process:prompt(Pid, [&lt;&lt;&quot;list_row&quot;&gt;&gt;, JsonRow]);
 
-render_list_tail({Lang, Pid}, Req, Db) -&gt;
-    JsonReq = couch_httpd_external:json_req_obj(Req, Db),
-    JsonResp = couch_os_process:prompt(Pid, [&lt;&lt;&quot;list_tail&quot;&gt;&gt;, JsonReq]),
+render_list_row({_Lang, Pid}, _, {Key, Value}) -&gt;
+    JsonRow = {[{key, Key}, {value, Value}]},
+    couch_os_process:prompt(Pid, [&lt;&lt;&quot;list_row&quot;&gt;&gt;, JsonRow]).
+
+render_list_tail({Lang, Pid}) -&gt;
+    JsonResp = couch_os_process:prompt(Pid, [&lt;&lt;&quot;list_end&quot;&gt;&gt;]),
     ok = ret_os_process(Lang, Pid),
-    JsonResp.
+    JsonResp.    
     
-    
-render_reduce_head({_Lang, Pid}, Req, Db) -&gt;
-    Head = {[]},
-    JsonReq = couch_httpd_external:json_req_obj(Req, Db),
-    couch_os_process:prompt(Pid, [&lt;&lt;&quot;list_begin&quot;&gt;&gt;, Head, JsonReq]).
 
-render_reduce_row({_Lang, Pid}, Req, Db, {Key, Value}) -&gt;
-    JsonRow = {[{key, Key}, {value, Value}]},
-    JsonReq = couch_httpd_external:json_req_obj(Req, Db),
-    couch_os_process:prompt(Pid, [&lt;&lt;&quot;list_row&quot;&gt;&gt;, JsonRow, JsonReq]).
 
 
 init([]) -&gt;</diff>
      <filename>src/couchdb/couch_query_servers.erl</filename>
    </modified>
    <modified>
      <diff>@@ -22,31 +22,6 @@ require 'spec'
 require 'json'
 
 
-JSON_REQ = {
-  &quot;body&quot;=&gt;&quot;undefined&quot;, 
-  &quot;verb&quot;=&gt;&quot;GET&quot;, 
-  &quot;info&quot;=&gt;{
-    &quot;disk_format_version&quot;=&gt;2, 
-    &quot;purge_seq&quot;=&gt;0, 
-    &quot;doc_count&quot;=&gt;9082, 
-    &quot;instance_start_time&quot;=&gt;&quot;1243713611467271&quot;, 
-    &quot;update_seq&quot;=&gt;9512, 
-    &quot;disk_size&quot;=&gt;27541604, 
-    &quot;compact_running&quot;=&gt;false, 
-    &quot;db_name&quot;=&gt;&quot;toast&quot;, 
-    &quot;doc_del_count&quot;=&gt;1
-  }, 
-  &quot;cookie&quot;=&gt;{}, 
-  &quot;form&quot;=&gt;{}, 
-  &quot;query&quot;=&gt;{&quot;q&quot;=&gt;&quot;stuff&quot;}, 
-  &quot;path&quot;=&gt;[&quot;toast&quot;, &quot;_ext&quot;], 
-  &quot;headers&quot;=&gt;{
-    &quot;User-Agent&quot;=&gt;&quot;curl/7.18.1 (i386-apple-darwin9.2.2) libcurl/7.18.1 zlib/1.2.3&quot;, 
-    &quot;Host&quot;=&gt;&quot;localhost:5984&quot;, 
-    &quot;Accept&quot;=&gt;&quot;*/*&quot;
-  }
-}
-
 class OSProcessRunner
   def self.run
     trace = false
@@ -78,9 +53,9 @@ class OSProcessRunner
   def add_fun(fun)
     run([&quot;add_fun&quot;, fun])
   end
-  def get_chunk
+  def get_chunks
     resp = jsgets
-    raise &quot;not a chunk&quot; unless resp.first == &quot;chunk&quot;
+    raise &quot;not a chunk&quot; unless resp.first == &quot;chunks&quot;
     return resp[1]
   end
   def run json
@@ -103,10 +78,10 @@ class OSProcessRunner
     # puts &quot;err: #{err}&quot; if err
     if resp
       rj = JSON.parse(&quot;[#{resp.chomp}]&quot;)[0]
-      if rj.respond_to?(:[]) &amp;&amp; !rj.is_a?(Array) &amp;&amp; 
-        if rj[&quot;log&quot;]
-          log = rj[&quot;log&quot;]
-          puts &quot;log: #{log}&quot; #if @trace
+      if rj.respond_to?(:[]) &amp;&amp; rj.is_a?(Array)
+        if rj[0] == &quot;log&quot;
+          log = rj[1]
+          puts &quot;log: #{log}&quot; if @trace
           rj = jsgets
         end
       end
@@ -177,131 +152,300 @@ describe &quot;query server normal case&quot; do
       @qs.run([&quot;rereduce&quot;, [@fun], vs]).should == [true, [45]]
     end
   end
+  
   # it &quot;should validate&quot;
+  describe &quot;validation&quot; do
+    before(:all) do
+      @fun = &lt;&lt;-JS
+        function(newDoc, oldDoc, userCtx) {
+          if (newDoc.bad) throw({forbidden:&quot;bad doc&quot;});
+          &quot;foo bar&quot;;
+        }
+        JS
+      @qs.reset!
+    end
+    it &quot;should allow good updates&quot; do
+      @qs.run([&quot;validate&quot;, @fun, {&quot;good&quot; =&gt; true}, {}, {}]).should == 1
+    end
+    it &quot;should reject invalid updates&quot; do
+      @qs.run([&quot;validate&quot;, @fun, {&quot;bad&quot; =&gt; true}, {}, {}]).should == {&quot;forbidden&quot;=&gt;&quot;bad doc&quot;}
+    end
+  end
   
   describe &quot;show&quot; do
-     before(:all) do
-       @fun = &lt;&lt;-JS
-         function(doc, req) {
-           return [doc.title, doc.body].join(' - ')
-         }
-         JS
-       @qs.reset!
-     end
-     it &quot;should show&quot; do
-       @qs.rrun([&quot;show_doc&quot;, @fun, 
-         {:title =&gt; &quot;Best ever&quot;, :body =&gt; &quot;Doc body&quot;}, JSON_REQ])
-       @qs.jsgets.should == {&quot;body&quot;=&gt;&quot;Best ever - Doc body&quot;}
-     end
-   end
-   
-   describe &quot;show with headers&quot; do
-     before(:all) do
-       @fun = &lt;&lt;-JS
-         function(doc, req) {
-           return {
-             headers : {&quot;X-Plankton&quot;:&quot;Rusty&quot;},
-             body : [doc.title, doc.body].join(' - ')
-           }
-           
-         }
-         JS
-       @qs.reset!
-     end
-     it &quot;should show&quot; do
-       @qs.rrun([&quot;show_doc&quot;, @fun, 
-         {:title =&gt; &quot;Best ever&quot;, :body =&gt; &quot;Doc body&quot;}])
-       @qs.jsgets.should == {&quot;headers&quot;=&gt;{&quot;X-Plankton&quot;=&gt;&quot;Rusty&quot;}, &quot;body&quot;=&gt;&quot;Best ever - Doc body&quot;}
-     end
-   end
-     
-   describe &quot;list with headers&quot; do
-     before(:each) do
-       @fun = &lt;&lt;-JS
-         function(head, row, req) {
-           if (head) return {headers : {&quot;Content-Type&quot; : &quot;text/plain&quot;}, code : 200, &quot;body&quot; : &quot;foo&quot;};
-           if (row) return 'some &quot;text&quot; here';
-           return &quot;tail&quot;;
-         };
-         JS
-       @qs.reset!
-       @qs.add_fun(@fun).should == true
-     end
-     it &quot;should send head, row, and tail&quot; do
-       @qs.rrun([&quot;list_begin&quot;, {&quot;total_rows&quot;=&gt;1000}, {&quot;q&quot; =&gt; &quot;ok&quot;}])
-       @qs.jsgets.should == {&quot;headers&quot;=&gt;{&quot;Content-Type&quot;=&gt;&quot;text/plain&quot;}, &quot;code&quot;=&gt;200, &quot;body&quot;=&gt;&quot;foo&quot;}
-       @qs.run([&quot;list_row&quot;, {&quot;foo&quot;=&gt;&quot;bar&quot;}, {&quot;q&quot; =&gt; &quot;ok&quot;}]).should == {&quot;body&quot;=&gt;&quot;some \&quot;text\&quot; here&quot;}
-       @qs.run([&quot;list_tail&quot;, {&quot;foo&quot;=&gt;&quot;bar&quot;}, {&quot;q&quot; =&gt; &quot;ok&quot;}]).should == {&quot;body&quot;=&gt;&quot;tail&quot;}
-     end
-   end
-   
-   describe &quot;list with headers and rows&quot; do
-     before(:each) do
-       @fun = &lt;&lt;-JS
-         function(head, row, req) {
-           if (head) return {headers : {&quot;Content-Type&quot; : &quot;text/plain&quot;}, code : 200, &quot;body&quot; : &quot;foo&quot;};
-           if (row) return 'row value '+row.value;
-           return &quot;tail &quot;+req.q;
-         };
-         JS
-       @qs.reset!
-       @qs.add_fun(@fun).should == true
-     end
-     it &quot;should render rows&quot; do
-       @qs.rrun([&quot;list_begin&quot;, {&quot;total_rows&quot;=&gt;1000}, {&quot;q&quot; =&gt; &quot;ok&quot;}])
-       @qs.jsgets.should == {&quot;headers&quot;=&gt;{&quot;Content-Type&quot;=&gt;&quot;text/plain&quot;}, &quot;code&quot;=&gt;200, &quot;body&quot;=&gt;&quot;foo&quot;}
-       @qs.run([&quot;list_row&quot;, {&quot;value&quot;=&gt;&quot;bar&quot;}, {&quot;q&quot; =&gt; &quot;ok&quot;}]).should == {&quot;body&quot;=&gt;&quot;row value bar&quot;}
-       @qs.run([&quot;list_row&quot;, {&quot;value&quot;=&gt;&quot;baz&quot;}, {&quot;q&quot; =&gt; &quot;ok&quot;}]).should == {&quot;body&quot;=&gt;&quot;row value baz&quot;}
-       @qs.run([&quot;list_row&quot;, {&quot;value&quot;=&gt;&quot;bam&quot;}, {&quot;q&quot; =&gt; &quot;ok&quot;}]).should == {&quot;body&quot;=&gt;&quot;row value bam&quot;}
-       @qs.run([&quot;list_tail&quot;, {&quot;q&quot; =&gt; &quot;ok&quot;}]).should == {&quot;body&quot;=&gt;&quot;tail ok&quot;}
-     end
-   end
- end # query server normal case
+    before(:all) do
+      @fun = &lt;&lt;-JS
+        function(doc, req) {
+          log(&quot;ok&quot;);
+          return [doc.title, doc.body].join(' - ');
+        }
+        JS
+      @qs.reset!
+    end
+    it &quot;should show&quot; do
+      @qs.rrun([&quot;show&quot;, @fun, 
+        {:title =&gt; &quot;Best ever&quot;, :body =&gt; &quot;Doc body&quot;}])
+      @qs.jsgets.should == [&quot;resp&quot;, {&quot;body&quot; =&gt; &quot;Best ever - Doc body&quot;}]
+    end
+  end
+  
+  describe &quot;show with headers&quot; do
+    before(:all) do
+      @fun = &lt;&lt;-JS
+        function(doc, req) {
+          var resp = {&quot;code&quot;:200, &quot;headers&quot;:{&quot;X-Plankton&quot;:&quot;Rusty&quot;}};
+          resp.body = [doc.title, doc.body].join(' - ');
+          return resp;
+        }
+        JS
+      @qs.reset!
+    end
+    it &quot;should show headers&quot; do
+      @qs.rrun([&quot;show&quot;, @fun, 
+        {:title =&gt; &quot;Best ever&quot;, :body =&gt; &quot;Doc body&quot;}])
+      @qs.jsgets.should == [&quot;resp&quot;, {&quot;code&quot;=&gt;200,&quot;headers&quot; =&gt; {&quot;X-Plankton&quot;=&gt;&quot;Rusty&quot;}, &quot;body&quot; =&gt; &quot;Best ever - Doc body&quot;}]
+    end
+  end
+    
+# end
+#                    LIST TESTS
+# __END__
+    
+  describe &quot;raw list with headers&quot; do
+    before(:each) do
+      @fun = &lt;&lt;-JS
+        function(head, req) {
+          start({headers:{&quot;Content-Type&quot; : &quot;text/plain&quot;}});
+          send(&quot;first chunk&quot;);
+          send('second &quot;chunk&quot;');
+          return &quot;tail&quot;;
+        };
+        JS
+      @qs.reset!
+      @qs.add_fun(@fun).should == true
+    end
+    it &quot;should do headers proper&quot; do
+      @qs.rrun([&quot;list&quot;, {&quot;total_rows&quot;=&gt;1000}, {&quot;q&quot; =&gt; &quot;ok&quot;}])
+      @qs.jsgets.should == [&quot;start&quot;, [&quot;first chunk&quot;, 'second &quot;chunk&quot;'], {&quot;headers&quot;=&gt;{&quot;Content-Type&quot;=&gt;&quot;text/plain&quot;}}]
+      @qs.rrun([&quot;list_end&quot;])
+      @qs.jsgets.should == [&quot;end&quot;, [&quot;tail&quot;]]
+    end
+  end
+  
+  describe &quot;list with rows&quot; do
+    before(:each) do
+      @fun = &lt;&lt;-JS
+        function(head, req) {
+          send(&quot;first chunk&quot;);
+          send(req.q);
+          var row;
+          log(&quot;about to getRow &quot; + typeof(getRow));
+          while(row = getRow()) {
+            send(row.key);        
+          };
+          return &quot;tail&quot;;
+        };
+        JS
+      @qs.run([&quot;reset&quot;]).should == true    
+      @qs.add_fun(@fun).should == true
+    end
+    it &quot;should should list em&quot; do
+      @qs.rrun([&quot;list&quot;, {&quot;foo&quot;=&gt;&quot;bar&quot;}, {&quot;q&quot; =&gt; &quot;ok&quot;}])
+      @qs.jsgets.should == [&quot;start&quot;, [&quot;first chunk&quot;, &quot;ok&quot;], {&quot;headers&quot;=&gt;{}}]
+      @qs.rrun([&quot;list_row&quot;, {&quot;key&quot;=&gt;&quot;baz&quot;}])
+      @qs.get_chunks.should == [&quot;baz&quot;]
+      @qs.rrun([&quot;list_row&quot;, {&quot;key&quot;=&gt;&quot;bam&quot;}])
+      @qs.get_chunks.should == [&quot;bam&quot;]
+      @qs.rrun([&quot;list_end&quot;])
+      @qs.jsgets.should == [&quot;end&quot;, [&quot;tail&quot;]]
+    end
+    it &quot;should work with zero rows&quot; do
+      @qs.rrun([&quot;list&quot;, {&quot;foo&quot;=&gt;&quot;bar&quot;}, {&quot;q&quot; =&gt; &quot;ok&quot;}])
+      @qs.jsgets.should == [&quot;start&quot;, [&quot;first chunk&quot;, &quot;ok&quot;], {&quot;headers&quot;=&gt;{}}]
+      @qs.rrun([&quot;list_end&quot;])
+      @qs.jsgets.should == [&quot;end&quot;, [&quot;tail&quot;]]
+    end
+  end
+  
+  describe &quot;should buffer multiple chunks sent for a single row.&quot; do
+    before(:all) do
+      @fun = &lt;&lt;-JS
+        function(head, req) {
+          send(&quot;bacon&quot;);
+          var row;
+          log(&quot;about to getRow &quot; + typeof(getRow));
+          while(row = getRow()) {
+            send(row.key);        
+            send(&quot;eggs&quot;);        
+          };
+          return &quot;tail&quot;;
+        };
+        JS
+      @qs.reset!
+      @qs.add_fun(@fun).should == true
+    end
+    it &quot;should should buffer em&quot; do
+      @qs.rrun([&quot;list&quot;, {&quot;foo&quot;=&gt;&quot;bar&quot;}, {&quot;q&quot; =&gt; &quot;ok&quot;}])
+      @qs.jsgets.should == [&quot;start&quot;, [&quot;bacon&quot;], {&quot;headers&quot;=&gt;{}}]
+      @qs.rrun([&quot;list_row&quot;, {&quot;key&quot;=&gt;&quot;baz&quot;}])
+      @qs.get_chunks.should == [&quot;baz&quot;, &quot;eggs&quot;]
+      @qs.rrun([&quot;list_row&quot;, {&quot;key&quot;=&gt;&quot;bam&quot;}])
+      @qs.get_chunks.should == [&quot;bam&quot;, &quot;eggs&quot;]
+      @qs.rrun([&quot;list_end&quot;])
+      @qs.jsgets.should == [&quot;end&quot;, [&quot;tail&quot;]]
+    end
+  end
+
+  describe &quot;example list&quot; do
+    before(:all) do
+      @fun = &lt;&lt;-JS
+        function(head, req) {
+          send(&quot;first chunk&quot;);
+          send(req.q);
+          var row;
+          while(row = getRow()) {
+            send(row.key);    
+          };
+          return &quot;early&quot;;
+        };
+        JS
+      @qs.reset!
+      @qs.add_fun(@fun).should == true
+    end
+    it &quot;should run normal&quot; do
+      @qs.run([&quot;list&quot;, {&quot;foo&quot;=&gt;&quot;bar&quot;}, {&quot;q&quot; =&gt; &quot;ok&quot;}]).should == [&quot;start&quot;, [&quot;first chunk&quot;, &quot;ok&quot;], {&quot;headers&quot;=&gt;{}}]
+      @qs.run([&quot;list_row&quot;, {&quot;key&quot;=&gt;&quot;baz&quot;}]).should ==  [&quot;chunks&quot;, [&quot;baz&quot;]]
+      @qs.run([&quot;list_row&quot;, {&quot;key&quot;=&gt;&quot;bam&quot;}]).should ==  [&quot;chunks&quot;, [&quot;bam&quot;]]
+      @qs.run([&quot;list_row&quot;, {&quot;key&quot;=&gt;&quot;foom&quot;}]).should == [&quot;chunks&quot;, [&quot;foom&quot;]]
+      @qs.run([&quot;list_row&quot;, {&quot;key&quot;=&gt;&quot;fooz&quot;}]).should == [&quot;chunks&quot;, [&quot;fooz&quot;]]
+      @qs.run([&quot;list_row&quot;, {&quot;key&quot;=&gt;&quot;foox&quot;}]).should == [&quot;chunks&quot;, [&quot;foox&quot;]]
+      @qs.run([&quot;list_end&quot;]).should == [&quot;end&quot; , [&quot;early&quot;]]
+    end
+  end
+  
+  describe &quot;only goes to 2 list&quot; do
+    before(:all) do
+      @fun = &lt;&lt;-JS
+        function(head, req) {
+          send(&quot;first chunk&quot;);
+          send(req.q);
+          var row, i=0;
+          while(row = getRow()) {
+            send(row.key);  
+            i += 1;
+            if (i &gt; 2) {
+              return('early tail');
+            }  
+          };
+        };
+        JS
+      @qs.reset!
+      @qs.add_fun(@fun).should == true
+    end
+    it &quot;should end early&quot; do
+      @qs.run([&quot;list&quot;, {&quot;foo&quot;=&gt;&quot;bar&quot;}, {&quot;q&quot; =&gt; &quot;ok&quot;}]).
+        should == [&quot;start&quot;, [&quot;first chunk&quot;, &quot;ok&quot;], {&quot;headers&quot;=&gt;{}}]
+      @qs.run([&quot;list_row&quot;, {&quot;key&quot;=&gt;&quot;baz&quot;}]).
+        should ==  [&quot;chunks&quot;, [&quot;baz&quot;]]
 
- describe &quot;query server errors&quot; do
-   before(:each) do
-     @qs = QueryServerRunner.run
-   end
-   after(:each) do
-     @qs.close
-   end
-   
-   describe &quot;list&quot; do
-     before(:each) do
-       @fun = &lt;&lt;-JS
-         function(head, row, req) {
-           if (head) return {headers : {&quot;Content-Type&quot; : &quot;text/plain&quot;}, code : 200, &quot;body&quot; : &quot;foo&quot;};
-           if (row) return 'row value '+row.value;
-           return &quot;tail &quot;+req.q;
-         };
-         JS
-       @qs.run([&quot;reset&quot;]).should == true    
-       @qs.add_fun(@fun).should == true
-     end
-     it &quot;should reset in the middle&quot; do
-       @qs.rrun([&quot;list_begin&quot;, {&quot;total_rows&quot;=&gt;1000}, {&quot;q&quot; =&gt; &quot;ok&quot;}])
-       @qs.jsgets.should == {&quot;headers&quot;=&gt;{&quot;Content-Type&quot;=&gt;&quot;text/plain&quot;}, &quot;code&quot;=&gt;200, &quot;body&quot;=&gt;&quot;foo&quot;}
-       @qs.run([&quot;list_row&quot;, {&quot;value&quot;=&gt;&quot;bar&quot;}, {&quot;q&quot; =&gt; &quot;ok&quot;}]).should == {&quot;body&quot;=&gt;&quot;row value bar&quot;}
-       @qs.run([&quot;reset&quot;]).should == true
-     end
-   end  
- end #query server that errors
+      @qs.run([&quot;list_row&quot;, {&quot;key&quot;=&gt;&quot;bam&quot;}]).
+        should ==  [&quot;chunks&quot;, [&quot;bam&quot;]]
+
+      @qs.run([&quot;list_row&quot;, {&quot;key&quot;=&gt;&quot;foom&quot;}]).
+        should == [&quot;end&quot;, [&quot;foom&quot;, &quot;early tail&quot;]]
+      # here's where js has to discard quit properly
+      @qs.run([&quot;reset&quot;]).
+        should == true
+    end
+  end
+end
 
-## tests for the generic &quot;echo&quot; external
- 
-# describe &quot;running an external&quot; do
-#   before(:all) do
-#     @ext = ExternalRunner.run
-#     
-#   end
-#   it &quot;should respond to 'info'&quot; do
-#     @ext.rrun(['info'])
-#     @ext.jsgets.should == [&quot;info&quot;, &quot;echo&quot;, &quot;external server that prints its arguments as JSON&quot;]
-#   end
-#   it &quot;should echo the request&quot; do
+def should_have_exited qs
+  begin
+    qs.run([&quot;reset&quot;])
+    &quot;raise before this&quot;.should == true
+  rescue RuntimeError =&gt; e
+    e.message.should == &quot;no response&quot;
+  rescue Errno::EPIPE
+    true.should == true
+  end
+end
 
-#     @ext.rrun(['req', req_obj])
-#     @ext.jsgets.should == [&quot;x&quot;]
-#   end
-# end
-# 
+describe &quot;query server that exits&quot; do
+  before(:each) do
+    @qs = QueryServerRunner.run
+  end
+  after(:each) do
+    @qs.close
+  end
+  
+  describe &quot;old style list&quot; do
+    before(:each) do
+      @fun = &lt;&lt;-JS
+        function(head, req, foo, bar) {
+          return &quot;stuff&quot;;
+        }
+        JS
+      @qs.reset!
+      @qs.add_fun(@fun).should == true
+    end
+    it &quot;should get a warning&quot; do
+      resp = @qs.run([&quot;list&quot;, {&quot;foo&quot;=&gt;&quot;bar&quot;}, {&quot;q&quot; =&gt; &quot;ok&quot;}])
+      resp[&quot;error&quot;].should == &quot;render_error&quot;
+      resp[&quot;reason&quot;].should include(&quot;the list API has changed&quot;)
+    end
+  end
+  
+  describe &quot;only goes to 2 list&quot; do
+    before(:each) do
+      @fun = &lt;&lt;-JS
+        function(head, req) {
+          send(&quot;bacon&quot;)
+          var row, i = 0;
+          while(row = getRow()) {
+            send(row.key);        
+            i += 1;
+            if (i &gt; 2) {
+              return('early');
+            }
+          };
+        }
+        JS
+      @qs.reset!
+      @qs.add_fun(@fun).should == true
+    end
+    it &quot;should exit if erlang sends too many rows&quot; do
+      @qs.run([&quot;list&quot;, {&quot;foo&quot;=&gt;&quot;bar&quot;}, {&quot;q&quot; =&gt; &quot;ok&quot;}]).should == [&quot;start&quot;, [&quot;bacon&quot;], {&quot;headers&quot;=&gt;{}}]
+      @qs.run([&quot;list_row&quot;, {&quot;key&quot;=&gt;&quot;baz&quot;}]).should ==  [&quot;chunks&quot;, [&quot;baz&quot;]]
+      @qs.run([&quot;list_row&quot;, {&quot;key&quot;=&gt;&quot;foom&quot;}]).should == [&quot;chunks&quot;, [&quot;foom&quot;]]
+      @qs.run([&quot;list_row&quot;, {&quot;key&quot;=&gt;&quot;fooz&quot;}]).should == [&quot;end&quot;, [&quot;fooz&quot;, &quot;early&quot;]]
+      @qs.rrun([&quot;list_row&quot;, {&quot;key&quot;=&gt;&quot;foox&quot;}])
+      @qs.jsgets[&quot;error&quot;].should == &quot;query_server_error&quot;
+      should_have_exited @qs
+    end
+  end
+  
+  describe &quot;raw list&quot; do
+    before(:each) do
+      @fun = &lt;&lt;-JS
+        function(head, req) {
+          send(&quot;first chunk&quot;);
+          send(req.q);
+          var row;
+          while(row = getRow()) {
+            send(row.key);        
+          };
+          return &quot;tail&quot;;
+        };
+        JS
+      @qs.run([&quot;reset&quot;]).should == true    
+      @qs.add_fun(@fun).should == true
+    end
+    it &quot;should exit if it gets a non-row in the middle&quot; do
+      @qs.rrun([&quot;list&quot;, {&quot;foo&quot;=&gt;&quot;bar&quot;}, {&quot;q&quot; =&gt; &quot;ok&quot;}])
+      @qs.jsgets.should == [&quot;start&quot;, [&quot;first chunk&quot;, &quot;ok&quot;], {&quot;headers&quot;=&gt;{}}]
+      @qs.run([&quot;reset&quot;])[&quot;error&quot;].should == &quot;query_server_error&quot;
+      should_have_exited @qs
+    end
+  end  
+end</diff>
      <filename>test/query_server_spec.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>549a98e80c989990cbaf00b03c8914f3bfe13c45</id>
    </parent>
  </parents>
  <author>
    <name>jchris</name>
    <email>jchris@13f79535-47bb-0310-9956-ffa450edef68</email>
  </author>
  <url>http://github.com/halorgium/couchdb/commit/77e6336ca275cf11c9623bb4fd8b297e1a94eead</url>
  <id>77e6336ca275cf11c9623bb4fd8b297e1a94eead</id>
  <committed-date>2009-06-14T11:45:49-07:00</committed-date>
  <authored-date>2009-06-14T11:45:49-07:00</authored-date>
  <message>merge list-iterator branch to trunk. changes JavaScript _list API


git-svn-id: http://svn.apache.org/repos/asf/couchdb/trunk@784601 13f79535-47bb-0310-9956-ffa450edef68</message>
  <tree>00bde7d855ba9cd07c2e5d0463dfd32c4f0badcc</tree>
  <committer>
    <name>jchris</name>
    <email>jchris@13f79535-47bb-0310-9956-ffa450edef68</email>
  </committer>
</commit>
