couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cml...@apache.org
Subject svn commit: r643085 - /incubator/couchdb/branches/mochiweb/src/couchdb/couch_httpd.erl
Date Mon, 31 Mar 2008 18:01:16 GMT
Author: cmlenz
Date: Mon Mar 31 11:01:11 2008
New Revision: 643085

URL: http://svn.apache.org/viewvc?rev=643085&view=rev
Log:
mochiweb branch: improve error handling, other fixes/updates.

Modified:
    incubator/couchdb/branches/mochiweb/src/couchdb/couch_httpd.erl

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=643085&r1=643084&r2=643085&view=diff
==============================================================================
--- incubator/couchdb/branches/mochiweb/src/couchdb/couch_httpd.erl (original)
+++ incubator/couchdb/branches/mochiweb/src/couchdb/couch_httpd.erl Mon Mar 31 11:01:11 2008
@@ -38,7 +38,6 @@
     skip = 0
 }).
 
-
 start_link(DocumentRoot) ->
     Loop = fun (Req) -> handle_request(Req, DocumentRoot) end,
     mochiweb_http:start([{loop, Loop} | ?DEFAULTS]).
@@ -57,28 +56,36 @@
     couch_log:debug("Request URI: ~p", [Req:get(raw_path)]),
     couch_log:debug("Headers: ~p", [mochiweb_headers:to_list(Req:get(headers))]),
 
-    Resp = case Path of
+    {ok, Resp} = case catch(handle_request(Req, DocumentRoot, Method, Path)) of
+        {ok, Resp0} ->
+            {ok, Resp0};
+        Error ->
+            send_error(Req, Error)
+    end,
+
+    couch_log:info("~s - - ~p ~B", [
+        Req:get(peer),
+        atom_to_list(Req:get(method)) ++ " " ++ Path,
+        Resp:get(code)
+    ]).
+
+handle_request(Req, DocumentRoot, Method, Path) ->
+    case Path of
         "/" ->
             handle_welcome_request(Req, Method);
         "/_all_dbs" ->
             handle_all_dbs_request(Req, Method);
         "/favicon.ico" ->
-            Req:serve_file("favicon.ico", DocumentRoot);
+            {ok, Req:serve_file("favicon.ico", DocumentRoot)};
         "/_replicate" ->
             handle_replicate_request(Req, Method);
         "/_utils" ->
-            Req:respond({301, [{"Location", "/_utils/"}], <<>>});
+            {ok, Req:respond({301, [{"Location", "/_utils/"}], <<>>})};
         "/_utils/" ++ PathInfo ->
-            Req:serve_file(PathInfo, DocumentRoot);
+            {ok, Req:serve_file(PathInfo, DocumentRoot)};
         _Else ->
             handle_db_request(Req, Method, {Path})
-    end,
-
-    couch_log:info("~s - - ~p ~B", [
-        Req:get(peer),
-        atom_to_list(Req:get(method)) ++ " " ++ Path,
-        Resp:get(code)
-    ]).
+    end.
 
 % Global request handlers
 
@@ -88,19 +95,15 @@
         {"version", couch_server:get_version()}
     ]});
 
-handle_welcome_request(Req, _Method) ->
-    send_error(Req, {method_not_allowed, "GET,HEAD"}).
+handle_welcome_request(_Req, _Method) ->
+    throw({method_not_allowed, "GET,HEAD"}).
 
 handle_all_dbs_request(Req, 'GET') ->
-    case couch_server:all_databases() of
-        {ok, DbNames} ->
-            send_json(Req, list_to_tuple(DbNames));
-        Error ->
-            send_error(Req, Error)
-    end;
+    {ok, DbNames} = couch_server:all_databases(),
+    send_json(Req, list_to_tuple(DbNames));
 
-handle_all_dbs_request(Req, _Method) ->
-    send_error(Req, {method_not_allowed, "GET,HEAD"}).
+handle_all_dbs_request(_Req, _Method) ->
+    throw({method_not_allowed, "GET,HEAD"}).
 
 handle_replicate_request(Req, 'POST') ->
     {obj, Props} = cjson:decode(Req:recv_body()),
