You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by fd...@apache.org on 2010/12/22 20:43:30 UTC

svn commit: r1052047 - in /couchdb/trunk: share/www/script/test/attachments_multipart.js src/couchdb/couch_httpd_db.erl

Author: fdmanana
Date: Wed Dec 22 19:43:30 2010
New Revision: 1052047

URL: http://svn.apache.org/viewvc?rev=1052047&view=rev
Log:
Allow a multipart/mixed document GET to send the attachments in encoded (compressed) form

Currently this API is not used internally but it's a very important one for the new replicator.


Modified:
    couchdb/trunk/share/www/script/test/attachments_multipart.js
    couchdb/trunk/src/couchdb/couch_httpd_db.erl

Modified: 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=1052047&r1=1052046&r2=1052047&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/test/attachments_multipart.js (original)
+++ couchdb/trunk/share/www/script/test/attachments_multipart.js Wed Dec 22 19:43:30 2010
@@ -278,5 +278,131 @@ couchTests.attachments_multipart= functi
   T(doc._attachments['bar.txt'].follows == true);
   
   T(sections[1].body == "this is 18 chars l");
-  
+
+
+  // check that with the document multipart/mixed API it's possible to receive
+  // attachments in compressed form (if they're stored in compressed form)
+
+  var server_config = [
+    {
+      section: "attachments",
+      key: "compression_level",
+      value: "8"
+    },
+    {
+      section: "attachments",
+      key: "compressible_types",
+      value: "text/plain"
+    }
+  ];
+
+  function testMultipartAttCompression() {
+    var doc = { _id: "foobar" };
+    var lorem =
+      CouchDB.request("GET", "/_utils/script/test/lorem.txt").responseText;
+    var helloData = "hello world";
+
+    TEquals(true, db.save(doc).ok);
+
+    var firstRev = doc._rev;
+    var xhr = CouchDB.request(
+      "PUT",
+      "/" + db.name + "/" + doc._id + "/data.bin?rev=" + firstRev,
+      {
+        body: helloData,
+        headers: {"Content-Type": "application/binary"}
+      }
+    );
+    TEquals(201, xhr.status);
+
+    var secondRev = db.open(doc._id)._rev;
+    xhr = CouchDB.request(
+      "PUT",
+      "/" + db.name + "/" + doc._id + "/lorem.txt?rev=" + secondRev,
+      {
+        body: lorem,
+        headers: {"Content-Type": "text/plain"}
+      }
+    );
+    TEquals(201, xhr.status);
+
+    var thirdRev = db.open(doc._id)._rev;
+
+    xhr = CouchDB.request(
+      "GET",
+      '/' + db.name + '/' + doc._id + '?open_revs=["' + thirdRev + '"]',
+      {
+        headers: {
+          "Accept": "multipart/mixed",
+          "X-CouchDB-Send-Encoded-Atts": "true"
+        }
+      }
+    );
+    TEquals(200, xhr.status);
+
+    var sections = parseMultipart(xhr);
+    // 1 section, with a multipart/related Content-Type
+    TEquals(1, sections.length);
+    TEquals(0,
+      sections[0].headers['Content-Type'].indexOf('multipart/related;'));
+
+    var innerSections = parseMultipart(sections[0]);
+    // 3 inner sections: a document body section plus 2 attachment data sections
+    TEquals(3, innerSections.length);
+    TEquals('application/json', innerSections[0].headers['content-type']);
+
+    doc = JSON.parse(innerSections[0].body);
+
+    TEquals(true, doc._attachments['lorem.txt'].follows);
+    TEquals("gzip", doc._attachments['lorem.txt'].encoding);
+    TEquals(true, doc._attachments['data.bin'].follows);
+    T(doc._attachments['data.bin'] !== "gzip");
+
+    if (innerSections[1].body === helloData) {
+      T(innerSections[2].body !== lorem);
+    } else if (innerSections[2].body === helloData) {
+      T(innerSections[1].body !== lorem);
+    } else {
+      T(false, "Could not found data.bin attachment data");
+    }
+
+    // now test that it works together with the atts_since parameter
+
+    xhr = CouchDB.request(
+      "GET",
+      '/' + db.name + '/' + doc._id + '?open_revs=["' + thirdRev + '"]' +
+        '&atts_since=["' + secondRev + '"]',
+      {
+        headers: {
+          "Accept": "multipart/mixed",
+          "X-CouchDB-Send-Encoded-Atts": "true"
+        }
+      }
+    );
+    TEquals(200, xhr.status);
+
+    sections = parseMultipart(xhr);
+    // 1 section, with a multipart/related Content-Type
+    TEquals(1, sections.length);
+    TEquals(0,
+      sections[0].headers['Content-Type'].indexOf('multipart/related;'));
+
+    innerSections = parseMultipart(sections[0]);
+    // 2 inner sections: a document body section plus 1 attachment data section
+    TEquals(2, innerSections.length);
+    TEquals('application/json', innerSections[0].headers['content-type']);
+
+    doc = JSON.parse(innerSections[0].body);
+
+    TEquals(true, doc._attachments['lorem.txt'].follows);
+    TEquals("gzip", doc._attachments['lorem.txt'].encoding);
+    TEquals("undefined", typeof doc._attachments['data.bin'].follows);
+    TEquals(true, doc._attachments['data.bin'].stub);
+    T(innerSections[1].body !== lorem);
+  }
+
+  run_on_modified_server(server_config, testMultipartAttCompression);
+
+  // cleanup
+  db.deleteDb();
 };

