%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2005-2016. 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. %% %% %CopyrightEnd% %% %% -module(httpc_cookie_SUITE). -include_lib("common_test/include/ct.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). -define(URL, "http://myhost.cookie.test.org"). -define(URL_DOMAIN, "http://myhost2.cookie.test.org"). -define(URL_SECURE, "https://myhost.cookie.test.org"). %% Test server callback functions suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{seconds,15}} ]. all() -> [ session_cookies_only, netscape_cookies, cookie_cancel, cookie_expires, persistent_cookie, domain_cookie, secure_cookie, update_cookie, update_cookie_session, cookie_attributes ]. groups() -> []. init_per_suite(Config) -> Config. end_per_suite(_Config) -> ok. init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. init_per_testcase(session_cookies_only = Case, Config0) -> Config = init_workdir(Case, Config0), application:start(inets), httpc:set_options([{cookies, verify}]), Config; init_per_testcase(cookie_expires = Case, Config0) -> Config = init_workdir(Case, Config0), CaseDir = proplists:get_value(case_top_dir, Config), application:start(inets), application:set_env(inets, services, [{httpc, {default, CaseDir}}]), application:start(inets), httpc:set_options([{cookies, verify}]), Config; init_per_testcase(Case, Config0) -> Config = init_workdir(Case, Config0), CaseDir = proplists:get_value(case_top_dir, Config), application:load(inets), application:set_env(inets, services, [{httpc, {default, CaseDir}}]), application:start(inets), httpc:set_options([{cookies, verify}]), Config. init_workdir(Case, Config) -> PrivDir = proplists:get_value(priv_dir, Config), SuiteTopDir = filename:join(PrivDir, ?MODULE), case file:make_dir(SuiteTopDir) of ok -> ok; {error, eexist} -> ok; Error -> ct:fail({failed_creating_subsuite_top_dir, Error}) end, CaseTopDir = filename:join(SuiteTopDir, Case), ok = file:make_dir(CaseTopDir), [{suite_top_dir, SuiteTopDir}, {case_top_dir, CaseTopDir} | Config]. end_per_testcase(_, _) -> application:stop(inets). %% Test cases starts here. %%-------------------------------------------------------------------- session_cookies_only() -> [{doc, "Test that all cookies are handled as session cookies if there" "does not exist a directory to save presitent cookies in."}]. session_cookies_only(Config) when is_list(Config) -> SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" ";max-age=60000"}], httpc:store_cookies(SetCookieHeaders, ?URL), {"cookie", "$Version=0; test_cookie=true; $Path=/"} = httpc:cookie_header(?URL), application:stop(inets), application:start(inets), {"cookie", ""} = httpc:cookie_header(?URL). netscape_cookies() -> [{doc, "Test that the old (original) format of cookies are accepted."}]. netscape_cookies(Config) when is_list(Config) -> Expires = future_netscape_date(), SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/; " "expires=" ++ Expires}], httpc:store_cookies(SetCookieHeaders, ?URL), {"cookie", "$Version=0; test_cookie=true; $Path=/"} = httpc:cookie_header(?URL). cookie_cancel() -> [{doc, "A cookie can be canceld by sending the same cookie with max-age=0 " "this test cheks that cookie is canceled."}]. cookie_cancel(Config) when is_list(Config) -> SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" "max-age=60000"}], httpc:store_cookies(SetCookieHeaders, ?URL), {"cookie", "$Version=0; test_cookie=true; $Path=/"} = httpc:cookie_header(?URL), NewSetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;max-age=0"}], httpc:store_cookies(NewSetCookieHeaders, ?URL), {"cookie", ""} = httpc:cookie_header(?URL). cookie_expires() -> [{doc, "Test that a cookie is not used when it has expired"}]. cookie_expires(Config) when is_list(Config) -> SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" "max-age=5"}], httpc:store_cookies(SetCookieHeaders, ?URL), {"cookie", "$Version=0; test_cookie=true; $Path=/"} = httpc:cookie_header(?URL), timer:sleep(10000), {"cookie", ""} = httpc:cookie_header(?URL). persistent_cookie() -> [{doc, "Test domian cookie attribute"}]. persistent_cookie(Config) when is_list(Config)-> SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" "max-age=60000"}], httpc:store_cookies(SetCookieHeaders, ?URL), {"cookie", "$Version=0; test_cookie=true; $Path=/"} = httpc:cookie_header(?URL), CaseDir = proplists:get_value(case_top_dir, Config), application:stop(inets), application:load(inets), application:set_env(inets, services, [{httpc, {default, CaseDir}}]), application:start(inets), httpc:set_options([{cookies, enabled}]), {"cookie","$Version=0; test_cookie=true; $Path=/"} = httpc:cookie_header(?URL). domain_cookie() -> [{doc, "Test the domian cookie attribute"}]. domain_cookie(Config) when is_list(Config) -> SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" "domain=.cookie.test.org"}], httpc:store_cookies(SetCookieHeaders, ?URL), {"cookie","$Version=0; test_cookie=true; $Path=/; " "$Domain=.cookie.test.org"} = httpc:cookie_header(?URL_DOMAIN). secure_cookie() -> [{doc, "Test the secure cookie attribute"}]. secure_cookie(Config) when is_list(Config) -> SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/; secure"}], ok = httpc:store_cookies(SetCookieHeaders, ?URL), check_cookie("$Version=0; test_cookie=true; $Path=/", ?URL_SECURE), check_cookie("", ?URL), SetCookieHeaders1 = [{"set-cookie", "test1_cookie=true; path=/; secure"}], ok = httpc:store_cookies(SetCookieHeaders1, ?URL), check_cookie("$Version=0; test_cookie=true; $Path=/; " "test1_cookie=true; $Path=/", ?URL_SECURE). update_cookie()-> [{doc, "Test that a (plain) cookie can be updated."}]. update_cookie(Config) when is_list(Config) -> print_cookies("Cookies before store"), SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/; max-age=6500"}, {"set-cookie", "test_cookie2=true; path=/; max-age=6500"}], httpc:store_cookies(SetCookieHeaders, ?URL), print_cookies("Cookies after first store"), ExpectCookie1 = "$Version=0; " "test_cookie=true; $Path=/; " "test_cookie2=true; $Path=/", expect_cookie_header(1, ExpectCookie1), NewSetCookieHeaders = [{"set-cookie", "test_cookie=false; path=/; max-age=6500"}], httpc:store_cookies(NewSetCookieHeaders, ?URL), print_cookies("Cookies after second store"), ExpectCookie2 = "$Version=0; " "test_cookie2=true; $Path=/; " "test_cookie=false; $Path=/", expect_cookie_header(2, ExpectCookie2). update_cookie_session()-> [{doc, "Test that a session cookie can be updated."}]. update_cookie_session(Config) when is_list(Config)-> print_cookies("Cookies before store"), SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/"}, {"set-cookie", "test_cookie2=true; path=/"}], httpc:store_cookies(SetCookieHeaders, ?URL), print_cookies("Cookies after first store"), ExpectedCookie1 = "$Version=0; test_cookie=true; $Path=/; test_cookie2=true; $Path=/", expect_cookie_header(1, ExpectedCookie1), NewSetCookieHeaders = [{"set-cookie", "test_cookie=false; path=/"}], httpc:store_cookies(NewSetCookieHeaders, ?URL), print_cookies("Cookies after second store"), ExpectedCookie2 = "$Version=0; test_cookie2=true; $Path=/; test_cookie=false; $Path=/", expect_cookie_header(2, ExpectedCookie2). cookie_attributes() -> [{doc, "Test attribute not covered by the other test cases"}]. cookie_attributes(Config) when is_list(Config) -> SetCookieHeaders = [{"set-cookie", "test_cookie=true;version=1;" "comment=foobar; "%% Comment "foo=bar;" %% Nonsense should be ignored "max-age=60000"}], httpc:store_cookies(SetCookieHeaders, ?URL), {"cookie","$Version=1; test_cookie=true"} = httpc:cookie_header(?URL). %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- print_cookies(Pre) -> io:format("~s: ", [Pre]), print_cookies2(httpc:which_cookies()). print_cookies2([]) -> ok; print_cookies2([{cookies, Cookies}|Rest]) -> print_cookies3("Cookies", Cookies), print_cookies2(Rest); print_cookies2([{session_cookies, Cookies}|Rest]) -> print_cookies3("Session Cookies", Cookies), print_cookies2(Rest); print_cookies2([_|Rest]) -> print_cookies2(Rest). print_cookies3(Header, []) -> io:format(" ~s: []", [Header]); print_cookies3(Header, Cookies) -> io:format(" ~s: ", [Header]), Prefix = " ", PrintCookie = fun(Cookie) -> io:format("~s", [httpc_cookie:image_of(Prefix, Cookie)]) end, lists:foreach(PrintCookie, Cookies). expect_cookie_header(No, ExpectedCookie) -> case httpc:cookie_header(?URL) of {"cookie", ExpectedCookie} -> ok; {"cookie", BadCookie} -> io:format("Bad Cookie ~w: " "~n Expected: ~s" "~n Received: ~s" "~n", [No, ExpectedCookie, BadCookie]), exit({bad_cookie_header, No, ExpectedCookie, BadCookie}) end. check_cookie(Expect, URL) -> case httpc:cookie_header(URL) of {"cookie", Expect} -> ok; {"cookie", Unexpected} -> case lists:prefix(Expect, Unexpected) of true -> Extra = Unexpected -- Expect, ct:fail({extra_cookie_info, Extra}); false -> ct:fail({unknown_cookie, Expect, Unexpected}) end; Bad -> ct:fail({bad_cookies, Bad}) end. future_netscape_date() -> [Day, DD, Mon, YYYY] = netscape_date(date()), lists:flatten(io_lib:format("~s, ~s ~s ~s 12:30:00 GMT", [Day, DD, Mon, YYYY])). netscape_date({Year, 12, _}) -> NewYear = Year + 1, NewMonth = 1, NewDay = calendar:last_day_of_the_month(NewYear, NewMonth), WeekDay = calendar:day_of_the_week(NewYear, NewMonth, NewDay), WeekDayNrStr = day_nr_str(NewDay), NewDayStr = week_day_str(WeekDay), MonthStr = month_str(NewMonth), [NewDayStr, WeekDayNrStr, MonthStr, integer_to_list(NewYear)]; netscape_date({Year, Month, _}) -> NewMonth = Month + 1, NewDay = calendar:last_day_of_the_month(Year, NewMonth), WeekDay = calendar:day_of_the_week(Year, NewMonth, NewDay), WeekDayNrStr = day_nr_str(NewDay), NewDayStr = week_day_str(WeekDay), MonthStr = month_str(NewMonth), [NewDayStr, WeekDayNrStr, MonthStr, integer_to_list(Year)]. week_day_str(1) -> "Mon"; week_day_str(2) -> "Tus"; week_day_str(3) -> "Wdy"; week_day_str(4) -> "Thu"; week_day_str(5) -> "Fri"; week_day_str(6) -> "Sat"; week_day_str(7) -> "Sun". day_nr_str(1) -> "01"; day_nr_str(2) -> "02"; day_nr_str(3) -> "03"; day_nr_str(4) -> "04"; day_nr_str(5) -> "05"; day_nr_str(6) -> "06"; day_nr_str(7) -> "07"; day_nr_str(8) -> "08"; day_nr_str(0) -> "09"; day_nr_str(N) -> integer_to_list(N). month_str(1) ->"Jan"; month_str(2) ->"Feb"; month_str(3) ->"Mar"; month_str(4) ->"Apr"; month_str(5) ->"May"; month_str(6) ->"Jun"; month_str(7) ->"Jul"; month_str(8) ->"Aug"; month_str(9) ->"Sep"; month_str(10) ->"Oct"; month_str(11) ->"Nov"; month_str(12) ->"Dec".