diff --git a/lib/inets/src/http_server/httpd_connection_sup.erl b/lib/inets/src/http_server/httpd_connection_sup.erl index f7b3bef245b5..9f76613e4473 100644 --- a/lib/inets/src/http_server/httpd_connection_sup.erl +++ b/lib/inets/src/http_server/httpd_connection_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2016. All Rights Reserved. +%% Copyright Ericsson AB 2008-2023. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -58,7 +58,7 @@ init([[Addr, Port]]) -> Name = undefined, % As simple_one_for_one is used. StartFunc = {httpd_request_handler, start_link, []}, Restart = temporary, % E.g. should not be restarted - Shutdown = 4000, + Shutdown = brutal_kill, Modules = [httpd_request_handler], Type = worker, diff --git a/lib/inets/src/http_server/httpd_manager.erl b/lib/inets/src/http_server/httpd_manager.erl index c4ac02b3c191..e87960e4cca2 100644 --- a/lib/inets/src/http_server/httpd_manager.erl +++ b/lib/inets/src/http_server/httpd_manager.erl @@ -472,20 +472,19 @@ count_children(Sup) -> Children = supervisor:count_children(whereis(Sup)), proplists:get_value(workers, Children). -shutdown_connections(Sup) -> - Children = [Child || {_,Child,_,_} <- supervisor:which_children(Sup)], - lists:foreach(fun(Pid) -> exit(Pid, kill) end, - Children). - -wait_for_shutdown(CSup, Manager) -> - case count_children(CSup) of - 0 -> - Manager ! connections_terminated; - _ -> - receive - after 500 -> - ok - end, - wait_for_shutdown(CSup, Manager) - end. - +shutdown_connections(CSup) -> + Children = [Child || {_,Child,_,_} <- supervisor:which_children(CSup)], + lists:foreach( + fun(Child) -> + _ = supervisor:terminate_child(CSup, Child) + end, Children). + +wait_for_shutdown(CSup, Manager) -> + Children = [Child || {_,Child,_,_} <- supervisor:which_children(CSup)], + Monitors = [erlang:monitor(process, Child) || Child <- Children], + lists:foreach( + fun(Mref) -> + receive {'DOWN', Mref, _, _, _} -> ok end + end, Monitors), + Manager ! connections_terminated, + ok. diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index 3b4fc5c5129a..43e916c61b7a 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -375,8 +375,10 @@ await_socket_ownership_transfer(AcceptTimeout) -> end. +handle_msg(Body, State) when is_binary(Body) -> + handle_response(State#state{body = Body}); %%% Internal chunking of client body -handle_msg({{continue, Chunk}, Module, Function, Args}, #state{chunk = {_, CbState}} = State) -> +handle_msg({{continue, Chunk}, Module, Function, Args}, #state{chunk = {_, CbState}} = State) when is_binary(Chunk) -> handle_internal_chunk(State#state{chunk = {continue, CbState}, body = Chunk}, Module, Function, Args); handle_msg({continue, Module, Function, Args}, #state{mod = ModData} = State) -> @@ -388,38 +390,42 @@ handle_msg({last, Body}, #state{headers = Headers, chunk = {_, CbState}} = State headers = NewHeaders, body = Body}); %%% Last data chunked by client -handle_msg({ChunkedHeaders, Body}, #state{headers = Headers , chunk = {ChunkState, CbState}} = State) when ChunkState =/= undefined -> +handle_msg({ChunkedHeaders, Body}, #state{headers = Headers , chunk = {ChunkState, CbState}} = State) when ChunkState =/= undefined, is_binary(Body) -> NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders), handle_response(State#state{chunk = {last, CbState}, headers = NewHeaders, body = Body}); -handle_msg({ChunkedHeaders, Body}, #state{headers = Headers , chunk = {undefined, _}} = State) -> +handle_msg({ChunkedHeaders, Body}, #state{headers = Headers , chunk = {undefined, _}} = State) when is_binary(Body) -> NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders), handle_response(State#state{headers = NewHeaders, body = Body}); +%%% handle_msg(Result, State) -> handle_http_msg(Result, State). -handle_http_msg({_, _, Version, {_, _}, _}, - #state{status = busy, mod = ModData} = State) -> - handle_manager_busy(State#state{mod = +%% status = busy +handle_http_msg({_, _, Version, {_, _}, _}, + #state{status = busy, mod = ModData} = State) -> + handle_manager_busy(State#state{mod = ModData#mod{http_version = Version}}), - {stop, normal, State}; + {stop, normal, State}; -handle_http_msg({_, _, Version, {_, _}, _}, +%% status = blocked +handle_http_msg({_, _, Version, {_, _}, _}, #state{status = blocked, mod = ModData} = State) -> - handle_manager_blocked(State#state{mod = + handle_manager_blocked(State#state{mod = ModData#mod{http_version = Version}}), - {stop, normal, State}; + {stop, normal, State}; +%% status = accept handle_http_msg({Method, Uri, Version, {RecordHeaders, Headers}, Body}, - #state{status = accept, mod = ModData} = State) -> + #state{status = accept, mod = ModData} = State) -> + true = is_binary(Body), case httpd_request:validate(Method, Uri, Version) of {ok, NormalizedURI} -> - {ok, NewModData} = + {ok, NewModData} = httpd_request:update_mod_data(ModData, Method, NormalizedURI, Version, Headers), - case is_host_specified_if_required(NewModData#mod.absolute_uri, RecordHeaders, Version) of true -> @@ -427,8 +433,8 @@ handle_http_msg({Method, Uri, Version, {RecordHeaders, Headers}, Body}, body = Body, mod = NewModData}); false -> - httpd_response:send_status(ModData#mod{http_version = - Version}, + httpd_response:send_status(ModData#mod{http_version = + Version}, 400, none), {stop, normal, State#state{response_sent = true}} end; @@ -445,9 +451,7 @@ handle_http_msg({Method, Uri, Version, {RecordHeaders, Headers}, Body}, ModData#mod{http_version = httpd_request:default_version()}, 400, Ver, {malformed_syntax, Ver}), {stop, normal, State#state{response_sent = true}} - end; -handle_http_msg(Body, State) -> - handle_response(State#state{body = Body}). + end. handle_manager_busy(#state{mod = #mod{config_db = ConfigDB}} = State) -> MaxClients = httpd_util:lookup(ConfigDB, max_clients, 150), @@ -492,7 +496,7 @@ handle_body(#state{headers = Headers, body = Body, {noreply, State#state{mfa = {Module, Function, Args}, chunk = chunk_start(MaxChunk)}}; - {ok, {ChunkedHeaders, NewBody}} -> + {ok, {ChunkedHeaders, NewBody}} when is_binary(NewBody) -> NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders), handle_response(State#state{headers = NewHeaders, body = NewBody, @@ -525,12 +529,12 @@ handle_body(#state{headers = Headers, body = Body, setopts(ModData#mod.socket, ModData#mod.socket_type, [{active, once}]), {noreply, State#state{mfa = {Module, Function, Args}}}; - {ok, {{continue, Chunk}, Module, Function, Args}} -> + {ok, {{continue, Chunk}, Module, Function, Args}} when is_binary(Chunk) -> handle_internal_chunk(State#state{chunk = chunk_start(MaxChunk), body = Chunk}, Module, Function, Args); %% Whole body delivered, if chunking mechanism is enabled the whole %% body fits in one chunk. - {ok, NewBody} -> + {ok, NewBody} when is_binary(NewBody) -> handle_response(State#state{chunk = chunk_finish(ChunkState, CbState, MaxChunk), headers = Headers, @@ -678,7 +682,7 @@ handle_next_request(#state{mod = #mod{connection = true} = ModData, mfa = MFA, max_keep_alive_request = decrease(Max), headers = #http_request_h{}, - body = undefined, + body = <<>>, chunk = chunk_start(MaxChunk), response_sent = false}, diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl index 92f206c1a05f..8e42311d0040 100644 --- a/lib/inets/src/http_server/mod_esi.erl +++ b/lib/inets/src/http_server/mod_esi.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2022. All Rights Reserved. +%% Copyright Ericsson AB 1997-2023. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -297,7 +297,7 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid) -> deliver_webpage_chunk(ModData, Pid, Timeout). deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) -> - case receive_headers(Timeout) of + case receive_headers(Pid, Timeout) of {error, Reason} -> %% Happens when webpage generator callback/3 is undefined {error, Reason}; @@ -334,17 +334,17 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) -> {proceed,[{response, {already_sent, 504, 0}} | ModData#mod.data]} end. -receive_headers(Timeout) -> +receive_headers(Pid, Timeout) -> receive {esi_data, Chunk} -> httpd_esi:parse_headers(lists:flatten(Chunk)); {ok, Chunk} -> httpd_esi:parse_headers(lists:flatten(Chunk)); - {'EXIT', Pid, erl_scheme_webpage_chunk_undefined} when is_pid(Pid) -> + {'EXIT', Pid, erl_scheme_webpage_chunk_undefined} -> {error, erl_scheme_webpage_chunk_undefined}; - {'EXIT', Pid, {continue, _} = Continue} when is_pid(Pid) -> + {'EXIT', Pid, {continue, _} = Continue} -> Continue; - {'EXIT', Pid, Reason} when is_pid(Pid) -> + {'EXIT', Pid, Reason} -> exit({mod_esi_linked_process_died, Pid, Reason}) after Timeout -> timeout diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index 121969994f32..0ee7d38c5105 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -1599,6 +1599,7 @@ disturbing_1_0(Config) when is_list(Config) -> disturbing([{http_version, "HTTP/1.0"} | Config]). disturbing(Config) when is_list(Config)-> + LogWatcher = start_log_watcher(), Server = proplists:get_value(server_pid, Config), Version = proplists:get_value(http_version, Config), Host = proplists:get_value(host, Config), @@ -1615,12 +1616,13 @@ disturbing(Config) when is_list(Config)-> Close = list_to_atom((typestr(Type)) ++ "_closed"), receive {Close, Socket} -> - ok; - Msg -> - ct:fail({{expected, {Close, Socket}}, {got, Msg}}) - end, - inets_test_lib:close(Type, Socket), - [{server_name, "httpd_disturbing_" ++ Version}] = httpd:info(Server, [server_name]). + inets_test_lib:close(Type, Socket), + [{server_name, "httpd_disturbing_" ++ Version}] = + httpd:info(Server, [server_name]), + [] = stop_log_watcher(LogWatcher), + [] = inets_test_lib:flush(), + ok + end. %%------------------------------------------------------------------------- non_disturbing_1_1(Config) when is_list(Config) -> non_disturbing([{http_version, "HTTP/1.1"} | Config]). @@ -1680,7 +1682,6 @@ reload_config_file(Config) when is_list(Config) -> ok = file:write_file(HttpdConf, NewConfig), ok = httpd:reload_config(HttpdConf, non_disturbing), "httpd_test_new" = proplists:get_value(server_name, httpd:info(Server)). - %%------------------------------------------------------------------------- mime_types_format(Config) when is_list(Config) -> DataDir = proplists:get_value(data_dir, Config), @@ -2654,3 +2655,56 @@ peer(Config) -> _ -> "false" end. + +start_log_watcher() -> + Spawner = self(), + EventDest = erlang:alias(), + HandlerId = ?MODULE, + _ = + spawn( + fun () -> + MonAlias = + monitor(process, Spawner, [{alias,reply_demonitor}]), + EventDest ! {started,EventDest,MonAlias}, + receive + {stop,MonAlias} -> + _ = logger:remove_handler(HandlerId), + EventDest ! {stopped,EventDest}, + ok; + {'DOWN',MonAlias,_,_,_} -> + _ = logger:remove_handler(HandlerId), + ok + end + end), + receive + {started,EventDest,Watcher} -> + Config = #{ config => EventDest }, + ok = logger:add_handler(HandlerId, ?MODULE, Config), + {EventDest,Watcher} + end. + +stop_log_watcher({EventDest,Watcher}) -> + Watcher ! {stop,Watcher}, + receive + {stopped,EventDest} -> + true = unalias(EventDest), + stop_log_watcher_collect(EventDest) + end. +%% +stop_log_watcher_collect(EventDest) -> + receive + {event,EventDest,Event} -> + [Event | stop_log_watcher_collect(EventDest)] + after 0 -> + [] + end. + +log(#{level := Level} = Event, #{ config := EventDest }) -> + %% Pass on events of level 'error' or worse + case logger:compare_levels(Level, error) of + lt -> + ok; + _ -> + EventDest ! {event,EventDest,Event}, + ok + end.