Modified: couchdb/trunk/src/couchdb/couch_httpd_db.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_db.erl?rev=1052047&r1=1052046&r2=1052047&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_db.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_db.erl Wed Dec 22 19:43:30 2010
@@ -747,24 +747,30 @@ send_doc_efficiently(#httpd{mochi_req = 
         send_json(Req, 200, Headers, couch_doc:to_json_obj(Doc, Options))
     end.
 
-send_docs_multipart(Req, Results, Options) ->
+send_docs_multipart(Req, Results, Options1) ->
     OuterBoundary = couch_uuids:random(),
     InnerBoundary = couch_uuids:random(),
+    {Options, CompressedAtts} =
+    case couch_httpd:header_value(Req, "X-CouchDB-Send-Encoded-Atts") of
+    "true" ->
+        {[attachments, follows, att_encoding_info | Options1], true};
+    _ ->
+        {[attachments, follows | Options1], false}
+    end,
     CType = {"Content-Type", 
         "multipart/mixed; boundary=\"" ++ ?b2l(OuterBoundary) ++ "\""},
     {ok, Resp} = start_chunked_response(Req, 200, [CType]),
     couch_httpd:send_chunk(Resp, <<"--", OuterBoundary/binary>>),
     lists:foreach(
         fun({ok, #doc{atts=Atts}=Doc}) ->
-            JsonBytes = ?JSON_ENCODE(couch_doc:to_json_obj(Doc, 
-                    [attachments,follows|Options])),
+            JsonBytes = ?JSON_ENCODE(couch_doc:to_json_obj(Doc, Options)),
             {ContentType, _Len} = couch_doc:len_doc_to_multi_part_stream(
-                    InnerBoundary, JsonBytes, Atts, false),
+                    InnerBoundary, JsonBytes, Atts, CompressedAtts),
             couch_httpd:send_chunk(Resp, <<"\r\nContent-Type: ",
                     ContentType/binary, "\r\n\r\n">>),
             couch_doc:doc_to_multi_part_stream(InnerBoundary, JsonBytes, Atts,
                     fun(Data) -> couch_httpd:send_chunk(Resp, Data)
-                    end, false),
+                    end, CompressedAtts),
              couch_httpd:send_chunk(Resp, <<"\r\n--", OuterBoundary/binary>>);
         ({{not_found, missing}, RevId}) ->
              RevStr = couch_doc:rev_to_str(RevId),