You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by rn...@apache.org on 2019/11/25 21:39:36 UTC

[couchdb] 01/01: WIP proxy the response from the other node

This is an automated email from the ASF dual-hosted git repository.

rnewson pushed a commit to branch 1523-bye-bye-5986-rnewson-4
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 936984f0a479375081b0322719a08cf436f43bed
Author: Robert Newson <rn...@apache.org>
AuthorDate: Mon Nov 25 14:03:15 2019 +0000

    WIP proxy the response from the other node
---
 src/chttpd/src/chttpd_node.erl | 19 ++++++++++++++++--
 src/couch/src/couch_httpd.erl  | 44 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 2 deletions(-)

diff --git a/src/chttpd/src/chttpd_node.erl b/src/chttpd/src/chttpd_node.erl
index 9786be5..ea51823 100644
--- a/src/chttpd/src/chttpd_node.erl
+++ b/src/chttpd/src/chttpd_node.erl
@@ -130,17 +130,32 @@ handle_node_req(#httpd{path_parts=[_, Node | PathParts],
     {_, Query, Fragment} = mochiweb_util:urlsplit_path(RawUri),
     NewPath0 = "/" ++ lists:join("/", [?b2l(P) || P <- PathParts]),
     NewRawPath = mochiweb_util:urlunsplit_path({NewPath0, Query, Fragment}),
-    MochiReq = mochiweb_request:new(self(),
+    MaxSize =  config:get_integer("httpd", "max_http_request_size", 4294967296),
+    NewOpts = [{body, MochiReq0:recv_body(MaxSize)} | MochiReq0:get(opts)],
+    Ref = erlang:make_ref(),
+    MochiReq = mochiweb_request:new({remote, self(), Ref},
+                               NewOpts,
                                MochiReq0:get(method),
                                NewRawPath,
                                MochiReq0:get(version),
                                MochiReq0:get(headers)),
-    call_node(Node, couch_httpd, handle_request, [MochiReq]);
+    call_node(Node, couch_httpd, handle_request, [MochiReq]),
+    recv_loop(Ref, MochiReq0:get(socket));
 handle_node_req(#httpd{path_parts=[_]}=Req) ->
     chttpd:send_error(Req, {bad_request, <<"Incomplete path to _node request">>});
 handle_node_req(Req) ->
     chttpd:send_error(Req, not_found).
 
+recv_loop(Ref, Socket) ->
+    receive
+        {Ref, closed} ->
+            ok;
+        {Ref, Data} ->
+            gen_tcp:send(Socket, Data),
+            recv_loop(Ref, Socket)
+    after 5000 ->
+            ok
+    end.
 
 call_node(Node0, Mod, Fun, Args) when is_binary(Node0) ->
     Node1 = try
diff --git a/src/couch/src/couch_httpd.erl b/src/couch/src/couch_httpd.erl
index ba35d6f..43c887e 100644
--- a/src/couch/src/couch_httpd.erl
+++ b/src/couch/src/couch_httpd.erl
@@ -221,8 +221,52 @@ make_fun_spec_strs(SpecStr) ->
     re:split(SpecStr, "(?<=})\\s*,\\s*(?={)", [{return, list}]).
 
 handle_request(MochiReq) ->
+    handle_request(MochiReq:get(socket), MochiReq).
+
+handle_request({remote, RemotePid, RemoteRef}, MochiReq0) ->
+    Body = proplists:get_value(body, MochiReq0:get(opts)),
+    erlang:put(mochiweb_request_body, Body),
+    LoopbackAddress = loopback_address(),
+    ListenOpts = [binary, {packet, 0}, {active, false}, {ifaddr, LoopbackAddress}],
+    {ok, ListenSocket} = gen_tcp:listen(0, ListenOpts),
+    spawn_link(fun() ->
+        {ok, Socket} = gen_tcp:accept(ListenSocket),
+        recv_loop(RemotePid, RemoteRef, Socket) end),
+    {ok, ListenPort} = inet:port(ListenSocket),
+    {ok, LocalSocket} = gen_tcp:connect(LoopbackAddress, ListenPort, []),
+    MochiReq = set_socket(MochiReq0, LocalSocket),
+    try
+        apply(?MODULE, handle_request, [MochiReq | get_httpd_handlers()])
+    after
+        gen_tcp:close(LocalSocket),
+        gen_tcp:close(ListenSocket)
+    end;
+handle_request(_Socket, MochiReq) ->
     apply(?MODULE, handle_request, [MochiReq | get_httpd_handlers()]).
 
+recv_loop(RemotePid, RemoteRef, Socket) ->
+    case gen_tcp:recv(Socket, 0) of
+        {ok, Data} ->
+            RemotePid ! {RemoteRef, Data},
+            recv_loop(RemotePid, RemoteRef, Socket);
+        {error, closed} ->
+            RemotePid ! {RemoteRef, closed}
+    end.
+
+set_socket(MochiReq, NewSocket) ->
+    mochiweb_request:new(
+        NewSocket,
+        MochiReq:get(method),
+        MochiReq:get(raw_path),
+        MochiReq:get(version),
+        MochiReq:get(headers)).
+
+loopback_address() ->
+    {ok, Interfaces} = inet:getifaddrs(),
+    hd([Addr || {_, Opts} <- Interfaces,
+        {addr, Addr} <- Opts,
+        {flags, Flags} <- Opts, lists:member(loopback, Flags)]).
+
 handle_request(MochiReq, DefaultFun, UrlHandlers, DbUrlHandlers,
     DesignUrlHandlers) ->
     %% reset rewrite count for new request