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

vatamane pushed a commit to branch 3.3.x
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit c7ea3b88a1925153609a4cbed9d498d80f26f553
Author: Nick Vatamaniuc <vatam...@gmail.com>
AuthorDate: Thu Aug 31 15:40:20 2023 -0400

    Update mochiweb to 3.2.0
    
    Mochiweb [3.2.0](https://github.com/mochi/mochiweb/releases/tag/v3.2.0) has 
a
    `mochiweb_request:is_closed/1` function which we can use instead of our 
socket
    check in `chttpd_util`.
    
    Since `mochiweb_request:is_closed/1` takes a request object, switch the 
client
    request monitoring code and tests to use requests insteads of sockets. That
    involves doing a bunch of renames mostly which makes the PR a bit long, but
    otherwise, everything should work as before.
---
 rebar.config.script                          |   2 +-
 src/chttpd/src/chttpd.erl                    |   4 +-
 src/chttpd/src/chttpd_util.erl               | 120 +++++----------------------
 src/chttpd/test/eunit/chttpd_util_test.erl   |  33 ++++----
 src/fabric/src/fabric_db_update_listener.erl |  16 ++--
 src/fabric/src/fabric_streams.erl            |  36 +++++---
 src/fabric/src/fabric_view_changes.erl       |   4 +-
 7 files changed, 74 insertions(+), 141 deletions(-)

diff --git a/rebar.config.script b/rebar.config.script
index 1e3c5924b..9d9a6f6e7 100644
--- a/rebar.config.script
+++ b/rebar.config.script
@@ -154,7 +154,7 @@ DepDescs = [
 {hyper,            "hyper",            {tag, "CouchDB-2.2.0-7"}},
 {ibrowse,          "ibrowse",          {tag, "CouchDB-4.4.2-5"}},
 {jiffy,            "jiffy",            {tag, "CouchDB-1.0.9-2"}},
-{mochiweb,         "mochiweb",         {tag, "v3.1.1"}},
+{mochiweb,         "mochiweb",         {tag, "v3.2.0"}},
 {meck,             "meck",             {tag, "0.9.2"}},
 {recon,            "recon",            {tag, "2.5.3"}}
 ].
diff --git a/src/chttpd/src/chttpd.erl b/src/chttpd/src/chttpd.erl
index f8088593f..e35f8e9b7 100644
--- a/src/chttpd/src/chttpd.erl
+++ b/src/chttpd/src/chttpd.erl
@@ -320,7 +320,7 @@ handle_request_int(MochiReq) ->
     erlang:put(dont_log_response, true),
 
     % Save client socket so that it can be monitored for disconnects
-    chttpd_util:mochiweb_socket_set(MochiReq:get(socket)),
+    chttpd_util:mochiweb_client_req_set(MochiReq),
 
     {HttpReq2, Response} =
         case before_request(HttpReq0) of
@@ -330,7 +330,7 @@ handle_request_int(MochiReq) ->
                 {HttpReq0, Response0}
         end,
 
-    chttpd_util:mochiweb_socket_clean(),
+    chttpd_util:mochiweb_client_req_clean(),
 
     {Status, Code, Reason, Resp} = split_response(Response),
 
diff --git a/src/chttpd/src/chttpd_util.erl b/src/chttpd/src/chttpd_util.erl
index 05adef717..4dc590949 100644
--- a/src/chttpd/src/chttpd_util.erl
+++ b/src/chttpd/src/chttpd_util.erl
@@ -23,14 +23,14 @@
     get_chttpd_auth_config_boolean/2,
     maybe_add_csp_header/3,
     get_db_info/1,
-    mochiweb_socket_set/1,
-    mochiweb_socket_clean/0,
-    mochiweb_socket_get/0,
-    mochiweb_socket_check_msec/0,
+    mochiweb_client_req_set/1,
+    mochiweb_client_req_clean/0,
+    mochiweb_client_req_get/0,
+    mochiweb_client_req_check_msec/0,
     stop_client_process_if_disconnected/2
 ]).
 
