couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dam...@apache.org
Subject svn commit: r836087 - in /couchdb/trunk: share/Makefile.am share/www/script/couch_tests.js share/www/script/test/attachments_multipart.js src/couchdb/couch_doc.erl src/couchdb/couch_httpd_db.erl
Date Sat, 14 Nov 2009 01:15:00 GMT
Author: damien
Date: Sat Nov 14 01:14:59 2009
New Revision: 836087

URL: http://svn.apache.org/viewvc?rev=836087&view=rev
Log:
Better testing of multipart/related

Added:
    couchdb/trunk/share/www/script/test/attachments_multipart.js
Modified:
    couchdb/trunk/share/Makefile.am
    couchdb/trunk/share/www/script/couch_tests.js
    couchdb/trunk/src/couchdb/couch_doc.erl
    couchdb/trunk/src/couchdb/couch_httpd_db.erl

Modified: couchdb/trunk/share/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/Makefile.am?rev=836087&r1=836086&r2=836087&view=diff
==============================================================================
--- couchdb/trunk/share/Makefile.am (original)
+++ couchdb/trunk/share/Makefile.am Sat Nov 14 01:14:59 2009
@@ -97,6 +97,7 @@
     www/script/sha1.js \
     www/script/test/all_docs.js \
     www/script/test/attachments.js \
+    www/script/test/attachments_multipart.js \
     www/script/test/attachment_names.js \
     www/script/test/attachment_paths.js \
     www/script/test/attachment_views.js \

Modified: couchdb/trunk/share/www/script/couch_tests.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/couch_tests.js?rev=836087&r1=836086&r2=836087&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/couch_tests.js [utf-8] (original)
+++ couchdb/trunk/share/www/script/couch_tests.js [utf-8] Sat Nov 14 01:14:59 2009
@@ -30,6 +30,7 @@
 // keep sorted
 loadTest("all_docs.js");
 loadTest("attachments.js");
+loadTest("attachments_multipart.js");
 loadTest("attachment_names.js");
 loadTest("attachment_paths.js");
 loadTest("attachment_views.js");