@@ -110,8 +113,8 @@
     {ok, JsonResults} = couch_rep:replicate(Source, Target, Options),
     send_json(Req, JsonResults);
 
-handle_replicate_request(Req, _Method) ->
-    send_error(Req, {method_not_allowed, "POST"}).
+handle_replicate_request(_Req, _Method) ->
+    throw({method_not_allowed, "POST"}).
 
 % Database request handlers
 
@@ -126,10 +129,10 @@
             send_json(Req, 201, {obj, [{ok, true}]});
         {error, database_already_exists} ->
             Msg = io_lib:format("Database ~p already exists.", [DbName]),
-            send_error(Req, {database_already_exists, Msg});
+            throw({database_already_exists, Msg});
         Error ->
             Msg = io_lib:format("Error creating database ~p: ~p", [DbName, Error]),
-            send_error(Req, {unknown_error, Msg})
+            throw({unknown_error, Msg})
     end;
 
 handle_db_request(Req, Method, {DbName, Rest}) ->
@@ -137,7 +140,7 @@
         {ok, Db} ->
             handle_db_request(Req, Method, {DbName, Db, Rest});
         Error ->
-            send_error(Req, Error)
+            throw(Error)
     end;
 
 handle_db_request(Req, 'DELETE', {DbName, _Db, []}) ->
@@ -166,8 +169,8 @@
         {rev, NewRev}
     ]});
 
-handle_db_request(Req, _Method, {_DbName, _Db, []}) ->
-    send_error(Req, {method_not_allowed, "DELETE,GET,HEAD,POST"});
+handle_db_request(_Req, _Method, {_DbName, _Db, []}) ->
+    throw({method_not_allowed, "DELETE,GET,HEAD,POST"});
 
 handle_db_request(Req, 'POST', {_DbName, Db, ["_bulk_docs"]}) ->
     Options = [], % put options here.
@@ -198,16 +201,21 @@
                 {obj, [{"id", Doc#doc.id}, {"rev", NewRev}]}
             end,
             Docs, ResultRevs),
-        send_json(Req, 201, [{new_revs, list_to_tuple(DocResults)}]);
+        send_json(Req, 201, {obj, [
+            {ok, true},
+            {new_revs, list_to_tuple(DocResults)}
+        ]});
 
     false ->
         Docs = [couch_doc:from_json_obj(JsonObj) || JsonObj <- tuple_to_list(DocsArray)],
         ok = couch_db:save_docs(Db, Docs, Options),
-        send_json(Req, 201)
+        send_json(Req, 201, {obj, [
+            {ok, true}
+        ]})
     end;
 
-handle_db_request(Req, _Method, {_DbName, _Db, ["_bulk_docs"]}) ->
-    send_error(Req, {method_not_allowed, "POST"});
+handle_db_request(_Req, _Method, {_DbName, _Db, ["_bulk_docs"]}) ->
+    throw({method_not_allowed, "POST"});
 
 % View request handlers
 
@@ -239,11 +247,57 @@
             {Count, SkipCount, undefined, []}),
     finish_view_fold(Req, {ok, TotalRowCount, FoldResult});
 
-handle_db_request(Req, _Method, {_DbName, _Db, ["_all_docs"]}) ->
-    send_error(Req, {method_not_allowed, "GET,HEAD"});
+handle_db_request(_Req, _Method, {_DbName, _Db, ["_all_docs"]}) ->
+    throw({method_not_allowed, "GET,HEAD"});
 
-handle_db_request(Req, 'GET', {DbName, Db, ["_all_docs_by_update_seq"]}) ->
-    send_error(Req, not_implemented); % TODO
+handle_db_request(Req, 'GET', {_DbName, Db, ["_all_docs_by_update_seq"]}) ->
+    #view_query_args{
+        start_key = StartKey,
+        count = Count,
+        skip = SkipCount,
+        direction = Dir
+    } = QueryArgs = parse_view_query(Req),
+
+    {ok, Info} = couch_db:get_db_info(Db),
+    TotalRowCount = proplists:get_value(doc_count, Info),
+
+    FoldlFun = make_view_fold_fun(Req, QueryArgs),
+    StartKey2 = case StartKey of
+        nil -> 0;
+        <<>> -> 100000000000;
+        StartKey when is_integer(StartKey) -> StartKey
+    end,
+    {ok, FoldResult} = couch_db:enum_docs_since(Db, StartKey2, Dir,
+        fun(DocInfo, Offset, Acc) ->
+            #doc_info{
+                id=Id,
+                rev=Rev,
+                update_seq=UpdateSeq,
+                deleted=Deleted,
+                conflict_revs=ConflictRevs,
+                deleted_conflict_revs=DelConflictRevs
+            } = DocInfo,
+            Json = {obj,
+                [{"rev", Rev}] ++
+                case ConflictRevs of
+                    []  ->  [];
+                    _   ->  [{"conflicts", list_to_tuple(ConflictRevs)}]
+                end ++
+                case DelConflictRevs of
+                    []  ->  [];
+                    _   ->  [{"deleted_conflicts", list_to_tuple(DelConflictRevs)}]
+                end ++
+                case Deleted of
+                    true -> [{"deleted", true}];
+                    false -> []
+                end
+            },
+            FoldlFun(Id, UpdateSeq, Json, Offset, TotalRowCount, Acc)
+        end, {Count, SkipCount, undefined, []}),
+    finish_view_fold(Req, {ok, TotalRowCount, FoldResult});
+
+handle_db_request(_Req, _Method, {_DbName, _Db, ["_all_docs_by_update_seq"]}) ->
+    throw({method_not_allowed, "GET,HEAD"});
 
 handle_db_request(Req, 'GET', {DbName, _Db, ["_view", DocId, ViewName]}) ->
     #view_query_args{