--define(MOCHIWEB_SOCKET, mochiweb_connection_socket).
+-define(MOCHIWEB_CLIENT_REQ, mochiweb_client_req).
 -define(DISCONNECT_CHECK_MSEC, 30000).
 -define(DISCONNECT_CHECK_JITTER_MSEC, 15000).
 
@@ -120,16 +120,16 @@ get_db_info(DbName) ->
         _Tag:Error -> {error, Error}
     end.
 
-mochiweb_socket_set(Sock) ->
-    put(?MOCHIWEB_SOCKET, Sock).
+mochiweb_client_req_set(ClientReq) ->
+    put(?MOCHIWEB_CLIENT_REQ, ClientReq).
 
-mochiweb_socket_clean() ->
-    erase(?MOCHIWEB_SOCKET).
+mochiweb_client_req_clean() ->
+    erase(?MOCHIWEB_CLIENT_REQ).
 
-mochiweb_socket_get() ->
-    get(?MOCHIWEB_SOCKET).
+mochiweb_client_req_get() ->
+    get(?MOCHIWEB_CLIENT_REQ).
 
-mochiweb_socket_check_msec() ->
+mochiweb_client_req_check_msec() ->
     MSec = config:get_integer(
         "chttpd", "disconnect_check_msec", ?DISCONNECT_CHECK_MSEC
     ),
@@ -138,98 +138,18 @@ mochiweb_socket_check_msec() ->
     ),
     max(100, MSec + rand:uniform(max(1, JitterMSec))).
 
-stop_client_process_if_disconnected(Pid, Sock) ->
-    case is_mochiweb_socket_closed(Sock) of
+stop_client_process_if_disconnected(_Pid, undefined) ->
+    ok;
+stop_client_process_if_disconnected(Pid, ClientReq) ->
+    case mochiweb_request:is_closed(ClientReq) of
         true ->
             exit(Pid, {shutdown, client_disconnected}),
             couch_stats:increment_counter([couchdb, httpd, 
abandoned_streaming_requests]),
             ok;
         false ->
-            ok
-    end.
-
-is_mochiweb_socket_closed(undefined) ->
-    false;
-is_mochiweb_socket_closed(Sock) ->
-    OsType = os:type(),
-    case tcp_info_opt(OsType) of
-        {raw, _, _, _} = InfoOpt ->
-            case mochiweb_socket:getopts(Sock, [InfoOpt]) of
-                {ok, [{raw, _, _, <<State:8/native, _/binary>>}]} ->
-                    tcp_is_closed(State, OsType);
-                {ok, []} ->
-                    false;
-                {error, einval} ->
-                    % Already cleaned up
-                    true;
-                {error, _} ->
-                    false
-            end;
+            ok;
         undefined ->
-            false
+            % Treat unsupported OS-es (ex. Windows) as `not closed`
+            % so we default to the previous behavior.
+            ok
     end.
