diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index 4a4fedc14985..b837ef9d9ae5 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -1583,7 +1583,7 @@ tls_tunnel_request(#request{headers = Headers, id = RequestId, from = From, address = {Host, Port}= Adress, - ipv6_host_with_brackets = IPV6}) -> + ipv6_host_with_brackets = IPV6, request_options = ReqOptions}) -> URI = Host ++":" ++ integer_to_list(Port), @@ -1605,7 +1605,8 @@ tls_tunnel_request(#request{headers = Headers, userinfo = "", headers_as_is = [], started = http_util:timestamp(), - ipv6_host_with_brackets = IPV6 + ipv6_host_with_brackets = IPV6, + request_options = ReqOptions }. host_header(#http_request_h{host = Host}, _) -> diff --git a/lib/inets/test/httpc_proxy_SUITE.erl b/lib/inets/test/httpc_proxy_SUITE.erl index f7a97c09a9c9..66a3b0640706 100644 --- a/lib/inets/test/httpc_proxy_SUITE.erl +++ b/lib/inets/test/httpc_proxy_SUITE.erl @@ -35,6 +35,7 @@ -compile(export_all). -define(LOCAL_PROXY_SCRIPT, "server_proxy.sh"). +-define(DUMMY_PROXY_SCRIPT, "dummy_proxy.sh"). -define(p(F, A), % Debug printout begin io:format( @@ -53,18 +54,24 @@ suite() -> all() -> [{group,local_proxy}, - {group,local_proxy_https}]. + {group,local_proxy_https}, + {group, remote_proxy}, + {group, remote_proxy_https}, + {group, dummy_proxy}]. groups() -> [{local_proxy,[], [http_emulate_lower_versions - |local_proxy_cases()]}, + |proxy_cases()]}, {local_proxy_https,[], - local_proxy_cases() ++ local_proxy_https_cases()}]. + proxy_cases() ++ proxy_https_cases()}, + {remote_proxy, [], proxy_cases()}, + {remote_proxy_https, [], proxy_cases() ++ proxy_https_cases()}, + {dummy_proxy, [], [proxy_upgrade_connect_error]}]. %% internal functions -local_proxy_cases() -> +proxy_cases() -> [http_head, http_get, http_options, @@ -79,7 +86,7 @@ local_proxy_cases() -> http_stream, http_not_modified_otp_6821]. -local_proxy_https_cases() -> +proxy_https_cases() -> [https_connect_error, http_timeout]. @@ -108,12 +115,31 @@ suite_apps() -> init_per_group(local_proxy, Config) -> init_local_proxy([{protocol,http}|Config]); init_per_group(local_proxy_https, Config) -> - init_local_proxy([{protocol,https}|Config]). + init_local_proxy([{protocol,https}|Config]); + +init_per_group(remote_proxy, Config) -> + Config1 = init_local_proxy([{protocol,http}|Config]), + {local,{{"localhost",Port},[]}} = proplists:get_value(proxy, Config1), + lists:keyreplace(proxy, 1, Config1, {proxy, {local, {{"127.0.0.1", Port}, []}}}); +init_per_group(remote_proxy_https, Config) -> + Config1 = init_local_proxy([{protocol,https}|Config]), + {local,{{"localhost",Port},[]}} = proplists:get_value(proxy, Config1), + lists:keyreplace(proxy, 1, Config1, {proxy, {local, {{"127.0.0.1", Port}, []}}}); + +init_per_group(dummy_proxy, Config) -> + ProxyPort = 8000, + ProxyAddress = "127.0.0.1", + DummyPort = 8080, + DummyServer = "localhost", + [{proxy, {local, {{ProxyAddress, ProxyPort}, []}}}, {http, {DummyServer, DummyPort}} | Config]. + end_per_group(Group, Config) when Group =:= local_proxy; - Group =:= local_proxy_https -> + Group =:= local_proxy_https; + Group =:= remote_proxy; + Group =:= remote_proxy_https -> rcmd_local_proxy(["stop"], Config), Config; end_per_group(_, Config) -> @@ -121,7 +147,16 @@ end_per_group(_, Config) -> %%-------------------------------------------------------------------- -init_per_testcase(Case, Config0) -> +init_per_testcase(proxy_upgrade_connect_error = Case, Config) -> + Response = "HTTP/1.1 500", + init_dummy_proxy(Response, Config), + do_init_per_testcase(Case, Config); +init_per_testcase(Case, Config) -> + do_init_per_testcase(Case, Config). + +do_init_per_testcase(_, {skip, _} = Config) -> + Config; +do_init_per_testcase(Case, Config0) -> ct:timetrap({seconds,30}), Apps = apps(Case, Config0), case init_apps(Apps, Config0) of @@ -137,6 +172,12 @@ init_per_testcase(Case, Config0) -> E3 end. +end_per_testcase(proxy_upgrade_connect_error, Config) -> + DataDir = proplists:get_value(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), + Script = filename:join(DataDir, ?DUMMY_PROXY_SCRIPT), + rcmd(Script, ["stop"], [{cd, PrivDir}]), + Config; end_per_testcase(_Case, Config) -> app_stop(inets), Config. @@ -447,6 +488,26 @@ https_connect_error(Config) when is_list(Config) -> {error,{failed_connect,[_,{tls,_,_}]}} = httpc:request(Method, Request, HttpOpts, Opts). +%%-------------------------------------------------------------------- +proxy_upgrade_connect_error(doc) -> + ["This targets verification of upgrade process + when proxy sends back response code that is not 200"]; +proxy_upgrade_connect_error(Config) when is_list(Config) -> + {HttpServer,HttpPort} = proplists:get_value(http, Config), + Method = get, + %% using HTTPS scheme to test upgrade connection + URL = "https://" ++ HttpServer ++ ":" ++ + integer_to_list(HttpPort) ++ "/index.html", + Opts = [], + HttpOpts = [?SSL_NO_VERIFY], + Request = {URL,[]}, + %% This is a dummy proxy so no further connection will be established + %% We are only interested in testing parsing of the proxy response + {error,{failed_connect,[_,{_,_,econnrefused}]}} = + httpc:request(Method, Request, HttpOpts, Opts). + + + %%-------------------------------------------------------------------- http_timeout(doc) -> ["Test http/https connect and upgrade timeouts."]; @@ -455,10 +516,11 @@ http_timeout(Config) when is_list(Config) -> URL = url("/index.html", Config), Request = {URL,[]}, Timeout = timer:seconds(1), + {_,{{ProxyAddr, ProxyPort}, []}} = proplists:get_value(proxy, Config), HttpOpts1 = [{timeout, Timeout}, {connect_timeout, 0}, ?SSL_NO_VERIFY], {error, {failed_connect, - [{to_address,{"localhost",8000}}, + [{to_address,{ProxyAddr, ProxyPort}}, {inet,[inet],timeout}]}} = httpc:request(Method, Request, HttpOpts1, []), ok. @@ -538,6 +600,30 @@ url(AbsPath, Config) -> %%-------------------------------------------------------------------- +init_dummy_proxy(Response, Config) -> + case os:type() of + {unix, _} -> + case os:cmd("which ncat") of + [] -> + {skip, "Ncat not available on the system"}; + _ -> + Proxy = proplists:get_value(proxy, Config), + {_, {{ProxyAddress, ProxyPort}, _}} = Proxy, + + DataDir = proplists:get_value(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), + Script = filename:join(DataDir, ?DUMMY_PROXY_SCRIPT), + + spawn(fun() -> rcmd(Script, ["start", + ProxyAddress, + integer_to_list(ProxyPort), + Response], [{cd, PrivDir}]) + end) + end; + _ -> + {skip, "Platform cannot run dummy proxy script"} + end. + init_local_proxy(Config) -> case os:type() of {unix,_} -> diff --git a/lib/inets/test/httpc_proxy_SUITE_data/dummy_proxy.sh b/lib/inets/test/httpc_proxy_SUITE_data/dummy_proxy.sh new file mode 100755 index 000000000000..f628a78fc9e0 --- /dev/null +++ b/lib/inets/test/httpc_proxy_SUITE_data/dummy_proxy.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +# %CopyrightBegin% +# +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright Ericsson AB 2025. All Rights Reserved. +# +# %CopyrightEnd% + +case :"${1:?}" in + :start) + /bin/echo -ne "${4}\r\n\r\n" | ncat -l ${2} ${3} & echo $! > dummy_proxy.pid + ;; + :stop) + kill $(cat dummy_proxy.pid) || true + ;; +esac