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/02/13 04:10:06 UTC

[10/14] couch-httpd commit: updated refs/heads/1994-merge-rcouch to d936233

extract couch_httpd changes API in its own module


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

Branch: refs/heads/1994-merge-rcouch
Commit: c629342a16925d0f9b9094016f4e91fecb39a774
Parents: dc841e0
Author: benoitc <bc...@gmail.com>
Authored: Sun Feb 2 19:54:01 2014 +0100
Committer: Paul J. Davis <pa...@gmail.com>
Committed: Wed Feb 12 21:05:52 2014 -0600

----------------------------------------------------------------------
 src/couch_httpd_changes.erl | 174 +++++++++++++++++++++++++++++++++++++++
 src/couch_httpd_db.erl      |   8 +-
 2 files changed, 178 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch-httpd/blob/c629342a/src/couch_httpd_changes.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_changes.erl b/src/couch_httpd_changes.erl
new file mode 100644
index 0000000..1e431e9
--- /dev/null
+++ b/src/couch_httpd_changes.erl
@@ -0,0 +1,174 @@
+% 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_changes_req/2]).
+
+-include_lib("couch/include/couch_db.hrl").
+
+handle_changes_req(#httpd{method='POST'}=Req, Db) ->
+    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) ->
+    couch_httpd:send_method_not_allowed(Req, "GET,HEAD,POST").
+
+handle_changes_req1(Req, #db{name=DbName}=Db) ->
+    AuthDbName = ?l2b(couch_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,
+    handle_changes_req2(Req, Db).
+
+handle_changes_req2(Req, Db) ->
+    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,
+    ChangesArgs = parse_changes_query(Req, Db),
+    ChangesFun = couch_changes:handle_changes(ChangesArgs, Req, Db),
+    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.

http://git-wip-us.apache.org/repos/asf/couchdb-couch-httpd/blob/c629342a/src/couch_httpd_db.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_db.erl b/src/couch_httpd_db.erl
index 45a6dd5..0d1e0f8 100644
--- a/src/couch_httpd_db.erl
+++ b/src/couch_httpd_db.erl
@@ -19,10 +19,10 @@
     handle_design_info_req/3]).
 
 -import(couch_httpd,
-    [send_json/2,send_json/3,send_json/4,send_method_not_allowed/2,
-    start_json_response/2,send_chunk/2,last_chunk/1,end_json_response/1,
-    start_chunked_response/3, absolute_uri/2, send/2,
-    start_response_length/4, send_error/4]).
+        [send_json/2,send_json/3,send_json/4,send_method_not_allowed/2,
+         start_json_response/2,send_chunk/2,last_chunk/1,end_json_response/1,
+         start_chunked_response/3, absolute_uri/2, send/2,
+         start_response_length/4, send_error/4]).
 
 -record(doc_query_args, {
     options = [],