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]),