@@ -260,8 +314,8 @@
     FoldResult = couch_view:fold(View, Start, Dir, FoldlFun, FoldAccInit),
     finish_view_fold(Req, FoldResult);
 
-handle_db_request(Req, _Method, {_DbName, _Db, ["_view", _DocId, _ViewName]}) ->
-    send_error(Req, method_not_allowed, "GET,HEAD");
+handle_db_request(_Req, _Method, {_DbName, _Db, ["_view", _DocId, _ViewName]}) ->
+    throw({method_not_allowed, "GET,HEAD"});
 
 handle_db_request(Req, 'GET', {_DbName, Db, ["_missing_revs"]}) ->
     {obj, JsonDocIdRevs} = cjson:decode(Req:recv_body()),
@@ -295,8 +349,8 @@
     FoldResult = couch_view:fold(View, Start, Dir, FoldlFun, FoldAccInit),
     finish_view_fold(Req, FoldResult);
 
-handle_db_request(Req, _Method, {_DbName, _Db, ["_temp_view"]}) ->
-    send_error(Req, {method_not_allowed, "POST"});
+handle_db_request(_Req, _Method, {_DbName, _Db, ["_temp_view"]}) ->
+    throw({method_not_allowed, "POST"});
 
 % Document request handlers
 
@@ -306,11 +360,14 @@
 handle_doc_request(Req, 'DELETE', _DbName, Db, [DocId]) ->
     % TODO: Etag handling
     RevToDelete = proplists:get_value("rev", Req:parse_qs()),
+    couch_log:debug("Processing delete doc ~p, rev ~p", [DocId, RevToDelete]),
     {ok, NewRev} = couch_db:delete_doc(Db, DocId, [RevToDelete]),
-    send_json(Req, 202, [
-        {"id", DocId},
-        {"rev", NewRev}
-    ]);
+    couch_log:debug("Doc ~p deleted, newrev = ~p", [DocId, NewRev]),
+    send_json(Req, 202, {obj, [
+        {ok, true},
+        {id, DocId},
+        {rev, NewRev}
+    ]});
 
 handle_doc_request(Req, 'GET', _DbName, Db, [DocId]) ->
     #doc_query_args{