Added: couchdb/trunk/share/www/script/test/attachments_multipart.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/attachments_multipart.js?rev=836087&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/attachments_multipart.js (added)
+++ couchdb/trunk/share/www/script/test/attachments_multipart.js Sat Nov 14 01:14:59 2009
@@ -0,0 +1,185 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+couchTests.attachments_multipart= function(debug) {
+  var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
+  db.deleteDb();
+  db.createDb();
+  if (debug) debugger;
+  
+  // mime multipart
+            
+  xhr = CouchDB.request("PUT", "/test_suite_db/multipart", {
+    headers: {"Content-Type": "multipart/related;boundary=\"abc123\""},
+    body:
+      "--abc123\r\n" +
+      "content-type: application/json\r\n" +
+      "\r\n" +
+      JSON.stringify({
+        "body":"This is a body.",
+        "_attachments":{
+          "foo.txt": {
+            "follows":true,
+            "content_type":"text/plain",
+            "length":21
+            },
+          "bar.txt": {
+            "follows":true,
+            "content_type":"text/plain",
+            "length":20
+            },
+          "baz.txt": {
+            "follows":true,
+            "content_type":"text/plain",
+            "length":19
+            }
+          }
+        }) +
+      "\r\n--abc123\r\n" +
+      "\r\n" +
+      "this is 21 chars long" +
+      "\r\n--abc123\r\n" +
+      "\r\n" +
+      "this is 20 chars lon" +
+      "\r\n--abc123\r\n" +
+      "\r\n" +
+      "this is 19 chars lo" +
+      "\r\n--abc123--"
+    });
+    
+  var result = JSON.parse(xhr.responseText);
+  
+  T(result.ok)
+  
+  
+    
+  TEquals(201, xhr.status, "should send 201 Accepted");
+  
+  xhr = CouchDB.request("GET", "/test_suite_db/multipart/foo.txt");
+  
+  T(xhr.responseText == "this is 21 chars long");
+  
+  xhr = CouchDB.request("GET", "/test_suite_db/multipart/bar.txt");
+  
+  T(xhr.responseText == "this is 20 chars lon");
+  
+  xhr = CouchDB.request("GET", "/test_suite_db/multipart/baz.txt");
+  
+  T(xhr.responseText == "this is 19 chars lo");
+  
+  // now edit an attachment
+  
+  var doc = db.open("multipart");
+  
+  T(doc._attachments["foo.txt"].stub == true);
+  T(doc._attachments["bar.txt"].stub == true);
+  T(doc._attachments["baz.txt"].stub == true);
+  
+  //lets change attachment bar
+  delete doc._attachments["bar.txt"].stub; // remove stub member (or could set to false)
+  doc._attachments["bar.txt"].length = 18;
+  doc._attachments["bar.txt"].follows = true;
+  //lets delete attachment baz:
+  delete doc._attachments["baz.txt"];
+  
+  var xhr = CouchDB.request("PUT", "/test_suite_db/multipart", {
+    headers: {"Content-Type": "multipart/related;boundary=\"abc123\""},
+    body:
+      "--abc123\r\n" +
+      "content-type: application/json\r\n" +
+      "\r\n" +
+      JSON.stringify(doc) +
+      "\r\n--abc123\r\n" +
+      "\r\n" +
+      "this is 18 chars l" +
+      "\r\n--abc123--"
+    });
+  
+  xhr = CouchDB.request("GET", "/test_suite_db/multipart/bar.txt");
+  
+  T(xhr.responseText == "this is 18 chars l");
+  
+  xhr = CouchDB.request("GET", "/test_suite_db/multipart/baz.txt");
+  T(xhr.status == 404);
+  
+  xhr = CouchDB.request("GET", "/test_suite_db/multipart?attachments=true",
+    {headers:{"accept": "multipart/related,*/*;"}});
+
+  var headers = xhr.getAllResponseHeaders();
+  
+  var ctype = xhr.getResponseHeader("Content-Type");
+  
+  var ctypeArgs = ctype.split("; ").slice(1);
+  var boundary = null;
+  for(var i=0; i<ctypeArgs.length; i++) {
+    if (ctypeArgs[i].indexOf("boundary=") == 0) {
+      boundary = ctypeArgs[i].split("=")[1];
+      if (boundary.charAt(0) == '"') {
+        // stringified boundary, parse as json 
+        // (will maybe not if there are escape quotes)
+        boundary = JSON.parse(boundary);
+      }
+    }
+  }
+  
+  T(boundary != null);
+  
+  function parseMime(boundary, mimetext) {
+    // strip off leading boundary
+    var leading = "--" + boundary + "\r\n";
+    var last = "\r\n--" + boundary + "--";
+    
+    // strip off leading and trailing boundary
+    var leadingIdx = mimetext.indexOf(leading) + leading.length;
+    var trailingIdx = mimetext.indexOf(last);
+    mimetext = mimetext.slice(leadingIdx, trailingIdx);
+    
+    // now split the sections
+    var sections = mimetext.split(new RegExp("\\r\\n--" + boundary));
+    
+    // spilt out the headers for each section
+    for(var i=0; i < sections.length; i++) {
+      var section = sections[i];
+      var headerEndIdx = section.indexOf("\r\n\r\n");
+      var headersraw = section.slice(0, headerEndIdx).split(/\r\n/);
+      var body = section.slice(headerEndIdx + 4);
+      var headers = {};
+      for(var j=0; j<headersraw.length; j++) {
+        var tmp = headersraw[j].split(": ");
+        headers[tmp[0]] = tmp[1]; 
+      }
+      sections[i] = {"headers":headers, "body":body};
+    }
+    
+    return sections;
+  }
+  
+  // parse out the multipart
+  
+  T(xhr.status == 200);
+  
+  var sections = parseMime(boundary, xhr.responseText);
+  
+  // The first section is the json doc. Check it's content-type. It contains
+  // the metadata for all the following attachments
+  
+  T(sections[0].headers['content-type'] == "application/json");
+  
+  var doc = JSON.parse(sections[0].body);
+  
+  T(doc._attachments['foo.txt'].follows = true);
+  T(doc._attachments['bar.txt'].follows = true);
+  
+  T(sections[1].body == "this is 21 chars long");
+  T(sections[2].body == "this is 18 chars l");
+  
+};

Modified: couchdb/trunk/src/couchdb/couch_doc.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_doc.erl?rev=836087&r1=836086&r2=836087&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_doc.erl (original)
+++ couchdb/trunk/src/couchdb/couch_doc.erl Sat Nov 14 01:14:59 2009
@@ -340,14 +340,14 @@
 len_doc_to_multi_part_stream(Boundary,JsonBytes,Atts,AttsSinceRevPos) ->
     2 + % "--"
     size(Boundary) +
