You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by cm...@apache.org on 2008/04/07 02:04:54 UTC

svn commit: r645334 - in /incubator/couchdb/branches/mochiweb: share/www/style/layout.css src/couchdb/couch_httpd.erl

Author: cmlenz
Date: Sun Apr  6 17:04:52 2008
New Revision: 645334

URL: http://svn.apache.org/viewvc?rev=645334&view=rev
Log:
mochiweb branch: add proper Etag support for documents.

Modified:
    incubator/couchdb/branches/mochiweb/share/www/style/layout.css
    incubator/couchdb/branches/mochiweb/src/couchdb/couch_httpd.erl

Modified: incubator/couchdb/branches/mochiweb/share/www/style/layout.css
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/mochiweb/share/www/style/layout.css?rev=645334&r1=645333&r2=645334&view=diff
==============================================================================
--- incubator/couchdb/branches/mochiweb/share/www/style/layout.css (original)
+++ incubator/couchdb/branches/mochiweb/share/www/style/layout.css Sun Apr  6 17:04:52 2008
@@ -368,7 +368,7 @@
 #tests tbody.content td.status { background-position: 5px 8px;
   background-repeat: no-repeat; color: #999; padding-left: 20px;
 }
-#tests tbody.content td.details { width: 50%; overflow: auto; }
+#tests tbody.content td.details { width: 50%; }
 #tests tbody.content td.details a { border-bottom: 1px dashed #ccc;
   color: #999; float: right; font-size: 85%;
 }

Modified: incubator/couchdb/branches/mochiweb/src/couchdb/couch_httpd.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/branches/mochiweb/src/couchdb/couch_httpd.erl?rev=645334&r1=645333&r2=645334&view=diff
==============================================================================
--- incubator/couchdb/branches/mochiweb/src/couchdb/couch_httpd.erl (original)
+++ incubator/couchdb/branches/mochiweb/src/couchdb/couch_httpd.erl Sun Apr  6 17:04:52 2008
@@ -378,8 +378,26 @@
                               UnquotedFileName).
 
 handle_doc_request(Req, 'DELETE', _DbName, Db, DocId) ->
-    % TODO: Etag handling
-    RevToDelete = proplists:get_value("rev", Req:parse_qs()),
+    QueryRev = proplists:get_value("rev", Req:parse_qs()),
+    Etag = case Req:get_header_value("If-Match") of
+        undefined ->
+            undefined;
+        Tag ->
+            string:strip(Tag, both, $")
+    end,
+    RevToDelete = case {QueryRev, Etag} of
+    {undefined, undefined} ->
+        throw({missing_rev, "Document rev/etag must be specified to delete"});
+    {_, undefined} ->
+        QueryRev;
+    {undefined, _} ->
+        Etag;
+    _ when QueryRev == Etag ->
+        Etag;
+    _ ->
+        throw({bad_request, "Document rev and etag have different values"})
+    end,
+
     {ok, NewRev} = couch_db:delete_doc(Db, DocId, [RevToDelete]),
     send_json(Req, 202, {obj, [
         {ok, true},
@@ -400,13 +418,9 @@
             % open most recent rev
             case couch_db:open_doc(Db, DocId, Options) of
             {ok, #doc{revs=[DocRev|_]}=Doc} ->
-                Etag = Req:get_header_value("if-none-match"),
-                if Options == [] andalso Etag == DocRev ->
-                    Req:respond({304, [{"etag", DocRev}], ""});
-                true ->
-                    JsonDoc = couch_doc:to_json_obj(Doc, Options),
-                    send_json(Req, 200, JsonDoc)
-                end;
+                Etag = none_match(Req, DocRev),
+                JsonDoc = couch_doc:to_json_obj(Doc, Options),
+                send_json(Req, 200, [{"Etag", Etag}], JsonDoc);
             Error ->
                 throw(Error)
             end;
@@ -414,7 +428,7 @@
             % open a specific rev (deletions come back as stubs)
             case couch_db:open_doc_revs(Db, DocId, [Rev], Options) of
             {ok, [{ok, Doc}]} ->
-                send_json(Req, 200, [{"etag", Rev}],
+                send_json(Req, 200, [{"Etag", "\"" ++ Rev ++ "\""}],
                           couch_doc:to_json_obj(Doc, Options));
             {ok, [Else]} ->
                 throw(Else)
@@ -446,15 +460,31 @@
     end;
 
 handle_doc_request(Req, 'PUT', _DbName, Db, DocId) ->
-    % TODO: Etag handling
     Json = {obj, DocProps} = cjson:decode(Req:recv_body()),
-    Doc = couch_doc:from_json_obj(Json),
-    DocRev = proplists:get_value("_rev", DocProps, ""),
-    Revs = if DocRev /= "" -> [DocRev];
-        true -> []
+    DocRev = proplists:get_value("_rev", DocProps),
+    Etag = case Req:get_header_value("If-Match") of
+        undefined ->
+            undefined;
+        Tag ->
+            string:strip(Tag, both, $")
     end,
+    Revs = case {DocRev, Etag} of
+    {undefined, undefined} ->
+        [];
+    {_, undefined} ->
+        [DocRev];
+    {undefined, _} ->
+        [string:strip(Etag, both, $")];
+    _ when DocRev == Etag ->
+        [string:strip(Etag, both, $")];
+    _ ->
+        throw({bad_request, "Document rev and etag have different values"})
+    end,
+
+    Doc = couch_doc:from_json_obj(Json),
+
     {ok, NewRev} = couch_db:update_doc(Db, Doc#doc{id=DocId, revs=Revs}, []),
-    send_json(Req, 201, {obj, [
+    send_json(Req, 201, [{"Etag", "\"" ++ NewRev ++ "\""}], {obj, [
         {ok, true},
         {id, DocId},
         {rev, NewRev}
@@ -691,6 +721,21 @@
 
 % Utilities
 
+none_match(Req, Tag) ->
+    Etag = "\"" ++ Tag ++ "\"",
+    Etags = case Req:get_header_value("If-None-Match") of
+        undefined ->
+            [];
+        Tags ->
+            string:tokens(Tags, ", ")
+    end,
+    case lists:member(Etag, Etags) of
+        true ->
+            throw({not_modified, Etag});
+        false ->
+            Etag
+    end.
+
 error_to_json(Error) ->
     {HttpCode, Atom, Reason} = error_to_json0(Error),
     FormattedReason =
@@ -706,6 +751,10 @@
     ]},
     {HttpCode, Json}.
 
+error_to_json0(bad_request) ->
+    {400, bad_request, "Bad request"};
+error_to_json0({bad_request, Reason}) ->
+    {400, bad_request, Reason};
 error_to_json0(not_found) ->
     {404, not_found, "missing"};
 error_to_json0({missing_rev, Msg}) ->
@@ -724,7 +773,11 @@
     {500, error, Error}.
 
 send_error(Req, {method_not_allowed, Methods}) ->
-    Req:respond({405, [{"Allow", Methods}], ""});
+    {ok, Req:respond({405, [{"Allow", Methods}], <<>>})};
+send_error(Req, {modified, Etag}) ->
+    {ok, Req:respond({412, [{"Etag", Etag}], <<>>})};
+send_error(Req, {not_modified, Etag}) ->
+    {ok, Req:respond({304, [{"Etag", Etag}], <<>>})};
 send_error(Req, Error) ->
     {Code, Json} = error_to_json(Error),
     couch_log:info("HTTP Error (code ~w): ~p", [Code, Error]),