From 1577983b9b3883b74e3e460ed4f8f6916ffaa3a5 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 26 Oct 2011 12:20:29 +0200 Subject: Fixed hex-decoding. OTP-9655 --- lib/inets/src/http_lib/http_uri.erl | 33 +++++++++--- .../src/http_server/httpd_request_handler.erl | 4 +- lib/inets/src/http_server/httpd_response.erl | 16 ++++-- lib/inets/src/http_server/httpd_util.erl | 2 +- lib/inets/src/inets_app/inets.appup.src | 14 +++--- lib/inets/test/httpc_SUITE.erl | 27 ++++------ lib/inets/test/httpd_SUITE.erl | 4 +- lib/inets/test/httpd_test_lib.erl | 58 +++++++++++++++------- 8 files changed, 101 insertions(+), 57 deletions(-) diff --git a/lib/inets/src/http_lib/http_uri.erl b/lib/inets/src/http_lib/http_uri.erl index 3804af60f4..b470fd0b46 100644 --- a/lib/inets/src/http_lib/http_uri.erl +++ b/lib/inets/src/http_lib/http_uri.erl @@ -21,7 +21,8 @@ -module(http_uri). -export([parse/1]). --export([parse/1, encode/1, decode/1]). +-export([encode/1, decode/1]). + %%%========================================================================= %%% API @@ -45,16 +46,33 @@ encode(URI) -> $\\, $', $^, $%, $ ]), lists:append(lists:map(fun(Char) -> uri_encode(Char, Reserved) end, URI)). -decode([$%,Hex1,Hex2|Rest]) -> - [hex2dec(Hex1)*16+hex2dec(Hex2)|decode(Rest)]; -decode([First|Rest]) -> - [First|decode(Rest)]; -decode([]) -> +decode(String) -> + try + begin + do_decode(String) + end + catch + throw:{bad_hex_value, _BadChar} -> + %% The string is either badly encoded or a string + %% containing a % followed by a non-hex char. + %% In any case, return as-is since there is nothing + %% we can do... + %% Note that the valid hex-chars are: 0-9, a-f and A-F. + String + end. + +do_decode([$%,Hex1,Hex2|Rest]) -> + [hex2dec(Hex1)*16+hex2dec(Hex2)|do_decode(Rest)]; +do_decode([First|Rest]) -> + [First|do_decode(Rest)]; +do_decode([]) -> []. + %%%======================================================================== %%% Internal functions %%%======================================================================== + parse_scheme(AbsURI) -> case split_uri(AbsURI, ":", {error, no_scheme}, 1, 1) of {error, no_scheme} -> @@ -138,4 +156,5 @@ uri_encode(Char, Reserved) -> hex2dec(X) when (X>=$0) andalso (X=<$9) -> X-$0; hex2dec(X) when (X>=$A) andalso (X=<$F) -> X-$A+10; -hex2dec(X) when (X>=$a) andalso (X=<$f) -> X-$a+10. +hex2dec(X) when (X>=$a) andalso (X=<$f) -> X-$a+10; +hex2dec(X) -> throw({bad_hex_value, X}). diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index fa832cba3f..1bf1b20b5b 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -343,7 +343,7 @@ handle_http_msg({Method, Uri, Version, {RecordHeaders, Headers}, Body}, Reason = io_lib:format("Forbidden URI: ~p~n", [URI]), error_log(Reason, ModData), {stop, normal, State#state{response_sent = true}}; - {error,{bad_request, {malformed_syntax, URI}}} -> + {error, {bad_request, {malformed_syntax, URI}}} -> ?hdrd("validation failed: bad request - malformed syntax", [{uri, URI}]), httpd_response:send_status(ModData#mod{http_version = Version}, diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl index ea9cfbf4f2..1301f27081 100644 --- a/lib/inets/src/http_server/httpd_response.erl +++ b/lib/inets/src/http_server/httpd_response.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -100,12 +100,19 @@ send_status(#mod{socket_type = SocketType, socket = Socket, config_db = ConfigDB} = ModData, StatusCode, PhraseArgs) -> + ?hdrd("send status", [{status_code, StatusCode}, + {phrase_args, PhraseArgs}]), + ReasonPhrase = httpd_util:reason_phrase(StatusCode), Message = httpd_util:message(StatusCode, PhraseArgs, ConfigDB), Body = get_body(ReasonPhrase, Message), - send_header(ModData, StatusCode, [{content_type, "text/html"}, - {content_length, integer_to_list(length(Body))}]), + ?hdrt("send status - header", [{reason_phrase, ReasonPhrase}, + {message, Message}]), + send_header(ModData, StatusCode, + [{content_type, "text/html"}, + {content_length, integer_to_list(length(Body))}]), + httpd_socket:deliver(SocketType, Socket, Body). @@ -345,8 +352,9 @@ transform({Field, Value}) when is_list(Field) -> %% Leave this method and go on to the newer form of response %% OTP-4408 %%---------------------------------------------------------------------- -send_response_old(#mod{method = "HEAD"} = ModData, +send_response_old(#mod{method = "HEAD"} = ModData, StatusCode, Response) -> + NewResponse = lists:flatten(Response), case httpd_util:split(NewResponse, [?CR, ?LF, ?CR, ?LF],2) of diff --git a/lib/inets/src/http_server/httpd_util.erl b/lib/inets/src/http_server/httpd_util.erl index 2e0752bcc0..366843354e 100644 --- a/lib/inets/src/http_server/httpd_util.erl +++ b/lib/inets/src/http_server/httpd_util.erl @@ -183,7 +183,7 @@ message(400, none, _) -> "Your browser sent a query that this server could not understand. "; message(400, Msg, _) -> "Your browser sent a query that this server could not understand. " ++ - html_encode(http_uri:decode(Msg)); + html_encode(Msg); message(401, none, _) -> "This server could not verify that you are authorized to access the document you diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index ea696b9155..528a3601a4 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -20,9 +20,10 @@ [ {"5.3.5", [ - {load_module, http_util, soft_purge, soft_purge, []}, - {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, - {load_module, httpd_file, soft_purge, soft_purge, []} + {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, + {load_module, httpd_file, soft_purge, soft_purge, []}, + {load_module, httpd_response, soft_purge, soft_purge, []} ] }, {"5.3.4", @@ -54,9 +55,10 @@ [ {"5.3.5", [ - {load_module, http_util, soft_purge, soft_purge, []}, - {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, - {load_module, httpd_file, soft_purge, soft_purge, []} + {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, + {load_module, httpd_file, soft_purge, soft_purge, []}, + {load_module, httpd_response, soft_purge, soft_purge, []} ] }, {"5.3.4", diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index c52e3d25cd..3a1f4cc83d 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -197,9 +197,7 @@ init_per_testcase(Case, Timeout, Config) -> TmpConfig2 = lists:keydelete(local_ssl_server, 1, TmpConfig), %% Will start inets - Server = - inets_test_lib:start_http_server( - filename:join(PrivDir, SslConfFile)), + Server = start_http_server(PrivDir, SslConfFile), [{watchdog, Dog}, {local_ssl_server, Server} | TmpConfig2]; "proxy_" ++ Rest -> case Rest of @@ -209,9 +207,8 @@ init_per_testcase(Case, Timeout, Config) -> ok -> [{watchdog, Dog} | TmpConfig]; _ -> - [{skip, - "SSL does not seem to be supported"} - | TmpConfig] + [skip("SSL does not seem to be supported") | + TmpConfig] end; _ -> %% We use erlang.org for the proxy tests @@ -239,23 +236,20 @@ init_per_testcase(Case, Timeout, Config) -> ], case lists:member(Rest, BadCases) of true -> - [{skip, - "TC and server not compatible"}| + [skip("TC and server not compatible") | TmpConfig]; false -> inets:start(), [{watchdog, Dog} | TmpConfig] end; false -> - [{skip, "proxy not responding"} | TmpConfig] + [skip("proxy not responding") | TmpConfig] end end; _ -> TmpConfig2 = lists:keydelete(local_server, 1, TmpConfig), - Server = - %% Will start inets - inets_test_lib:start_http_server( - filename:join(PrivDir, IpConfFile)), + %% Will start inets + Server = start_http_server(PrivDir, IpConfFile), [{watchdog, Dog}, {local_server, Server} | TmpConfig2] end, @@ -264,11 +258,12 @@ init_per_testcase(Case, Timeout, Config) -> inets:enable_trace(max, io, httpc), %% inets:enable_trace(max, io, all), %% snmp:set_trace([gen_tcp, inet_tcp, prim_inet]), - io:format(user, "~n~n*** INIT ~w:~w ***~n" - " NewConfig: ~p~n~n", - [?MODULE, Case, NewConfig]), NewConfig. +start_http_server(ConfDir, ConfFile) -> + inets_test_lib:start_http_server( filename:join(ConfDir, ConfFile) ). + + %%-------------------------------------------------------------------- %% Function: end_per_testcase(Case, Config) -> _ %% Case - atom() diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index 7403d4a643..7d95cdbebd 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -1627,7 +1627,7 @@ ticket_6003(Config) -> ?IP_PORT, ?config(node, Config), "GET http://www.erlang.org/%skalle " "HTTP/1.0\r\n\r\n", - [{statuscode, 400}, + [{statuscode, 404}, {version, "HTTP/1.0"}]), ok. diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl index 6abee5be2c..becb54e479 100644 --- a/lib/inets/test/httpd_test_lib.erl +++ b/lib/inets/test/httpd_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -80,7 +80,9 @@ verify_request(SocketType, Host, Port, Node, RequestStr, Options) -> verify_request(SocketType, Host, Port, Node, RequestStr, Options, 30000). verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut) -> + tsp("verify_request -> connect to [~w] ~p:~w", [SocketType, Host, Port]), {ok, Socket} = inets_test_lib:connect_bin(SocketType, Host, Port), + inets_test_lib:send(SocketType, Socket, RequestStr), State = case inets_regexp:match(RequestStr, "printenv") of @@ -200,10 +202,9 @@ handle_http_body(Body, State = #state{headers = Headers, end. validate(RequestStr, #state{status_line = {Version, StatusCode, _}, - headers = Headers, - body = Body}, Options, N, P) -> - - %io:format("Status~p: H:~p B:~p~n", [StatusCode, Headers, Body]), + headers = Headers, + body = Body}, Options, N, P) -> + check_version(Version, Options), case lists:keysearch(statuscode, 1, Options) of {value, _} -> @@ -217,6 +218,7 @@ validate(RequestStr, #state{status_line = {Version, StatusCode, _}, list_to_integer(Headers#http_response_h.'content-length'), Body). + %%-------------------------------------------------------------------- %% Internal functions %%------------------------------------------------------------------ @@ -225,21 +227,20 @@ check_version(Version, Options) -> {value, {version, Version}} -> ok; {value, {version, Ver}} -> - test_server:fail({wrong_version, [{got, Version}, - {expected, Ver}]}); + tsf({wrong_version, [{got, Version}, + {expected, Ver}]}); _ -> case Version of "HTTP/1.1" -> ok; _ -> - test_server:fail({wrong_version, [{got, Version}, - {expected, "HTTP/1.1"}]}) + tsf({wrong_version, [{got, Version}, + {expected, "HTTP/1.1"}]}) end end. check_status_code(StatusCode, [], Options) -> - test_server:fail({wrong_status_code, [{got, StatusCode}, - {expected, Options}]}); + tsf({wrong_status_code, [{got, StatusCode}, {expected, Options}]}); check_status_code(StatusCode, Current = [_ | Rest], Options) -> case lists:keysearch(statuscode, 1, Current) of {value, {statuscode, StatusCode}} -> @@ -247,8 +248,7 @@ check_status_code(StatusCode, Current = [_ | Rest], Options) -> {value, {statuscode, _OtherStatus}} -> check_status_code(StatusCode, Rest, Options); false -> - test_server:fail({wrong_status_code, [{got, StatusCode}, - {expected, Options}]}) + tsf({wrong_status_code, [{got, StatusCode}, {expected, Options}]}) end. do_validate(_, [], _, _) -> @@ -279,8 +279,7 @@ do_validate(Header, [{header, HeaderField, Value}|Rest],N,P) -> Header}) end, do_validate(Header, Rest, N, P); -do_validate(Header,[{no_last_modified,HeaderField}|Rest],N,P) -> -% io:format("Header: ~p~nHeaderField: ~p~n",[Header,HeaderField]), +do_validate(Header,[{no_last_modified, HeaderField}|Rest],N,P) -> case lists:keysearch(HeaderField,1,Header) of {value,_} -> test_server:fail({wrong_header_field_value, HeaderField, @@ -293,7 +292,6 @@ do_validate(Header, [_Unknown | Rest], N, P) -> do_validate(Header, Rest, N, P). is_expect(RequestStr) -> - case inets_regexp:match(RequestStr, "xpect:100-continue") of {match, _, _}-> true; @@ -302,15 +300,15 @@ is_expect(RequestStr) -> end. %% OTP-5775, content-length -check_body("GET /cgi-bin/erl/httpd_example:get_bin HTTP/1.0\r\n\r\n", 200, "text/html", Length, _Body) when Length /= 274-> - test_server:fail(content_length_error); +check_body("GET /cgi-bin/erl/httpd_example:get_bin HTTP/1.0\r\n\r\n", 200, "text/html", Length, _Body) when (Length =/= 274) -> + tsf(content_length_error); check_body("GET /cgi-bin/cgi_echo HTTP/1.0\r\n\r\n", 200, "text/plain", _, Body) -> case size(Body) of 100 -> ok; _ -> - test_server:fail(content_length_error) + tsf(content_length_error) end; check_body(RequestStr, 200, "text/html", _, Body) -> @@ -330,3 +328,25 @@ print(Proto, Data, #state{print = true}) -> print(_, _, #state{print = false}) -> ok. +tsf(Reason) -> + test_server:fail(Reason). + +%% tsp(F) -> +%% tsp(F, []). +tsp(F, A) -> + Timestamp = formated_timestamp(), + test_server:format("** ~s ** ~p ~p:" ++ F ++ "~n", + [Timestamp, self(), ?MODULE | A]). + +formated_timestamp() -> + format_timestamp( os:timestamp() ). + +format_timestamp({_N1, _N2, N3} = Now) -> + {Date, Time} = calendar:now_to_datetime(Now), + {YYYY,MM,DD} = Date, + {Hour,Min,Sec} = Time, + FormatDate = + io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", + [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), + lists:flatten(FormatDate). + -- cgit v1.2.3