-    34 + % "\r\ncontent-type: application/json\r\n"
+    36 + % "\r\ncontent-type: application/json\r\n\r\n"
     iolist_size(JsonBytes) +
     4 + % "\r\n--"
     size(Boundary) +
     + lists:foldl(fun(#att{revpos=RevPos,len=Len}, AccAttsSize) ->
             if RevPos > AttsSinceRevPos ->
                 AccAttsSize +  
-                2 + % "\r\n"
+                4 + % "\r\n\r\n"
                 Len +
                 4 + % "\r\n--"
                 size(Boundary);
@@ -358,17 +358,18 @@
     2. % "--"
     
 doc_to_multi_part_stream(Boundary,JsonBytes,Atts,AttsSinceRevPos,WriteFun) ->
-    WriteFun([<<"--", Boundary, "\r\ncontent-type: application/json\r\n">>,
-            JsonBytes, <<"\r\n--", Boundary>>]),
+    WriteFun([<<"--", Boundary/binary,
+            "\r\ncontent-type: application/json\r\n\r\n">>,
+            JsonBytes, <<"\r\n--", Boundary/binary>>]),
     atts_to_mp(Atts, Boundary, WriteFun, AttsSinceRevPos).
 
 atts_to_mp([], _Boundary, WriteFun, _AttsSinceRevPos) ->
     WriteFun(<<"--">>);
 atts_to_mp([#att{revpos=RevPos} = Att | RestAtts], Boundary, WriteFun, 
         AttsSinceRevPos) when RevPos > AttsSinceRevPos ->
-    WriteFun(<<"\r\n">>),
+    WriteFun(<<"\r\n\r\n">>),
     att_foldl(Att, fun(Data, ok) -> WriteFun(Data) end, ok),
-    WriteFun(<<"\r\n--", Boundary>>),
+    WriteFun(<<"\r\n--", Boundary/binary>>),
     atts_to_mp(RestAtts, Boundary, WriteFun, AttsSinceRevPos);
 atts_to_mp([_ | RestAtts], Boundary, WriteFun, AttsSinceRevPos) ->
     atts_to_mp(RestAtts, Boundary, WriteFun, AttsSinceRevPos).

Modified: couchdb/trunk/src/couchdb/couch_httpd_db.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_db.erl?rev=836087&r1=836086&r2=836087&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_db.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_db.erl Sat Nov 14 01:14:59 2009
@@ -784,7 +784,7 @@
     
     Loc = absolute_uri(Req, "/" ++ ?b2l(Db#db.name) ++ "/" ++ ?b2l(DocId)),
     RespHeaders = [{"Location", Loc}],
-    case couch_httpd:header_value(Req, "content-type") of
+    case couch_httpd:header_value(Req, "Content-Type") of
     ("multipart/related" ++  _Rest) = ContentType->
         Doc0 = couch_doc:doc_from_multi_part_stream(ContentType,
                 fun() -> receive_request_data(Req) end),
@@ -869,18 +869,18 @@
             undefined       -> [];
             AcceptHeader    -> string:tokens(AcceptHeader, ", ")
         end,
-        case lists:member(AcceptedTypes, "multipart/related") of
+        case lists:member("multipart/related", AcceptedTypes) of
         false ->
             send_json(Req, 200, [], couch_doc:to_json_obj(Doc, Options));
         true ->
             Boundary = couch_uuids:random(),
-            JsonBytes = ?JSON_ENCODE(couch_doc:to_json_obj(Doc, Options)),
+            JsonBytes = ?JSON_ENCODE(couch_doc:to_json_obj(Doc, [follows|Options])),
             AttsSinceRevPos = proplists:get_value(atts_after_revpos, Options, 0),
             Len = couch_doc:len_doc_to_multi_part_stream(Boundary,JsonBytes,Atts,
                     AttsSinceRevPos),
-            CType = {<<"content-type">>, 
-                    <<"multipart/related; boundary=", Boundary/binary>>},
-            Resp = start_response_length(Req, 200, [CType | Headers], Len),
+            CType = {<<"Content-Type">>, 
+                    <<"multipart/related; boundary=\"", Boundary/binary, "\"">>},
+            {ok, Resp} = start_response_length(Req, 200, [CType|Headers], Len),
             couch_doc:doc_to_multi_part_stream(Boundary,JsonBytes,Atts,
                     AttsSinceRevPos,
                     fun(Data) -> couch_httpd:send(Resp, Data) end)



Mime
View raw message