You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2014/12/04 21:09:41 UTC

[10/22] couch commit: updated refs/heads/2491-refactor-couch-httpd-auth to 3e8286d

Refactor code for sanity and so the view changes handler can reuse code better


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/da2836b6
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/da2836b6
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/da2836b6

Branch: refs/heads/2491-refactor-couch-httpd-auth
Commit: da2836b6afa4fcb96a18bff5f32f5ac3d6376de8
Parents: 20cd47e
Author: Benjamin Bastian <be...@gmail.com>
Authored: Thu Aug 28 00:00:50 2014 +0700
Committer: Benjamin Bastian <be...@gmail.com>
Committed: Fri Oct 31 12:43:53 2014 -0700

----------------------------------------------------------------------
 src/couch_httpd_changes.erl | 259 ---------------------------------------
 src/couch_httpd_db.erl      |  54 ++++----
 2 files changed, 29 insertions(+), 284 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/da2836b6/src/couch_httpd_changes.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_changes.erl b/src/couch_httpd_changes.erl
deleted file mode 100644
index 4963a5f..0000000
--- a/src/couch_httpd_changes.erl
+++ /dev/null
@@ -1,259 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_httpd_changes).
-
--export([handle_db_changes_req/2,
-         handle_changes_req/4,
-         parse_changes_query/2]).
-
--include_lib("couch/include/couch_db.hrl").
-
-handle_db_changes_req(Req, Db) ->
-    ChangesArgs = parse_changes_query(Req, Db),
-    ChangesFun = couch_changes:handle_db_changes(ChangesArgs, Req, Db),
-    handle_changes_req(Req, Db, ChangesArgs, ChangesFun).
-
-handle_changes_req(#httpd{method='POST'}=Req, Db, ChangesArgs, ChangesFun) ->
-    couch_httpd:validate_ctype(Req, "application/json"),
-    handle_changes_req1(Req, Db, ChangesArgs, ChangesFun);
-handle_changes_req(#httpd{method='GET'}=Req, Db, ChangesArgs, ChangesFun) ->
-    handle_changes_req1(Req, Db, ChangesArgs, ChangesFun);
-handle_changes_req(#httpd{}=Req, _Db, _ChangesArgs, _ChangesFun) ->
-    couch_httpd:send_method_not_allowed(Req, "GET,HEAD,POST").
-
-handle_changes_req1(Req, #db{name=DbName}=Db, ChangesArgs, ChangesFun) ->
-    AuthDbName = ?l2b(config:get("couch_httpd_auth", "authentication_db")),
-    case AuthDbName of
-    DbName ->
-        % in the authentication database, _changes is admin-only.
-        ok = couch_db:check_is_admin(Db);
-    _Else ->
-        % on other databases, _changes is free for all.
-        ok
-    end,
-
-    MakeCallback = fun(Resp) ->
-        fun({change, {ChangeProp}=Change, _}, "eventsource") ->
-            Seq = proplists:get_value(<<"seq">>, ChangeProp),
-            couch_httpd:send_chunk(Resp, ["data: ", ?JSON_ENCODE(Change),
-                              "\n", "id: ", ?JSON_ENCODE(Seq),
-                              "\n\n"]);
-        ({change, Change, _}, "continuous") ->
-            couch_httpd:send_chunk(Resp, [?JSON_ENCODE(Change) | "\n"]);
-        ({change, Change, Prepend}, _) ->
-            couch_httpd:send_chunk(Resp, [Prepend, ?JSON_ENCODE(Change)]);
-        (start, "eventsource") ->
-            ok;
-        (start, "continuous") ->
-            ok;
-        (start, _) ->
-            couch_httpd:send_chunk(Resp, "{\"results\":[\n");
-        ({stop, _EndSeq}, "eventsource") ->
-            couch_httpd:end_json_response(Resp);
-        ({stop, EndSeq}, "continuous") ->
-            couch_httpd:send_chunk(
-                Resp,
-                [?JSON_ENCODE({[{<<"last_seq">>, EndSeq}]}) | "\n"]
-            ),
-            couch_httpd:end_json_response(Resp);
-        ({stop, EndSeq}, _) ->
-            couch_httpd:send_chunk(
-                Resp,
-                io_lib:format("\n],\n\"last_seq\":~w}\n", [EndSeq])
-            ),
-            couch_httpd:end_json_response(Resp);
-        (timeout, _) ->
-            couch_httpd:send_chunk(Resp, "\n")
-        end
-    end,
-    WrapperFun = case ChangesArgs#changes_args.feed of
-    "normal" ->
-        {ok, Info} = couch_db:get_db_info(Db),
-        CurrentEtag = couch_httpd:make_etag(Info),
-        fun(FeedChangesFun) ->
-            couch_httpd:etag_respond(
-                Req,
-                CurrentEtag,
-                fun() ->
-                    {ok, Resp} = couch_httpd:start_json_response(
-                         Req, 200, [{"ETag", CurrentEtag}]
-                    ),
-                    FeedChangesFun(MakeCallback(Resp))
-                end
-            )
-        end;
-    "eventsource" ->
-        Headers = [
-            {"Content-Type", "text/event-stream"},
-            {"Cache-Control", "no-cache"}
-        ],
-        {ok, Resp} = couch_httpd:start_chunked_response(Req, 200, Headers),
-        fun(FeedChangesFun) ->
-            FeedChangesFun(MakeCallback(Resp))
-        end;
-    _ ->
-        % "longpoll" or "continuous"
-        {ok, Resp} = couch_httpd:start_json_response(Req, 200),
-        fun(FeedChangesFun) ->
-            FeedChangesFun(MakeCallback(Resp))
-        end
-    end,
-    couch_stats_collector:increment(
-        {httpd, clients_requesting_changes}
-    ),
-    try
-        WrapperFun(ChangesFun)
-    after
-    couch_stats_collector:decrement(
-        {httpd, clients_requesting_changes}
-    )
-    end.
-
-
-parse_changes_query(Req, Db) ->
-    ChangesArgs = lists:foldl(fun({Key, Value}, Args) ->
-        case {string:to_lower(Key), Value} of
-        {"feed", _} ->
-            Args#changes_args{feed=Value};
-        {"descending", "true"} ->
-            Args#changes_args{dir=rev};
-        {"since", "now"} ->
-            UpdateSeq = couch_util:with_db(Db#db.name, fun(WDb) ->
-                                        couch_db:get_update_seq(WDb)
-                                end),
-            Args#changes_args{since=UpdateSeq};
-        {"since", _} ->
-            Args#changes_args{since=list_to_integer(Value)};
-        {"last-event-id", _} ->
-            Args#changes_args{since=list_to_integer(Value)};
-        {"limit", _} ->
-            Args#changes_args{limit=list_to_integer(Value)};
-        {"style", _} ->
-            Args#changes_args{style=list_to_existing_atom(Value)};
-        {"heartbeat", "true"} ->
-            Args#changes_args{heartbeat=true};
-        {"heartbeat", _} ->
-            Args#changes_args{heartbeat=list_to_integer(Value)};
-        {"timeout", _} ->
-            Args#changes_args{timeout=list_to_integer(Value)};
-        {"include_docs", "true"} ->
-            Args#changes_args{include_docs=true};
-        {"attachments", "true"} ->
-            Opts = Args#changes_args.doc_options,
-            Args#changes_args{doc_options=[attachments|Opts]};
-        {"att_encoding_info", "true"} ->
-            Opts = Args#changes_args.doc_options,
-            Args#changes_args{doc_options=[att_encoding_info|Opts]};
-        {"conflicts", "true"} ->
-            Args#changes_args{conflicts=true};
-        {"filter", _} ->
-            Args#changes_args{filter=Value};
-        _Else -> % unknown key value pair, ignore.
-            Args
-        end
-    end, #changes_args{}, couch_httpd:qs(Req)),
-    %% if it's an EventSource request with a Last-event-ID header
-    %% that should override the `since` query string, since it's
-    %% probably the browser reconnecting.
-    case ChangesArgs#changes_args.feed of
-        "eventsource" ->
-            case couch_httpd:header_value(Req, "last-event-id") of
-                undefined ->
-                    ChangesArgs;
-                Value ->
-                    ChangesArgs#changes_args{since=list_to_integer(Value)}
-            end;
-        _ ->
-            ChangesArgs
-    end.
-
-parse_view_param({json_req, {Props}}) ->
-    {Query} = couch_util:get_value(<<"query">>, Props),
-    parse_view_param1(couch_util:get_value(<<"view">>, Query, <<"">>));
-parse_view_param(Req) ->
-    parse_view_param1(list_to_binary(couch_httpd:qs_value(Req, "view", ""))).
-
-parse_view_param1(ViewParam) ->
-    case re:split(ViewParam, <<"/">>) of
-        [DName, ViewName] ->
-            {<< "_design/", DName/binary >>, ViewName};
-        _ ->
-            throw({bad_request, "Invalid `view` parameter."})
-    end.
-
-parse_view_options([], Acc) ->
-    Acc;
-parse_view_options([{K, V} | Rest], Acc) ->
-    Acc1 = case couch_util:to_binary(K) of
-        <<"reduce">> ->
-            [{reduce, couch_mrview_http:parse_boolean(V)}];
-        <<"key">> ->
-            V1 = parse_json(V),
-            [{start_key, V1}, {end_key, V1} | Acc];
-        <<"keys">> ->
-            [{keys, parse_json(V)} | Acc];
-        <<"startkey">> ->
-            [{start_key, parse_json(V)} | Acc];
-        <<"start_key">> ->
-            [{start_key, parse_json(V)} | Acc];
-        <<"startkey_docid">> ->
-            [{start_key_docid, couch_util:to_binary(V)} | Acc];
-        <<"start_key_docid">> ->
-            [{start_key_docid, couch_util:to_binary(V)} | Acc];
-        <<"endkey">> ->
-            [{end_key, parse_json(V)} | Acc];
-        <<"end_key">> ->
-            [{end_key, parse_json(V)} | Acc];
-        <<"endkey_docid">> ->
-            [{start_key_docid, couch_util:to_binary(V)} | Acc];
-        <<"end_key_docid">> ->
-            [{start_key_docid, couch_util:to_binary(V)} | Acc];
-        <<"limit">> ->
-            [{limit, couch_mrview_http:parse_pos_int(V)} | Acc];
-        <<"count">> ->
-            throw({query_parse_error, <<"QS param `count` is not `limit`">>});
-        <<"stale">> when V =:= <<"ok">> orelse V =:= "ok" ->
-            [{stale, ok} | Acc];
-        <<"stale">> when V =:= <<"update_after">> orelse V =:= "update_after" ->
-            [{stale, update_after} | Acc];
-        <<"stale">> ->
-            throw({query_parse_error, <<"Invalid value for `stale`.">>});
-        <<"descending">> ->
-            case couch_mrview_http:parse_boolean(V) of
-                true ->
-                    [{direction, rev} | Acc];
-                _ ->
-                    [{direction, fwd} | Acc]
-            end;
-        <<"skip">> ->
-            [{skip, couch_mrview_http:parse_pos_int(V)} | Acc];
-        <<"group">> ->
-            case couch_mrview_http:parse_booolean(V) of
-                true ->
-                    [{group_level, exact} | Acc];
-                _ ->
-                    [{group_level, 0} | Acc]
-            end;
-        <<"group_level">> ->
-            [{group_level, couch_mrview_http:parse_pos_int(V)} | Acc];
-        <<"inclusive_end">> ->
-            [{inclusive_end, couch_mrview_http:parse_boolean(V)}];
-        _ ->
-            Acc
-    end,
-    parse_view_options(Rest, Acc1).
-
-parse_json(V) when is_list(V) ->
-    ?JSON_DECODE(V);
-parse_json(V) ->
-    V.

http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/da2836b6/src/couch_httpd_db.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_db.erl b/src/couch_httpd_db.erl
index 442e872..34f04f2 100644
--- a/src/couch_httpd_db.erl
+++ b/src/couch_httpd_db.erl
@@ -14,9 +14,10 @@
 -include_lib("couch/include/couch_db.hrl").
 
 -export([handle_request/1, handle_compact_req/2, handle_design_req/2,
-    db_req/2, couch_doc_open/4,handle_changes_req/2,
+    db_req/2, couch_doc_open/4, handle_db_changes_req/2,
     update_doc_result_to_json/1, update_doc_result_to_json/2,
-    handle_design_info_req/3, parse_copy_destination_header/1]).
+    handle_design_info_req/3, parse_copy_destination_header/1,
+    parse_changes_query/2, handle_changes_req/4]).
 
 -import(couch_httpd,
     [send_json/2,send_json/3,send_json/4,send_method_not_allowed/2,
@@ -54,15 +55,22 @@ handle_request(#httpd{path_parts=[DbName|RestParts],method=Method,
         do_db_req(Req, Handler)
     end.
 
-handle_changes_req(#httpd{method='POST'}=Req, Db) ->
+
+handle_db_changes_req(Req, Db) ->
+    ChangesArgs = parse_changes_query(Req, Db),
+    ChangesFun = couch_changes:handle_db_changes(ChangesArgs, Req, Db),
+    handle_changes_req(Req, Db, ChangesArgs, ChangesFun).
+
+
+handle_changes_req(#httpd{method='POST'}=Req, Db, ChangesArgs, ChangesFun) ->
     couch_httpd:validate_ctype(Req, "application/json"),
-    handle_changes_req1(Req, Db);
-handle_changes_req(#httpd{method='GET'}=Req, Db) ->
-    handle_changes_req1(Req, Db);
-handle_changes_req(#httpd{path_parts=[_,<<"_changes">>]}=Req, _Db) ->
-    send_method_not_allowed(Req, "GET,HEAD,POST").
+    handle_changes_req1(Req, Db, ChangesArgs, ChangesFun);
+handle_changes_req(#httpd{method='GET'}=Req, Db, ChangesArgs, ChangesFun) ->
+    handle_changes_req1(Req, Db, ChangesArgs, ChangesFun);
+handle_changes_req(#httpd{}=Req, _Db, _ChangesArgs, _ChangesFun) ->
+    couch_httpd:send_method_not_allowed(Req, "GET,HEAD,POST").
 
-handle_changes_req1(Req, #db{name=DbName}=Db) ->
+handle_changes_req1(Req, #db{name=DbName}=Db, ChangesArgs, ChangesFun) ->
     AuthDbName = ?l2b(config:get("couch_httpd_auth", "authentication_db")),
     case AuthDbName of
     DbName ->
@@ -72,47 +80,41 @@ handle_changes_req1(Req, #db{name=DbName}=Db) ->
         % on other databases, _changes is free for all.
         ok
     end,
-    handle_changes_req2(Req, Db).
 
-handle_changes_req2(Req, Db) ->
     MakeCallback = fun(Resp) ->
         fun({change, {ChangeProp}=Change, _}, "eventsource") ->
             Seq = proplists:get_value(<<"seq">>, ChangeProp),
-            send_chunk(Resp, ["data: ", ?JSON_ENCODE(Change),
+            couch_httpd:send_chunk(Resp, ["data: ", ?JSON_ENCODE(Change),
                               "\n", "id: ", ?JSON_ENCODE(Seq),
                               "\n\n"]);
         ({change, Change, _}, "continuous") ->
-            send_chunk(Resp, [?JSON_ENCODE(Change) | "\n"]);
+            couch_httpd:send_chunk(Resp, [?JSON_ENCODE(Change) | "\n"]);
         ({change, Change, Prepend}, _) ->
-            send_chunk(Resp, [Prepend, ?JSON_ENCODE(Change)]);
+            couch_httpd:send_chunk(Resp, [Prepend, ?JSON_ENCODE(Change)]);
         (start, "eventsource") ->
             ok;
         (start, "continuous") ->
             ok;
         (start, _) ->
-            send_chunk(Resp, "{\"results\":[\n");
+            couch_httpd:send_chunk(Resp, "{\"results\":[\n");
         ({stop, _EndSeq}, "eventsource") ->
-            end_json_response(Resp);
+            couch_httpd:end_json_response(Resp);
         ({stop, EndSeq}, "continuous") ->
-            send_chunk(
+            couch_httpd:send_chunk(
                 Resp,
                 [?JSON_ENCODE({[{<<"last_seq">>, EndSeq}]}) | "\n"]
             ),
-            end_json_response(Resp);
+            couch_httpd:end_json_response(Resp);
         ({stop, EndSeq}, _) ->
-            send_chunk(
+            couch_httpd:send_chunk(
                 Resp,
                 io_lib:format("\n],\n\"last_seq\":~w}\n", [EndSeq])
             ),
-            end_json_response(Resp);
-        (timeout, "eventsource") ->
-            send_chunk(Resp, "event: heartbeat\ndata: \n\n");
+            couch_httpd:end_json_response(Resp);
         (timeout, _) ->
-            send_chunk(Resp, "\n")
+            couch_httpd:send_chunk(Resp, "\n")
         end
     end,
-    ChangesArgs = parse_changes_query(Req, Db),
-    ChangesFun = couch_changes:handle_db_changes(ChangesArgs, Req, Db),
     WrapperFun = case ChangesArgs#changes_args.feed of
     "normal" ->
         {ok, Info} = couch_db:get_db_info(Db),
@@ -154,6 +156,8 @@ handle_changes_req2(Req, Db) ->
             [couchdb, httpd, clients_requesting_changes])
     end.
 
+
+
 handle_compact_req(#httpd{method='POST'}=Req, Db) ->
     case Req#httpd.path_parts of
         [_DbName, <<"_compact">>] ->