-
-% All OS-es have the tcpi_state (uint8) as first member of tcp_info struct
-
-tcp_info_opt({unix, linux}) ->
-    %% netinet/in.h
-    %%   IPPROTO_TCP = 6
-    %%
-    %% netinet/tcp.h
-    %%   #define TCP_INFO 11
-    %%
-    {raw, 6, 11, 1};
-tcp_info_opt({unix, darwin}) ->
-    %% netinet/in.h
-    %%   #define IPPROTO_TCP   6
-    %%
-    %% netinet/tcp.h
-    %%   #define TCP_CONNECTION_INFO  0x106
-    %%
-    {raw, 6, 16#106, 1};
-tcp_info_opt({unix, freebsd}) ->
-    %% sys/netinet/in.h
-    %%   #define  IPPROTO_TCP  6
-    %%
-    %% sys/netinet/tcp.h
-    %%   #define  TCP_INFO    32
-    %%
-    {raw, 6, 32, 1};
-tcp_info_opt({_, _}) ->
-    undefined.
-
-tcp_is_closed(State, {unix, linux}) ->
-    %% netinet/tcp.h
-    %%   enum
-    %%   {
-    %%     TCP_ESTABLISHED = 1,
-    %%     TCP_SYN_SENT,
-    %%     TCP_SYN_RECV,
-    %%     TCP_FIN_WAIT1,
-    %%     TCP_FIN_WAIT2,
-    %%     TCP_TIME_WAIT,
-    %%     TCP_CLOSE,
-    %%     TCP_CLOSE_WAIT,
-    %%     TCP_LAST_ACK,
-    %%     TCP_LISTEN,
-    %%     TCP_CLOSING
-    %%   }
-    %%
-    lists:member(State, [4, 5, 6, 7, 8, 9, 11]);
-tcp_is_closed(State, {unix, Type}) when Type =:= darwin; Type =:= freebsd ->
-    %% tcp_fsm.h states are the same on macos and freebsd
-    %%
-    %% netinet/tcp_fsm.h
-    %%   #define TCPS_CLOSED             0       /* closed */
-    %%   #define TCPS_LISTEN             1       /* listening for connection */
-    %%   #define TCPS_SYN_SENT           2       /* active, have sent syn */
-    %%   #define TCPS_SYN_RECEIVED       3       /* have send and received syn 
*/
-    %%   #define TCPS_ESTABLISHED        4       /* established */
-    %%   #define TCPS_CLOSE_WAIT         5       /* rcvd fin, waiting for 
close */
-    %%   #define TCPS_FIN_WAIT_1         6       /* have closed, sent fin */
-    %%   #define TCPS_CLOSING            7       /* closed xchd FIN; await FIN 
ACK */
-    %%   #define TCPS_LAST_ACK           8       /* had fin and close; await 
FIN ACK */
-    %%   #define TCPS_FIN_WAIT_2         9       /* have closed, fin is acked 
*/
-    %%   #define TCPS_TIME_WAIT          10      /* in 2*msl quiet wait after 
close */
-    %%
-    lists:member(State, [0, 5, 6, 7, 8, 9, 10]).
diff --git a/src/chttpd/test/eunit/chttpd_util_test.erl 
b/src/chttpd/test/eunit/chttpd_util_test.erl
index 76edffeac..fbfd532cc 100644
--- a/src/chttpd/test/eunit/chttpd_util_test.erl
+++ b/src/chttpd/test/eunit/chttpd_util_test.erl
@@ -120,27 +120,29 @@ chttpd_util_client_socker_monitor_test_() ->
         fun test_util:start_couch/0,
         fun test_util:stop_couch/1,
         with([
-            ?TDEF(t_socket_set_get_clean),
-            ?TDEF(t_socket_check_config),
+            ?TDEF(t_client_req_set_get_clean),
+            ?TDEF(t_client_req_check_config),
             ?TDEF(t_closed_socket_kills_coordinator)
         ])
     }.
 
-t_socket_set_get_clean(_) ->
-    ?assertEqual(undefined, chttpd_util:mochiweb_socket_get()),
+t_client_req_set_get_clean(_) ->
+    ?assertEqual(undefined, chttpd_util:mochiweb_client_req_get()),
     {ok, Sock} = gen_tcp:listen(0, [{active, false}]),
-    chttpd_util:mochiweb_socket_set(Sock),
-    ?assertEqual(Sock, chttpd_util:mochiweb_socket_get()),
-    chttpd_util:mochiweb_socket_clean(),
-    ?assertEqual(undefined, chttpd_util:mochiweb_socket_get()),
+    Headers = mochiweb_headers:make([]),
+    ClientReq = mochiweb_request:new(Sock, 'GET', "/foo", {1, 1}, Headers),
+    chttpd_util:mochiweb_client_req_set(ClientReq),
+    ?assertEqual(ClientReq, chttpd_util:mochiweb_client_req_get()),
+    chttpd_util:mochiweb_client_req_clean(),
+    ?assertEqual(undefined, chttpd_util:mochiweb_client_req_get()),
     gen_tcp:close(Sock).
 