@@ -333,16 +390,16 @@
                     send_json(Req, 200, JsonDoc)
                 end;
             Error ->
-                send_error(Req, Error)
+                throw(Error)
             end;
         _ ->
             % 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}],
-                                           couch_doc:to_json_obj(Doc, Options));
+                          couch_doc:to_json_obj(Doc, Options));
             {ok, [Else]} ->
-                send_error(Req, Else)
+                throw(Else)
             end
         end;
     _ ->
@@ -372,17 +429,22 @@
 
 handle_doc_request(Req, 'PUT', _DbName, Db, [DocId]) ->
     % TODO: Etag handling
-    Json = cjson:decode(Req:recv_body()),
+    {obj, ObjProps} = Json = cjson:decode(Req:recv_body()),
     Doc = couch_doc:from_json_obj(Json),
-    {ok, NewRev} = couch_db:update_doc(Db, Doc#doc{id=DocId, revs=[]}, []),
+    DocRev = proplists:get_value("_rev", ObjProps, ""),
+    Revs = if DocRev /= "" -> [DocRev];
+        true -> []
+    end,
+
+    {ok, NewRev} = couch_db:update_doc(Db, Doc#doc{id=DocId, revs=Revs}, []),
     send_json(Req, 201, {obj, [
         {ok, true},
         {id, DocId},
         {rev, NewRev}
     ]});
 
-handle_doc_request(Req, _Method, _DbName, _Db, [_DocId]) ->
-    send_error(Req, {method_not_allowed, "DELETE,GET,HEAD,PUT"}).
+handle_doc_request(_Req, _Method, _DbName, _Db, [_DocId]) ->
+    throw({method_not_allowed, "DELETE,GET,HEAD,PUT"}).
 
 % View request handling internals
 
@@ -606,11 +668,9 @@
 error_to_json0({not_found, Reason}) ->
     {404, not_found, Reason};
 error_to_json0({database_already_exists, Reason}) ->
-    {409, database_already_exists, Reason}; % 409, conflict error
+    {409, database_already_exists, Reason};
 error_to_json0(conflict) ->
-    {412, conflict, conflict}; % 412, conflict error
-error_to_json0({conflict, WinnerRev}) ->
-    {412, conflict, WinnerRev}; % 412, conflict error
+    {412, conflict, "Update conflict"};
 error_to_json0({doc_validation, Msg}) ->
     {406, doc_validation, Msg};
 error_to_json0({Id, Reason}) when is_atom(Id) ->
@@ -621,12 +681,13 @@
 send_error(Req, {method_not_allowed, Methods}) ->
     Req:respond({405, [{"Allow", Methods}], ""});
 send_error(Req, Error) ->
+    couch_log:info("Error: ~p", [Error]),
     {Code, Json} = error_to_json(Error),
     send_error(Req, Code, Json).
 
-send_error(Req, Code, {Id, Reason}) ->
-    couch_log:info("HTTP Error (code ~w): ~p", [Code,  {Id, Reason}]),
-    send_json(Req, Code, {obj, [{Id, Reason}]}).
+send_error(Req, Code, Json) ->
+    couch_log:info("HTTP Error (code ~w): ~p", [Code, Json]),
+    send_json(Req, Code, Json).
 
 send_json(Req, Value) ->
     send_json(Req, 200, Value).
@@ -638,7 +699,7 @@
     Resp = start_json_response(Req, Code, Headers),
     Resp:write_chunk(cjson:encode(Value)),
     end_json_response(Resp),
-    Resp.
+    {ok, Resp}.
 
 start_json_response(Req, Code) ->
     start_json_response(Req, Code, []).
@@ -656,4 +717,4 @@
 
 end_json_response(Resp) ->
     Resp:write_chunk(""),
-    Resp.
+    {ok, Resp}.



Mime
View raw message