summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/www/script/couch_tests.js193
-rw-r--r--src/couchdb/couch_httpd_db.erl16
2 files changed, 201 insertions, 8 deletions
diff --git a/share/www/script/couch_tests.js b/share/www/script/couch_tests.js
index d5cd7ff9..8a00dc93 100644
--- a/share/www/script/couch_tests.js
+++ b/share/www/script/couch_tests.js
@@ -912,6 +912,197 @@ var tests = {
},
+ attachment_paths : function(debug) {
+ var db = new CouchDB("test_suite_db");
+ db.deleteDb();
+ db.createDb();
+ if (debug) debugger;
+
+
+ // first just save a regular doc with an attachment that has a slash in the url.
+ // (also gonna run an encoding check case)
+ var binAttDoc = {
+ _id: "bin_doc",
+ _attachments:{
+ "foo/bar.txt": {
+ content_type:"text/plain",
+ data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
+ },
+ "foo%2Fbaz.txt": {
+ content_type:"text/plain",
+ data: "V2UgbGlrZSBwZXJjZW50IHR3byBGLg=="
+ }
+ }
+ }
+
+ T(db.save(binAttDoc).ok);
+
+ var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo/bar.txt");
+ T(xhr.responseText == "This is a base64 encoded text");
+ T(xhr.getResponseHeader("Content-Type") == "text/plain");
+
+ // lets try it with an escaped attachment id...
+ // weird that it's at two urls
+ var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo%2Fbar.txt");
+ T(xhr.status == 200);
+ // xhr.responseText == "This is a base64 encoded text"
+
+ var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo/baz.txt");
+ T(xhr.status == 404);
+
+ var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo%252Fbaz.txt");
+ T(xhr.status == 200);
+ T(xhr.responseText == "We like percent two F.");
+
+ // require a _rev to PUT
+ var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc/foo/attachment.txt", {
+ headers:{"Content-Type":"text/plain;charset=utf-8"},
+ body:"Just some text"
+ });
+ T(xhr.status == 412);
+
+ var xhr = CouchDB.request("PUT", "/test_suite_db/bin_doc/foo/bar2.txt?rev=" + binAttDoc._rev, {
+ body:"This is no base64 encoded text",
+ headers:{"Content-Type": "text/plain;charset=utf-8"}
+ });
+ T(xhr.status == 201);
+ var rev = JSON.parse(xhr.responseText).rev;
+
+ binAttDoc = db.open("bin_doc");
+
+ T(binAttDoc._attachments["foo/bar.txt"] !== undefined);
+ T(binAttDoc._attachments["foo%2Fbaz.txt"] !== undefined);
+ T(binAttDoc._attachments["foo/bar2.txt"] !== undefined);
+ T(binAttDoc._attachments["foo/bar2.txt"].content_type == "text/plain;charset=utf-8");
+ T(binAttDoc._attachments["foo/bar2.txt"].length == 30);
+
+ //// now repeat the while thing with a design doc
+
+ // first just save a regular doc with an attachment that has a slash in the url.
+ // (also gonna run an encoding check case)
+ var binAttDoc = {
+ _id: "_design/bin_doc",
+ _attachments:{
+ "foo/bar.txt": {
+ content_type:"text/plain",
+ data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
+ },
+ "foo%2Fbaz.txt": {
+ content_type:"text/plain",
+ data: "V2UgbGlrZSBwZXJjZW50IHR3byBGLg=="
+ }
+ }
+ }
+
+ T(db.save(binAttDoc).ok);
+
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design%2Fbin_doc/foo/bar.txt");
+ T(xhr.responseText == "This is a base64 encoded text");
+ T(xhr.getResponseHeader("Content-Type") == "text/plain");
+
+ // lets try it with an escaped attachment id...
+ // weird that it's at two urls
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design%2Fbin_doc/foo%2Fbar.txt");
+ T(xhr.responseText == "This is a base64 encoded text");
+ T(xhr.status == 200);
+
+ // err, 3 urls
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/bin_doc/foo%2Fbar.txt");
+ T(xhr.responseText == "This is a base64 encoded text");
+ T(xhr.status == 200);
+
+ // I mean um, 4 urls
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/bin_doc/foo/bar.txt");
+ T(xhr.responseText == "This is a base64 encoded text");
+ T(xhr.status == 200);
+
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design%2Fbin_doc/foo/baz.txt");
+ T(xhr.status == 404);
+
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design%2Fbin_doc/foo%252Fbaz.txt");
+ T(xhr.status == 200);
+ T(xhr.responseText == "We like percent two F.");
+
+ // require a _rev to PUT
+ var xhr = CouchDB.request("PUT", "/test_suite_db/_design%2Fbin_doc/foo/attachment.txt", {
+ headers:{"Content-Type":"text/plain;charset=utf-8"},
+ body:"Just some text"
+ });
+ T(xhr.status == 412);
+
+ var xhr = CouchDB.request("PUT", "/test_suite_db/_design%2Fbin_doc/foo/bar2.txt?rev=" + binAttDoc._rev, {
+ body:"This is no base64 encoded text",
+ headers:{"Content-Type": "text/plain;charset=utf-8"}
+ });
+ T(xhr.status == 201);
+ var rev = JSON.parse(xhr.responseText).rev;
+
+ binAttDoc = db.open("_design/bin_doc");
+
+ T(binAttDoc._attachments["foo/bar.txt"] !== undefined);
+ T(binAttDoc._attachments["foo/bar2.txt"] !== undefined);
+ T(binAttDoc._attachments["foo/bar2.txt"].content_type == "text/plain;charset=utf-8");
+ T(binAttDoc._attachments["foo/bar2.txt"].length == 30);
+
+ },
+
+ design_paths : function(debug) {
+ var db = new CouchDB("test_suite_db");
+ db.deleteDb();
+ db.createDb();
+ if (debug) debugger;
+
+ // create a ddoc w bulk_docs
+ db.bulkSave([{
+ _id : "_design/test",
+ views : {
+ "testing" : {
+ "map" : "function(){emit(1,1)}"
+ }
+ }
+ }])
+
+ // ddoc is getable
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/test");
+ var resp = JSON.parse(xhr.responseText);
+ T(resp._id == "_design/test");
+
+ // it's at 2 urls...
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design%2Ftest");
+ var resp = JSON.parse(xhr.responseText);
+ T(resp._id == "_design/test");
+
+ // ensure that views are addressable
+ resp = db.view("test/testing")
+ T(resp.total_rows == 0)
+
+ // create a ddoc by putting to url with raw slash
+ var xhr = CouchDB.request("PUT", "/test_suite_db/_design/test2",{
+ body : JSON.stringify({
+ _id : "_design/test2",
+ views : {
+ "testing" : {
+ "map" : "function(){emit(1,1)}"
+ }
+ }
+ })
+ });
+
+ // ddoc is getable
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design/test2");
+ var resp = JSON.parse(xhr.responseText);
+ T(resp._id == "_design/test2");
+
+ // it's at 2 urls...
+ var xhr = CouchDB.request("GET", "/test_suite_db/_design%2Ftest2");
+ var resp = JSON.parse(xhr.responseText);
+ T(resp._id == "_design/test2");
+
+ // ensure that views are addressable
+ resp = db.view("test2/testing")
+ T(resp.total_rows == 0)
+ },
+
content_negotiation: function(debug) {
var db = new CouchDB("test_suite_db");
db.deleteDb();
@@ -950,7 +1141,7 @@ var tests = {
}
var designDoc = {
- _id:"_design/test",
+ _id:"_design/test", // turn off couch.js id escaping?
language: "javascript",
views: {
all_docs_twice: {map: "function(doc) { emit(doc.integer, null); emit(doc.integer, null) }"},
diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl
index 119c20ee..16956a86 100644
--- a/src/couchdb/couch_httpd_db.erl
+++ b/src/couchdb/couch_httpd_db.erl
@@ -243,7 +243,7 @@ db_req(#httpd{method='GET',path_parts=[_,<<"_admins">>]}=Req, Db) ->
db_req(#httpd{path_parts=[_,<<"_admins">>]}=Req, _Db) ->
send_method_not_allowed(Req, "PUT,GET");
-db_req(#httpd{method='POST',path_parts=[DbName,<<"_design">>,Name|Rest]}=Req,
+db_req(#httpd{path_parts=[DbName,<<"_design">>,Name|Rest]}=Req,
Db) ->
% Special case to enable using an unencoded in the URL of design docs, as
% slashes in document IDs must otherwise be URL encoded
@@ -252,8 +252,8 @@ db_req(#httpd{method='POST',path_parts=[DbName,<<"_design">>,Name|Rest]}=Req,
db_req(#httpd{path_parts=[_, DocId]}=Req, Db) ->
db_doc_req(Req, Db, DocId);
-db_req(#httpd{path_parts=[_, DocId, FileName]}=Req, Db) ->
- db_attachment_req(Req, Db, DocId, FileName).
+db_req(#httpd{path_parts=[_, DocId | FileNameParts]}=Req, Db) ->
+ db_attachment_req(Req, Db, DocId, FileNameParts).
all_docs_view(Req, Db, Keys) ->
#view_query_args{
@@ -510,7 +510,8 @@ couch_doc_open(Db, DocId, Rev, Options) ->
% Attachment request handlers
-db_attachment_req(#httpd{method='GET'}=Req, Db, DocId, FileName) ->
+db_attachment_req(#httpd{method='GET'}=Req, Db, DocId, FileNameParts) ->
+ FileName = list_to_binary(mochiweb_util:join(lists:map(fun binary_to_list/1, FileNameParts),"/")),
case couch_db:open_doc(Db, DocId, []) of
{ok, #doc{attachments=Attachments}} ->
case proplists:get_value(FileName, Attachments) of
@@ -539,9 +540,10 @@ db_attachment_req(#httpd{method='GET'}=Req, Db, DocId, FileName) ->
throw(Error)
end;
-db_attachment_req(#httpd{method=Method}=Req, Db, DocId, FileName)
- when (Method == 'PUT') or (Method == 'DELETE') ->
+db_attachment_req(#httpd{method=Method}=Req, Db, DocId, FileNameParts)
+ when (Method == 'PUT') or (Method == 'DELETE') ->
+ FileName = list_to_binary(mochiweb_util:join(lists:map(fun binary_to_list/1, FileNameParts),"/")),
NewAttachment = case Method of
'DELETE' ->
[];
@@ -573,7 +575,7 @@ db_attachment_req(#httpd{method=Method}=Req, Db, DocId, FileName)
{rev, UpdatedRev}
]});
-db_attachment_req(Req, _Db, _DocId, _FileName) ->
+db_attachment_req(Req, _Db, _DocId, _FileNameParts) ->
send_method_not_allowed(Req, "DELETE,GET,HEAD,PUT").