-t_socket_check_config(_) ->
+t_client_req_check_config(_) ->
     config:set("chttpd", "disconnect_check_msec", "100", false),
     config:set("chttpd", "disconnect_check_jitter_msec", "50", false),
     lists:foreach(
         fun(_) ->
-            MSec = chttpd_util:mochiweb_socket_check_msec(),
+            MSec = chttpd_util:mochiweb_client_req_check_msec(),
             ?assert(is_integer(MSec)),
             ?assert(MSec >= 100),
             ?assert(MSec =< 150)
@@ -153,11 +155,12 @@ t_socket_check_config(_) ->
 t_closed_socket_kills_coordinator(_) ->
     {Pid, Ref} = spawn_coord(),
     {ok, Sock} = gen_tcp:listen(0, [{active, false}]),
-
+    Headers = mochiweb_headers:make([]),
+    ClientReq = mochiweb_request:new(Sock, 'GET', "/foo", {1, 1}, Headers),
     % Can call getopts many times in a row process should stay alive
     lists:foreach(
         fun(_) ->
-            ok = chttpd_util:stop_client_process_if_disconnected(Pid, Sock)
+            ok = chttpd_util:stop_client_process_if_disconnected(Pid, 
ClientReq)
         end,
         lists:seq(1, 10000)
     ),
@@ -165,7 +168,7 @@ t_closed_socket_kills_coordinator(_) ->
 
     gen_tcp:close(Sock),
 
-    ?assertEqual(ok, chttpd_util:stop_client_process_if_disconnected(Pid, 
Sock)),
+    ?assertEqual(ok, chttpd_util:stop_client_process_if_disconnected(Pid, 
ClientReq)),
     case tcp_info_works() of
         true ->
             ?assertEqual({shutdown, client_disconnected}, 
wait_coord_death(Ref));
@@ -178,7 +181,7 @@ t_closed_socket_kills_coordinator(_) ->
     % Can call stop_client_... even if process may be dead and the socket is 
closed
     lists:foreach(
         fun(_) ->
-            ok = chttpd_util:stop_client_process_if_disconnected(Pid, Sock)
+            ok = chttpd_util:stop_client_process_if_disconnected(Pid, 
ClientReq)
         end,
         lists:seq(1, 10000)
     ).
@@ -198,7 +201,7 @@ wait_coord_death(Ref) ->
 tcp_info_works() ->
     case os:type() of
         {unix, OsName} ->
-            lists:member(OsName, [linux, freebsd, darwin]);
+            lists:member(OsName, [linux, freebsd, netbsd, openbsd, darwin]);
         {_, _} ->
             false
     end.
diff --git a/src/fabric/src/fabric_db_update_listener.erl 
b/src/fabric/src/fabric_db_update_listener.erl
index d4a49f37d..4f3c30a25 100644
--- a/src/fabric/src/fabric_db_update_listener.erl
+++ b/src/fabric/src/fabric_db_update_listener.erl
@@ -36,12 +36,12 @@
     shards
 }).
 
-go(Parent, ParentRef, DbName, Timeout, ClientSock) ->
+go(Parent, ParentRef, DbName, Timeout, ClientReq) ->
     Shards = mem3:shards(DbName),
     Notifiers = start_update_notifiers(Shards),
     MonRefs = lists:usort([rexi_utils:server_pid(N) || #worker{node = N} <- 
Notifiers]),
     RexiMon = rexi_monitor:start(MonRefs),
-    MonPid = start_cleanup_monitor(self(), Notifiers, ClientSock),
+    MonPid = start_cleanup_monitor(self(), Notifiers, ClientReq),
     %% This is not a common pattern for rexi but to enable the calling
     %% process to communicate via handle_message/3 we "fake" it as a
     %% a spawned worker.
@@ -97,17 +97,17 @@ handle_db_event(_DbName, deleted, St) ->
 handle_db_event(_DbName, _Event, St) ->
     {ok, St}.
 
