diff --git a/.github/workflows/erlang.yml b/.github/workflows/erlang.yml index be8386f..1c49dc7 100644 --- a/.github/workflows/erlang.yml +++ b/.github/workflows/erlang.yml @@ -39,5 +39,3 @@ jobs: run: make eunit - name: Run dialyzer run: make dialyzer - - name: Ensure version consistency - run: ./check_vsns.escript diff --git a/changelog.md b/changelog.md index 636bfd3..c801e77 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,8 @@ # ehttpc changes -## 0.4.15 +## 0.5.0 +- Dropped hot-upgrade support. - Added support for using HTTP proxy (HTTP 1.1 only). To use it, pass `proxy` in the pool opts. diff --git a/check_vsns.escript b/check_vsns.escript deleted file mode 100755 index c9b24dc..0000000 --- a/check_vsns.escript +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env escript - --mode(compile). - -main(_) -> - {ok, [{application, ehttpc, App}]} = file:consult("src/ehttpc.app.src"), - {ok, [{VsnAppup, _Up, _Down}]} = file:consult("src/ehttpc.appup.src"), - {vsn, VsnApp} = lists:keyfind(vsn, 1, App), - case VsnAppup =:= VsnApp of - true -> ok; - false -> error([{appup, VsnAppup}, {app, VsnApp}]) - end. diff --git a/src/ehttpc.app.src b/src/ehttpc.app.src index 6eb73f8..151a496 100644 --- a/src/ehttpc.app.src +++ b/src/ehttpc.app.src @@ -1,6 +1,6 @@ {application, ehttpc, [ {description, "HTTP Client for Erlang/OTP"}, - {vsn, "0.4.15"}, + {vsn, "0.5.0"}, {registered, []}, {applications, [ kernel, diff --git a/src/ehttpc.appup.src b/src/ehttpc.appup.src deleted file mode 100644 index 4cf3f09..0000000 --- a/src/ehttpc.appup.src +++ /dev/null @@ -1,176 +0,0 @@ -%% -*- mode: erlang -*- -{"0.4.15", - [ - {"0.4.14", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc, brutal_purge, soft_purge, []} - ]}, - {"0.4.13", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc, brutal_purge, soft_purge, []} - ]}, - {"0.4.12", [ - {load_module, ehttpc, brutal_purge, soft_purge, []} - ]}, - {"0.4.11", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc, brutal_purge, soft_purge, []} - ]}, - {"0.4.10", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc, brutal_purge, soft_purge, []} - ]}, - {"0.4.9", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc, brutal_purge, soft_purge, []} - ]}, - {"0.4.8", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc, brutal_purge, soft_purge, []} - ]}, - {"0.4.7", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc, brutal_purge, soft_purge, []} - ]}, - {"0.4.6", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc, brutal_purge, soft_purge, []} - ]}, - {"0.4.5", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc, brutal_purge, soft_purge, []} - ]}, - {<<"0\\.4\\.[0-4]">>, [ - {load_module, ehttpc, brutal_purge, soft_purge, []}, - {load_module, ehttpc_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_worker_sup, brutal_purge, soft_purge, []} - ]}, - {"0.3.0", [ - {load_module, ehttpc, brutal_purge, soft_purge, []}, - {load_module, ehttpc_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_worker_sup, brutal_purge, soft_purge, []} - ]}, - {<<"0\\.2\\.[0-1]">>, [ - {load_module, ehttpc, brutal_purge, soft_purge, []}, - {load_module, ehttpc_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_worker_sup, brutal_purge, soft_purge, []} - ]}, - {<<"0\\.1\\\.[0-7]">>, [ - {update, ehttpc, {advanced, []}}, - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_worker_sup, brutal_purge, soft_purge, []} - ]}, - {<<"0\\.1\\.([8-9]|(1[0-4]))">>, [ - {update, ehttpc, {advanced, []}}, - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_worker_sup, brutal_purge, soft_purge, []} - ]}, - {"0.1.15", [ - {update, ehttpc, {advanced, []}}, - {load_module, ehttpc_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_worker_sup, brutal_purge, soft_purge, []} - ]} - ], - [ - {"0.4.14", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {update, ehttpc, {advanced, [no_proxy]}} - ]}, - {"0.4.13", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {update, ehttpc, {advanced, [no_proxy]}} - ]}, - {"0.4.12", [ - {update, ehttpc, {advanced, [no_proxy]}} - ]}, - {"0.4.11", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {update, ehttpc, {advanced, [no_proxy]}} - ]}, - {"0.4.10", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {update, ehttpc, {advanced, [no_proxy]}} - ]}, - {"0.4.9", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {update, ehttpc, {advanced, [no_proxy]}} - ]}, - {"0.4.8", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {update, ehttpc, {advanced, [no_proxy]}} - ]}, - {"0.4.7", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {update, ehttpc, {advanced, [no_proxy]}} - ]}, - {"0.4.6", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {update, ehttpc, {advanced, [no_proxy]}} - ]}, - {"0.4.5", [ - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {update, ehttpc, {advanced, [no_proxy]}} - ]}, - {<<"0\\.4\\.[0-4]">>, [ - {update, ehttpc, {advanced, [no_proxy]}}, - {load_module, ehttpc_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_worker_sup, brutal_purge, soft_purge, []} - ]}, - {"0.3.0", [ - {update, ehttpc, {advanced, [no_proxy]}}, - {load_module, ehttpc_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_worker_sup, brutal_purge, soft_purge, []} - ]}, - {<<"0\\.2\\.[0-1]">>, [ - {update, ehttpc, {advanced, [no_proxy]}}, - {load_module, ehttpc_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_worker_sup, brutal_purge, soft_purge, []} - ]}, - {<<"0\\.1\\.0">>, [ - {update, ehttpc, {advanced, [no_requests]}}, - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_worker_sup, brutal_purge, soft_purge, []} - ]}, - {<<"0\\.1\\.[1-7]">>, [ - {update, ehttpc, {advanced, [no_enable_pipelining]}}, - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_worker_sup, brutal_purge, soft_purge, []} - ]}, - {<<"0\\.1\\.([8-9]|(1[0-4]))">>, [ - {update, ehttpc, {advanced, [downgrade_requests]}}, - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_worker_sup, brutal_purge, soft_purge, []} - ]}, - {"0.1.15", [ - {update, ehttpc, {advanced, [downgrade_requests]}}, - {load_module, ehttpc_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool, brutal_purge, soft_purge, []}, - {load_module, ehttpc_pool_sup, brutal_purge, soft_purge, []}, - {load_module, ehttpc_worker_sup, brutal_purge, soft_purge, []} - ]} - ] -}. diff --git a/src/ehttpc.erl b/src/ehttpc.erl index 0e0a1da..6e3ea0e 100644 --- a/src/ehttpc.erl +++ b/src/ehttpc.erl @@ -37,7 +37,6 @@ handle_cast/2, handle_info/2, terminate/2, - code_change/3, format_status/1, format_status/2, format_state/2 @@ -329,118 +328,6 @@ terminate(_Reason, #state{pool = Pool, id = Id, client = Client}) -> gproc_pool:disconnect_worker(ehttpc:name(Pool), {Pool, Id}), ok. -%% NOTE: the git tag 0.1.0 was re-tagged -%% the actual version in use in EMQX 4.2 had requests missing -code_change({down, _Vsn}, State, [no_requests]) -> - %% downgrage to a version before 'requests' and 'enable_pipelining' were added - #state{ - pool = Pool, - id = ID, - client = Client, - mref = MRef, - host = Host, - port = Port, - gun_opts = GunOpts, - gun_state = GunState - } = State, - {ok, {state, Pool, ID, Client, MRef, Host, Port, GunOpts, GunState}}; -code_change({down, _Vsn}, State, [no_enable_pipelining]) -> - %% downgrade to a version before 'enable_pipelining' was added - #state{ - pool = Pool, - id = ID, - client = Client, - mref = MRef, - host = Host, - port = Port, - gun_opts = GunOpts, - gun_state = GunState, - requests = Requests - } = State, - OldRequests = downgrade_requests(Requests), - {ok, {state, Pool, ID, Client, MRef, Host, Port, GunOpts, GunState, OldRequests}}; -code_change({down, _Vsn}, State, [downgrade_requests]) -> - %% downgrade to a version which had old format 'requests' - #state{ - pool = Pool, - id = ID, - client = Client, - mref = MRef, - host = Host, - port = Port, - enable_pipelining = Pipelining, - gun_opts = GunOpts, - gun_state = GunState, - requests = Requests - } = State, - OldRequests = downgrade_requests(Requests), - {ok, {state, Pool, ID, Client, MRef, Host, Port, Pipelining, GunOpts, GunState, OldRequests}}; -code_change({down, _Vsn}, State, [no_proxy]) -> - %% downgrade to a version before `proxy' was added - #state{ - pool = Pool, - id = ID, - client = Client, - mref = MRef, - host = Host, - port = Port, - enable_pipelining = Pipelining, - gun_opts = GunOpts, - gun_state = GunState, - requests = Requests - } = State, - {ok, {state, Pool, ID, Client, MRef, Host, Port, Pipelining, GunOpts, GunState, Requests}}; -%% below are upgrade instructions -code_change(_Vsn, {state, Pool, ID, Client, MRef, Host, Port, GunOpts, GunState}, _Extra) -> - %% upgrade from a version before 'requests' field was added - {ok, #state{ - pool = Pool, - id = ID, - client = Client, - mref = MRef, - host = Host, - port = Port, - enable_pipelining = true, - gun_opts = GunOpts, - gun_state = GunState, - requests = upgrade_requests(#{}), - proxy = undefined - }}; -code_change(_Vsn, {state, Pool, ID, Client, MRef, Host, Port, GunOpts, GunState, Requests}, _) -> - %% upgrade from a version before 'enable_pipelining' field was added - {ok, #state{ - pool = Pool, - id = ID, - client = Client, - mref = MRef, - host = Host, - port = Port, - enable_pipelining = true, - gun_opts = GunOpts, - gun_state = GunState, - requests = upgrade_requests(Requests) - }}; -code_change( - _Vsn, {state, Pool, ID, Client, MRef, Host, Port, Pipelining, GunOpts, GunState, Requests}, _ -) -> - %% upgrade from a version before `proxy' field was added - {ok, #state{ - pool = Pool, - id = ID, - client = Client, - mref = MRef, - host = Host, - port = Port, - enable_pipelining = Pipelining, - gun_opts = GunOpts, - gun_state = GunState, - requests = upgrade_requests(Requests), - proxy = undefined - }}; -code_change(_Vsn, State, _) -> - %% upgrade from a version having old format 'requests' field - {ok, upgrade_requests(State)}. - format_status(Status = #{state := State}) -> Status#{state => format_state(State, minimal)}. diff --git a/test/ehttpc_app_tests.erl b/test/ehttpc_app_tests.erl new file mode 100644 index 0000000..bc54dff --- /dev/null +++ b/test/ehttpc_app_tests.erl @@ -0,0 +1,65 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2021-2022 EMQ Technologies Co., Ltd. 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. +%% 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(ehttpc_app_tests). + +-include_lib("eunit/include/eunit.hrl"). + +ensure_vsn_bump_test() -> + TagV = parse_semver(os:cmd("git describe --tags")), + {ok, [{application, ehttpc, Props}]} = file:consult("src/ehttpc.app.src"), + V = parse_semver(proplists:get_value(vsn, Props)), + ?assert(TagV =< V). + +ensure_changelog_test() -> + {ok, [{application, ehttpc, Props}]} = file:consult("src/ehttpc.app.src"), + Vsn = proplists:get_value(vsn, Props), + ExpectedChangeLogLine = "## " ++ Vsn, + {ok, Text} = file:read_file("changelog.md"), + Lines = binary:split(Text, <<"\n">>, [global]), + case lists:member(iolist_to_binary(ExpectedChangeLogLine), Lines) of + true -> ok; + false -> error({missing_changelog_for_vsn, ExpectedChangeLogLine}) + end. + +ensure_git_tag_test() -> + Latest = lists:last(lists:sort([parse_semver(T) || T <- all_tags()])), + LatestTag = format_semver(Latest), + ChangedFiles = os:cmd("git diff --name-only " ++ LatestTag ++ "...HEAD src"), + case ChangedFiles of + [] -> + ok; + Other -> + %% only print a warning message + %% because we can not control git tags in eunit + io:format( + user, + "################################~n" + "changed since ~s:~n~p~n", + [LatestTag, Other] + ), + ok + end. + +format_semver({Major, Minor, Patch}) -> + lists:flatten(io_lib:format("~p.~p.~p", [Major, Minor, Patch])). + +parse_semver(Str) -> + [Major, Minor, Patch | _] = string:tokens(Str, ".-\n"), + {list_to_integer(Major), list_to_integer(Minor), list_to_integer(Patch)}. + +all_tags() -> + string:tokens(os:cmd("git tag"), "\n"). diff --git a/test/ehttpc_appup_tests.erl b/test/ehttpc_appup_tests.erl deleted file mode 100644 index 8c6d99c..0000000 --- a/test/ehttpc_appup_tests.erl +++ /dev/null @@ -1,149 +0,0 @@ -%%-------------------------------------------------------------------- -%% Copyright (c) 2021-2022 EMQ Technologies Co., Ltd. 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. -%% 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(ehttpc_appup_tests). - --include_lib("eunit/include/eunit.hrl"). - -%% First version used in EMQX v4.3.0 --record(state_0_1_0, {pool, id, client, mref, host, port, gun_opts, gun_state}). --record(state_0_1_7, {pool, id, client, mref, host, port, gun_opts, gun_state, requests}). --record(state_0_1_8, { - pool, id, client, mref, host, port, enable_pipelining, gun_opts, gun_state, requests -}). - -app_vsn_test() -> - {ok, [{AppupVsn, _Upgraes, _Downgrades}]} = file:consult("src/ehttpc.appup.src"), - {ok, [{application, ehttpc, Props}]} = file:consult("src/ehttpc.app.src"), - Vsn = proplists:get_value(vsn, Props), - ?assertEqual(Vsn, AppupVsn). - -ensure_vsn_bump_test() -> - TagV = parse_semver(os:cmd("git describe --tags")), - {ok, [{application, ehttpc, Props}]} = file:consult("src/ehttpc.app.src"), - V = parse_semver(proplists:get_value(vsn, Props)), - ?assert(TagV =< V). - -ensure_changelog_test() -> - {ok, [{application, ehttpc, Props}]} = file:consult("src/ehttpc.app.src"), - Vsn = proplists:get_value(vsn, Props), - ExpectedChangeLogLine = "## " ++ Vsn, - {ok, Text} = file:read_file("changelog.md"), - Lines = binary:split(Text, <<"\n">>, [global]), - case lists:member(iolist_to_binary(ExpectedChangeLogLine), Lines) of - true -> ok; - false -> error({missing_changelog_for_vsn, ExpectedChangeLogLine}) - end. - -ensure_git_tag_test() -> - Latest = lists:last(lists:sort([parse_semver(T) || T <- all_tags()])), - LatestTag = format_semver(Latest), - ChangedFiles = os:cmd("git diff --name-only " ++ LatestTag ++ "...HEAD src"), - case ChangedFiles of - [] -> - ok; - Other -> - %% only print a warning message - %% because we can not control git tags in eunit - io:format( - user, - "################################~n" - "changed since ~s:~n~p~n", - [LatestTag, Other] - ), - ok - end. - -format_semver({Major, Minor, Patch}) -> - lists:flatten(io_lib:format("~p.~p.~p", [Major, Minor, Patch])). - -parse_semver(Str) -> - [Major, Minor, Patch | _] = string:tokens(Str, ".-\n"), - {list_to_integer(Major), list_to_integer(Minor), list_to_integer(Patch)}. - -all_tags() -> - string:tokens(os:cmd("git tag"), "\n"). - -git_tag_match_upgrade_vsn_test_() -> - {ok, [{AppupVsn, Upgraes, Downgrades}]} = file:consult("src/ehttpc.appup.src"), - [ - {Tag, fun() -> true = has_a_match(Tag, Upgraes, Downgrades) end} - || Tag <- all_tags(), Tag =/= AppupVsn - ]. - -has_a_match(Tag, Ups, Downs) -> - has_a_match(Tag, Ups) andalso - has_a_match(Tag, Downs). - -has_a_match(_, []) -> - false; -has_a_match(Tag, [{Tag, _} | _]) -> - true; -has_a_match(Tag, [{Tag1, _} | Rest]) when is_list(Tag1) -> - has_a_match(Tag, Rest); -has_a_match(Tag, [{Re, _} | Rest]) when is_binary(Re) -> - case re:run(Tag, Re, [unicode, {capture, first, list}]) of - {match, [Tag]} -> true; - _ -> has_a_match(Tag, Rest) - end. - -%% NOTE: the git tag 0.1.0 was re-tagged -upgrade_from_test_() -> - Old1 = #state_0_1_0{mref = make_ref()}, - Old2 = #state_0_1_7{mref = make_ref(), requests = #{}}, - Old3 = #state_0_1_8{mref = make_ref(), requests = #{}}, - F = fun({Old, Extra}) -> - {ok, New} = ehttpc:code_change("old", new_tag(Old), []), - #{requests := Requests} = ehttpc:format_state(New, normal), - ?assertMatch( - #{ - pending := _, - pending_count := 0, - sent := #{} - }, - Requests - ), - {ok, Down} = ehttpc:code_change({down, "new"}, New, Extra), - OldTag = element(1, Old), - ?assertEqual(Old, old_tag(OldTag, Down)) - end, - [ - {atom_to_list(element(1, Old)), fun() -> F({Old, Extra}) end} - || {Old, Extra} <- [ - {Old1, [no_requests]}, - {Old2, [no_enable_pipelining]}, - {Old3, [downgrade_requests]} - ] - ]. - -old_tag(Tag, State) -> - setelement(1, State, Tag). - -new_tag(State) -> - setelement(1, State, state). - -upgrade_requests_test() -> - Old = #{foo => bar}, - New = ehttpc:upgrade_requests(Old), - ?assertMatch( - #{ - sent := Old, - pending := _, - pending_count := 0 - }, - New - ), - ?assertMatch(Old, ehttpc:downgrade_requests(New)).