-start_cleanup_monitor(Parent, Notifiers, ClientSock) ->
+start_cleanup_monitor(Parent, Notifiers, ClientReq) ->
     spawn(fun() ->
         Ref = erlang:monitor(process, Parent),
-        cleanup_monitor(Parent, Ref, Notifiers, ClientSock)
+        cleanup_monitor(Parent, Ref, Notifiers, ClientReq)
     end).
 
 stop_cleanup_monitor(MonPid) ->
     MonPid ! {self(), stop}.
 
-cleanup_monitor(Parent, Ref, Notifiers, ClientSock) ->
-    CheckMSec = chttpd_util:mochiweb_socket_check_msec(),
+cleanup_monitor(Parent, Ref, Notifiers, ClientReq) ->
+    CheckMSec = chttpd_util:mochiweb_client_req_check_msec(),
     receive
         {'DOWN', Ref, _, _, _} ->
             stop_update_notifiers(Notifiers);
@@ -118,8 +118,8 @@ cleanup_monitor(Parent, Ref, Notifiers, ClientSock) ->
             stop_update_notifiers(Notifiers),
             exit(Parent, {unknown_message, Else})
     after CheckMSec ->
-        chttpd_util:stop_client_process_if_disconnected(Parent, ClientSock),
-        cleanup_monitor(Parent, Ref, Notifiers, ClientSock)
+        chttpd_util:stop_client_process_if_disconnected(Parent, ClientReq),
+        cleanup_monitor(Parent, Ref, Notifiers, ClientReq)
     end.
 
 stop_update_notifiers(Notifiers) ->
diff --git a/src/fabric/src/fabric_streams.erl 
b/src/fabric/src/fabric_streams.erl
index 905e836a1..8c9a6e9a4 100644
--- a/src/fabric/src/fabric_streams.erl
+++ b/src/fabric/src/fabric_streams.erl
@@ -43,8 +43,8 @@ start(Workers0, Keypos, StartFun, Replacements, RingOpts) ->
         replacements = Replacements,
         ring_opts = RingOpts
     },
-    ClientSock = chttpd_util:mochiweb_socket_get(),
-    spawn_worker_cleaner(self(), Workers0, ClientSock),
+    ClientReq = chttpd_util:mochiweb_client_req_get(),
+    spawn_worker_cleaner(self(), Workers0, ClientReq),
     Timeout = fabric_util:request_timeout(),
     case rexi_utils:recv(Workers0, Keypos, Fun, Acc, Timeout, infinity) of
         {ok, #stream_acc{ready = Workers}} ->
@@ -156,12 +156,12 @@ handle_stream_start(Else, _, _) ->
 % Spawn an auxiliary rexi worker cleaner. This will be used in cases
 % when the coordinator (request) process is forceably killed and doesn't
 % get a chance to process its `after` fabric:clean/1 clause.
-spawn_worker_cleaner(Coordinator, Workers, ClientSock) ->
+spawn_worker_cleaner(Coordinator, Workers, ClientReq) ->
     case get(?WORKER_CLEANER) of
         undefined ->
             Pid = spawn(fun() ->
                 erlang:monitor(process, Coordinator),
-                cleaner_loop(Coordinator, Workers, ClientSock)
+                cleaner_loop(Coordinator, Workers, ClientReq)
             end),
             put(?WORKER_CLEANER, Pid),
             Pid;
@@ -169,16 +169,16 @@ spawn_worker_cleaner(Coordinator, Workers, ClientSock) ->
             ExistingCleaner
     end.
 
-cleaner_loop(Pid, Workers, ClientSock) ->
-    CheckMSec = chttpd_util:mochiweb_socket_check_msec(),
+cleaner_loop(Pid, Workers, ClientReq) ->
+    CheckMSec = chttpd_util:mochiweb_client_req_check_msec(),
     receive
         {add_worker, Pid, Worker} ->
-            cleaner_loop(Pid, [Worker | Workers], ClientSock);
+            cleaner_loop(Pid, [Worker | Workers], ClientReq);
         {'DOWN', _, _, Pid, _} ->
             fabric_util:cleanup(Workers)
     after CheckMSec ->
-        chttpd_util:stop_client_process_if_disconnected(Pid, ClientSock),
-        cleaner_loop(Pid, Workers, ClientSock)
+        chttpd_util:stop_client_process_if_disconnected(Pid, ClientReq),
+        cleaner_loop(Pid, Workers, ClientReq)
     end.
 
 add_worker_to_cleaner(CoordinatorPid, Worker) ->
@@ -285,14 +285,22 @@ coordinator_is_killed_if_client_disconnects(_) ->
             die -> ok
         end
     end),
+    Headers = mochiweb_headers:make([]),
     {ok, Sock} = gen_tcp:listen(0, [{active, false}]),
+    ClientReq = mochiweb_request:new(Sock, 'GET', "/foo", {1, 1}, Headers),
     % Close the socket and then expect coordinator to be killed
     ok = gen_tcp:close(Sock),
-    Cleaner = spawn_worker_cleaner(Coord, Workers, Sock),
+    Cleaner = spawn_worker_cleaner(Coord, Workers, ClientReq),
     CleanerRef = erlang:monitor(process, Cleaner),
     % Assert the correct behavior on the support platforms (all except Windows 
so far)
     case os:type() of
-        {unix, Type} when Type =:= linux; Type =:= darwin; Type =:= freebsd ->
+        {unix, Type} when
+            Type =:= linux;
+            Type =:= darwin;
+            Type =:= freebsd;
+            Type =:= openbsd;
+            Type =:= netbsd
+        ->
             % Coordinator should be torn down
             receive
                 {'DOWN', CoordRef, _, _, Reason} ->
@@ -320,8 +328,10 @@ coordinator_is_not_killed_if_client_is_connected(_) ->
             die -> ok
         end
     end),
+    Headers = mochiweb_headers:make([]),
     {ok, Sock} = gen_tcp:listen(0, [{active, false}]),
-    Cleaner = spawn_worker_cleaner(Coord, Workers, Sock),
+    ClientReq = mochiweb_request:new(Sock, 'GET', "/foo", {1, 1}, Headers),
+    Cleaner = spawn_worker_cleaner(Coord, Workers, ClientReq),
     CleanerRef = erlang:monitor(process, Cleaner),
     % Coordinator should stay up
     receive
@@ -342,7 +352,7 @@ coordinator_is_not_killed_if_client_is_connected(_) ->
 setup() ->
     ok = meck:expect(rexi, kill_all, fun(_) -> ok end),
     % Speed up disconnect socket timeout for the test to 200 msec
-    ok = meck:expect(chttpd_util, mochiweb_socket_check_msec, 0, 200).
+    ok = meck:expect(chttpd_util, mochiweb_client_req_check_msec, 0, 200).
 
 teardown(_) ->
     meck:unload().
diff --git a/src/fabric/src/fabric_view_changes.erl 
b/src/fabric/src/fabric_view_changes.erl
index 2644cd59d..32e16fbc5 100644
--- a/src/fabric/src/fabric_view_changes.erl
+++ b/src/fabric/src/fabric_view_changes.erl
@@ -39,12 +39,12 @@ go(DbName, Feed, Options, Callback, Acc0) when
             {Timeout, _} = couch_changes:get_changes_timeout(Args, Callback),
             Ref = make_ref(),
             Parent = self(),
-            ClientSock = chttpd_util:mochiweb_socket_get(),
+            ClientReq = chttpd_util:mochiweb_client_req_get(),
             UpdateListener = {
                 spawn_link(
                     fabric_db_update_listener,
                     go,
-                    [Parent, Ref, DbName, Timeout, ClientSock]
+                    [Parent, Ref, DbName, Timeout, ClientReq]
                 ),
                 Ref
             },

Reply via email to