From 77acb473d8f056f6f534395f131c6e45693797f0 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Wed, 4 Nov 2015 15:14:21 +0100 Subject: inets: Terminate gracfully when an invalid chunked length header is encountered Also use integer_to_list/2 and list_to_integer/2 instead of reimplementing it. --- lib/inets/src/http_lib/http_chunk.erl | 8 ++- lib/inets/src/http_lib/http_util.erl | 69 ++-------------------- .../src/http_server/httpd_request_handler.erl | 10 +++- 3 files changed, 18 insertions(+), 69 deletions(-) (limited to 'lib/inets') diff --git a/lib/inets/src/http_lib/http_chunk.erl b/lib/inets/src/http_lib/http_chunk.erl index 9476ea9f5f..c17ff6cce5 100644 --- a/lib/inets/src/http_lib/http_chunk.erl +++ b/lib/inets/src/http_lib/http_chunk.erl @@ -143,20 +143,22 @@ decode_size(Data = <>, HexList, {MaxBodySize, Body, AccLength, MaxHeaderSize}) -> - ChunkSize = http_util:hexlist_to_integer(lists:reverse(HexList)), - case ChunkSize of + try http_util:hexlist_to_integer(lists:reverse(HexList)) of 0 -> % Last chunk, there was no data ignore_extensions(Data, {?MODULE, decode_trailer, [<<>>, [],[], MaxHeaderSize, Body, integer_to_list(AccLength)]}); - _ -> + ChunkSize -> %% Note decode_data may call decode_size again if there %% is more than one chunk, hence here is where the last parameter %% to this function comes in. decode_data(ChunkSize, ChunkRest, {MaxBodySize, Body, ChunkSize + AccLength , MaxHeaderSize}) + catch + _:_ -> + throw({error, {chunk_size, HexList}}) end; decode_size(<<";", Rest/binary>>, HexList, Info) -> %% Note ignore_extensions will call decode_size/1 again when diff --git a/lib/inets/src/http_lib/http_util.erl b/lib/inets/src/http_lib/http_util.erl index 0d07231302..aafa97afee 100644 --- a/lib/inets/src/http_lib/http_util.erl +++ b/lib/inets/src/http_lib/http_util.erl @@ -152,27 +152,11 @@ convert_netscapecookie_date([_D,_A,_Y, _SP, Sec=list_to_integer([S1,S2]), {{Year,Month,Day},{Hour,Min,Sec}}. -hexlist_to_integer([]) -> - empty; -%%When the string only contains one value its eaasy done. -%% 0-9 -hexlist_to_integer([Size]) when (Size >= 48) andalso (Size =< 57) -> - Size - 48; -%% A-F -hexlist_to_integer([Size]) when (Size >= 65) andalso (Size =< 70) -> - Size - 55; -%% a-f -hexlist_to_integer([Size]) when (Size >= 97) andalso (Size =< 102) -> - Size - 87; -hexlist_to_integer([_Size]) -> - not_a_num; +hexlist_to_integer(List) -> + list_to_integer(List, 16). -hexlist_to_integer(Size) -> - Len = string:span(Size, "1234567890abcdefABCDEF"), - hexlist_to_integer2(Size, 16 bsl (4 *(Len-2)),0). - -integer_to_hexlist(Num)-> - integer_to_hexlist(Num, get_size(Num), []). +integer_to_hexlist(Int) -> + integer_to_list(Int, 16). convert_month("Jan") -> 1; convert_month("Feb") -> 2; @@ -213,51 +197,6 @@ html_encode(Chars) -> %%%======================================================================== %%% Internal functions %%%======================================================================== -hexlist_to_integer2([],_Pos,Sum)-> - Sum; -hexlist_to_integer2([HexVal | HexString], Pos, Sum) - when HexVal >= 48, HexVal =< 57 -> - hexlist_to_integer2(HexString, Pos bsr 4, Sum + ((HexVal-48) * Pos)); - -hexlist_to_integer2([HexVal | HexString], Pos, Sum) - when HexVal >= 65, HexVal =<70 -> - hexlist_to_integer2(HexString, Pos bsr 4, Sum + ((HexVal-55) * Pos)); - -hexlist_to_integer2([HexVal | HexString], Pos, Sum) - when HexVal>=97, HexVal=<102 -> - hexlist_to_integer2(HexString, Pos bsr 4, Sum + ((HexVal-87) * Pos)); - -hexlist_to_integer2(_AfterHexString, _Pos, Sum)-> - Sum. - -integer_to_hexlist(Num, Pot, Res) when Pot < 0 -> - convert_to_ascii([Num | Res]); - -integer_to_hexlist(Num,Pot,Res) -> - Position = (16 bsl (Pot*4)), - PosVal = Num div Position, - integer_to_hexlist(Num - (PosVal*Position), Pot-1, [PosVal | Res]). - -get_size(Num)-> - get_size(Num, 0). - -get_size(Num, Pot) when Num < (16 bsl(Pot *4)) -> - Pot-1; - -get_size(Num, Pot) -> - get_size(Num, Pot+1). - -convert_to_ascii(RevesedNum) -> - convert_to_ascii(RevesedNum, []). - -convert_to_ascii([], Num)-> - Num; -convert_to_ascii([Num | Reversed], Number) - when (Num > -1) andalso (Num < 10) -> - convert_to_ascii(Reversed, [Num + 48 | Number]); -convert_to_ascii([Num | Reversed], Number) - when (Num > 9) andalso (Num < 16) -> - convert_to_ascii(Reversed, [Num + 55 | Number]). char_to_html_entity(Char, Reserved) -> case sets:is_element(Char, Reserved) of diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index e5d006c1fd..143d599edb 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -443,7 +443,7 @@ handle_body(#state{headers = Headers, body = Body, mod = ModData} = State, MaxHeaderSize, MaxBodySize) -> case Headers#http_request_h.'transfer-encoding' of "chunked" -> - case http_chunk:decode(Body, MaxBodySize, MaxHeaderSize) of + try http_chunk:decode(Body, MaxBodySize, MaxHeaderSize) of {Module, Function, Args} -> http_transport:setopts(ModData#mod.socket_type, ModData#mod.socket, @@ -455,6 +455,14 @@ handle_body(#state{headers = Headers, body = Body, mod = ModData} = State, http_chunk:handle_headers(Headers, ChunkedHeaders), handle_response(State#state{headers = NewHeaders, body = NewBody}) + catch + throw:Error -> + httpd_response:send_status(ModData, 400, + "Bad input"), + Reason = io_lib:format("Chunk decoding failed: ~p~n", + [Error]), + error_log(Reason, ModData), + {stop, normal, State#state{response_sent = true}} end; Encoding when is_list(Encoding) -> httpd_response:send_status(ModData, 501, -- cgit v1.2.3 From 6848ea3aa947ea8862e939af4f794700ebbcfed3 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Thu, 5 Nov 2015 15:56:03 +0100 Subject: inets: CT'ify http_format_SUITE --- lib/inets/test/http_format_SUITE.erl | 155 +++++++++++------------------------ 1 file changed, 46 insertions(+), 109 deletions(-) (limited to 'lib/inets') diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl index a97b51601f..83ffe259b3 100644 --- a/lib/inets/test/http_format_SUITE.erl +++ b/lib/inets/test/http_format_SUITE.erl @@ -20,26 +20,12 @@ %% -module(http_format_SUITE). --author('ingela@erix.ericsson.se'). -include_lib("common_test/include/ct.hrl"). --include("test_server_line.hrl"). -include("http_internal.hrl"). -%% Test server specific exports --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]). - -%% Test cases must be exported. --export([ chunk_decode/1, chunk_encode/1, - chunk_extensions_otp_6005/1, chunk_decode_otp_6264/1, - chunk_decode_empty_chunk_otp_6511/1, - chunk_decode_trailer/1, - http_response/1, http_request/1, validate_request_line/1, - esi_parse_headers/1, cgi_parse_headers/1, - is_absolut_uri/1, convert_netscapecookie_date/1, - check_content_length_encoding/1]). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +%% Note: This directive should only be used in test suites. +-compile(export_all). all() -> [{group, chunk}, http_response, http_request, @@ -81,12 +67,8 @@ end_per_testcase(_, Config) -> %% Test cases starts here. %%------------------------------------------------------------------------- - -%%------------------------------------------------------------------------- -chunk_decode(doc) -> - ["Test http_chunk:decode/3"]; -chunk_decode(suite) -> - []; +chunk_decode() -> + [{doc, "Test http_chunk:decode/3"}]. chunk_decode(Config) when is_list(Config) -> ReqHeaders = #http_request_h{'transfer-encoding' = "chunked"}, ChunkedBody = "A" ++ ?CRLF ++ "1234567890" ++ ?CRLF ++ "4" ++ @@ -114,10 +96,8 @@ chunk_decode(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -chunk_extensions_otp_6005(doc) -> - ["Make sure so called extensions are ignored"]; -chunk_extensions_otp_6005(suite) -> - []; +chunk_extensions_otp_6005() -> + [{doc, "Make sure so called extensions are ignored"}]. chunk_extensions_otp_6005(Config) when is_list(Config)-> ChunkedBody = "A;ignore this" ++ ?CRLF ++ "1234567890" ++ ?CRLF ++ "4" ++ ?CRLF ++ "HEJ!"++ ?CRLF ++ "0" ++ @@ -136,14 +116,11 @@ chunk_extensions_otp_6005(Config) when is_list(Config)-> ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), {_, NewBody} = parse(Module1, Function1, Args1, tl(ChunkedBody1)), - "1234567890HEJ!" = binary_to_list(NewBody), - ok. + "1234567890HEJ!" = binary_to_list(NewBody). %%------------------------------------------------------------------------- -chunk_decode_otp_6264(doc) -> - ["Check that 0 in the body does not count as the last chunk"]; -chunk_decode_otp_6264(suite) -> - []; +chunk_decode_otp_6264() -> + [{doc, "Check that 0 in the body does not count as the last chunk"}]. chunk_decode_otp_6264(Config) when is_list(Config)-> ChunkedBody = "A;ignore this" ++ ?CRLF ++ "1234567890" ++ ?CRLF ++ "4" ++ ?CRLF ++ "0123"++ ?CRLF ++ "0" ++ @@ -173,27 +150,18 @@ chunk_decode_otp_6264(Config) when is_list(Config)-> ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), {_, NewBody} = parse(Module1, Function1, Args1, tl(NewChunkedBody1)), - "12345678900" = binary_to_list(NewBody), - - ok. + "12345678900" = binary_to_list(NewBody). %%------------------------------------------------------------------------- -chunk_decode_empty_chunk_otp_6511(doc) -> - [""]; -chunk_decode_empty_chunk_otp_6511(suite) -> - []; chunk_decode_empty_chunk_otp_6511(Config) when is_list(Config) -> ChunkedBody = "0" ++ ?CRLF ++ ?CRLF, {ok,{["content-length:0"],<<>>}} = http_chunk:decode(list_to_binary(ChunkedBody), - ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), - ok. + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE). %%------------------------------------------------------------------------- -chunk_decode_trailer(doc) -> - ["Make sure trailers are handled correctly. Trailers should" - "become new headers"]; -chunk_decode_trailer(suite) -> - []; +chunk_decode_trailer() -> + [{doc,"Make sure trailers are handled correctly. Trailers should" + "become new headers"}]. chunk_decode_trailer(Config) when is_list(Config)-> ChunkedBody = "1a; ignore-stuff-here" ++ ?CRLF ++ "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF @@ -249,15 +217,11 @@ chunk_decode_trailer(Config) when is_list(Config)-> ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), {_, NewBody} = parse(Module1, Function1, Args1, tl(ChunkedBody3)), - "abcdefghijklmnopqrstuvwxyz1234567890abcdef" = binary_to_list(NewBody), - - ok. + "abcdefghijklmnopqrstuvwxyz1234567890abcdef" = binary_to_list(NewBody). %%------------------------------------------------------------------------- -chunk_encode(doc) -> - ["Test http_chunk:encode/1 & http_chunk:encode_last/0"]; -chunk_encode(suite) -> - []; +chunk_encode() -> + [{doc, "Test http_chunk:encode/1 & http_chunk:encode_last/0"}]. chunk_encode(Config) when is_list(Config) -> <<54, ?CR, ?LF, 102,111,111,98,97,114, ?CR, ?LF>> = http_chunk:encode(list_to_binary("foobar")), @@ -267,12 +231,10 @@ chunk_encode(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -http_response(doc) -> - ["Test httpc_response:parse*. This test case will simulate that the " +http_response() -> + [{doc, "Test httpc_response:parse*. This test case will simulate that the " "message will be recived a little at the time on a socket and the " - "package may be broken up into smaller parts at arbitrary point."]; -http_response(suite) -> - []; + "package may be broken up into smaller parts at arbitrary point."}]. http_response(Config) when is_list(Config) -> HttpHead1 = ["HTTP", "/1.1 ", "20", "0 ", "ok", [?CR, ?LF], @@ -340,12 +302,10 @@ http_response(Config) when is_list(Config) -> [<<>>,Length1], HttpBody1)), ok. %%------------------------------------------------------------------------- -http_request(doc) -> - ["Test httpd_request:parse* This test case will simulate that the " +http_request() -> + [{doc, "Test httpd_request:parse* This test case will simulate that the " "message will be recived a little at the time on a socket and the " - "package may be broken up into smaller parts at arbitrary point."]; -http_request(suite) -> - []; + "package may be broken up into smaller parts at arbitrary point."}]. http_request(Config) when is_list(Config) -> HttpHead = ["GE", "T ", "http://www.erlang", ".org ", "HTTP", @@ -407,15 +367,12 @@ http_request(Config) when is_list(Config) -> NewBody1 = binary_to_list(parse (httpd_request, whole_body, - [<<>>, Length1], HttpBody1)), - ok. + [<<>>, Length1], HttpBody1)). %%------------------------------------------------------------------------- -validate_request_line(doc) -> - ["Test httpd_request:validate/3. Makes sure you can not get past" +validate_request_line() -> + [{doc, "Test httpd_request:validate/3. Makes sure you can not get past" " the server_root and that the request is recognized by the server" - " and protcol version." ]; -validate_request_line(suite) -> - []; + " and protcol version."}]. validate_request_line(Config) when is_list(Config) -> %% HTTP/0.9 only has GET requests @@ -468,16 +425,12 @@ validate_request_line(Config) when is_list(Config) -> NewForbiddenUri1 = "http://127.0.0.1:8888/../home/ingela/test.html", {error, {bad_request, {forbidden, NewForbiddenUri1}}} = - httpd_request:validate("GET", NewForbiddenUri1, "HTTP/1.1"), - - ok. + httpd_request:validate("GET", NewForbiddenUri1, "HTTP/1.1"). %%------------------------------------------------------------------------- -check_content_length_encoding(doc) -> - ["Test http_request:headers/2. Check that the content-length is" - " encoded even when it is zero." ]; -check_content_length_encoding(suite) -> - []; +check_content_length_encoding() -> + [{doc, "Test http_request:headers/2. Check that the content-length is" + " encoded even when it is zero."}]. check_content_length_encoding(Config) when is_list(Config) -> %% Check that the content-length is preserved. @@ -486,16 +439,12 @@ check_content_length_encoding(Config) when is_list(Config) -> true = (string:str(Header1, "content-length: 123\r\n") > 0), %% Check that content-length=0 is handled correctly. Header2 = http_request:http_headers(#http_request_h{'content-length'="0"}), - true = (string:str(Header2, "content-length: 0\r\n") > 0), - - ok. + true = (string:str(Header2, "content-length: 0\r\n") > 0). %%------------------------------------------------------------------------- -esi_parse_headers(doc) -> - ["Test httpd_esi:*. All header values are received in the same" - " erlang message."]; -esi_parse_headers(suite) -> - []; +esi_parse_headers() -> + [{doc, "Test httpd_esi:*. All header values are received in the same" + " erlang message."}]. esi_parse_headers(Config) when is_list(Config) -> ESIResult = "content-type:text/html\r\ndate:Thu, 28 Oct 2004 07:57:43 " @@ -522,16 +471,14 @@ esi_parse_headers(Config) when is_list(Config) -> httpd_esi:handle_headers(Headers2), {proceed,"/foo/bar.html"} = - httpd_esi:handle_headers("location:/foo/bar.html\r\n"), - ok. + httpd_esi:handle_headers("location:/foo/bar.html\r\n"). %%-------------------------------------------------------------------- -cgi_parse_headers(doc) -> - ["Test httpd_cgi:*. This test case will simulate that the " +cgi_parse_headers() -> + [{doc, "Test httpd_cgi:*. This test case will simulate that the " "message will be recived a little at the time on a socket and the " - "package may be broken up into smaller parts at arbitrary point."]; -cgi_parse_headers(suite) -> - []; + "package may be broken up into smaller parts at arbitrary point."}]. + cgi_parse_headers(Config) when is_list(Config) -> CGIResult = ["content-type:text", "/html\ndate:Thu, 28 Oct 2004 07:57:43 " @@ -567,26 +514,18 @@ cgi_parse_headers(Config) when is_list(Config) -> {ok,[{"content-type","text/html"}, {"connection","close"}, {"content-language","en"}, - {"age","4711"}], {200,"ok"}} = httpd_cgi:handle_headers(Headers3), - - ok. - + {"age","4711"}], {200,"ok"}} = httpd_cgi:handle_headers(Headers3). %%------------------------------------------------------------------------- -is_absolut_uri(doc) -> - ["Test http_request:is_absolut_uri/1."]; -is_absolut_uri(suite) -> - []; +is_absolut_uri() -> + [{doc, "Test http_request:is_absolut_uri/1."}]. is_absolut_uri(Config) when is_list(Config) -> true = http_request:is_absolut_uri("http://www.erlang.org"), true = http_request:is_absolut_uri("https://www.erlang.org"), false = http_request:is_absolut_uri("index.html"). - %%------------------------------------------------------------------------- -convert_netscapecookie_date(doc) -> - ["Test http_util:convert_netscapecookie_date/1."]; -convert_netscapecookie_date(suite) -> - []; +convert_netscapecookie_date() -> + [{doc, "Test http_util:convert_netscapecookie_date/1."}]. convert_netscapecookie_date(Config) when is_list(Config) -> {{2006,1,6},{8,59,38}} = http_util:convert_netscapecookie_date("Mon, 06-Jan-2006 08:59:38 GMT"), @@ -619,9 +558,7 @@ convert_netscapecookie_date(Config) when is_list(Config) -> {{2006,12,12},{8,59,38}} = http_util:convert_netscapecookie_date("Sun 12-Dec-06 08:59:38 GMT"), {{2036,1,1},{8,0,1}} = - http_util:convert_netscapecookie_date("Tue Jan 01 08:00:01 2036 GMT"), - ok. - + http_util:convert_netscapecookie_date("Tue Jan 01 08:00:01 2036 GMT"). %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -- cgit v1.2.3 From 1545eae1470c0183bacbf1786d6f1c0366138157 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Thu, 5 Nov 2015 09:53:26 +0100 Subject: inets: Improve max header size handling The chunked length header should be checked as well as headers present in the chunk trailer part, ignored extensions are counted as header bytes. Also the decode trailer function will stop as soon as the header size is exceed, when that happens. --- lib/inets/src/http_lib/http_chunk.erl | 163 ++++++++++++++++++---------------- lib/inets/test/http_format_SUITE.erl | 67 ++++++++++++-- 2 files changed, 147 insertions(+), 83 deletions(-) (limited to 'lib/inets') diff --git a/lib/inets/src/http_lib/http_chunk.erl b/lib/inets/src/http_lib/http_chunk.erl index c17ff6cce5..2f8476a49d 100644 --- a/lib/inets/src/http_lib/http_chunk.erl +++ b/lib/inets/src/http_lib/http_chunk.erl @@ -57,7 +57,7 @@ %%------------------------------------------------------------------------- decode(ChunkedBody, MaxBodySize, MaxHeaderSize) -> %% Note decode_size will call decode_data. - decode_size([ChunkedBody, <<>>, [], + decode_size([ChunkedBody, <<>>, [], 0, {MaxBodySize, <<>>, 0, MaxHeaderSize}]). %%------------------------------------------------------------------------- @@ -120,67 +120,80 @@ handle_headers(ResponseHeaderRecord = #http_response_h{}, ChunkedHeaders) -> %% Functions that may be returned during the decoding process %% if the input data is incompleate. -decode_size([Bin, Rest, HexList, Info]) -> - decode_size(<>, HexList, Info). +decode_size([Bin, Rest, HexList, AccSize, Info]) -> + decode_size(<>, HexList, AccSize, Info). -ignore_extensions([Bin, Rest, NextFunction]) -> - ignore_extensions(<>, NextFunction). +ignore_extensions([Bin, Rest, RemainingSize, TotalMaxHeaderSize, NextFunction]) -> + ignore_extensions(<>, RemainingSize, TotalMaxHeaderSize, NextFunction). decode_data([Bin, ChunkSize, TotalChunk, Info]) -> decode_data(ChunkSize, <>, Info). -decode_trailer([Bin, Rest, Header, Headers, MaxHeaderSize, Body, - BodyLength]) -> +decode_trailer([Bin, Rest, Header, Headers, Body, + BodyLength, RemainingSize, TotalMaxHeaderSize]) -> decode_trailer(<>, - Header, Headers, MaxHeaderSize, Body, BodyLength). + Header, Headers, Body, BodyLength, RemainingSize, TotalMaxHeaderSize). %%%======================================================================== %%% Internal functions %%%======================================================================== -decode_size(<<>>, HexList, Info) -> - {?MODULE, decode_size, [<<>>, HexList, Info]}; -decode_size(Data = <>, HexList, +decode_size(_, _, AccHeaderSize, {_,_,_, MaxHeaderSize}) when + AccHeaderSize > MaxHeaderSize -> + throw({error, {header_too_long, {max, MaxHeaderSize}}}); + +decode_size(<<>>, HexList, AccHeaderSize, Info) -> + {?MODULE, decode_size, [<<>>, HexList, AccHeaderSize, Info]}; +decode_size(Data = <>, HexList, AccHeaderSize, {MaxBodySize, Body, AccLength, MaxHeaderSize}) -> try http_util:hexlist_to_integer(lists:reverse(HexList)) of 0 -> % Last chunk, there was no data - ignore_extensions(Data, {?MODULE, decode_trailer, - [<<>>, [],[], MaxHeaderSize, - Body, - integer_to_list(AccLength)]}); + ignore_extensions(Data, remaing_size(MaxHeaderSize, AccHeaderSize), MaxHeaderSize, + {?MODULE, decode_trailer, + [<<>>, [],[], + Body, + integer_to_list(AccLength)]}); ChunkSize -> %% Note decode_data may call decode_size again if there %% is more than one chunk, hence here is where the last parameter %% to this function comes in. decode_data(ChunkSize, ChunkRest, {MaxBodySize, Body, - ChunkSize + AccLength , + ChunkSize + AccLength, MaxHeaderSize}) catch _:_ -> - throw({error, {chunk_size, HexList}}) + throw({error, {chunk_size, lists:reverse(HexList)}}) end; -decode_size(<<";", Rest/binary>>, HexList, Info) -> +decode_size(<<";", Rest/binary>>, HexList, AccHeaderSize, {_,_,_, MaxHeaderSize} = Info) -> %% Note ignore_extensions will call decode_size/1 again when %% it ignored all extensions. - ignore_extensions(Rest, {?MODULE, decode_size, [<<>>, HexList, Info]}); -decode_size(<> = Data, HexList, Info) -> - {?MODULE, decode_size, [Data, HexList, Info]}; -decode_size(<>, HexList, Info) -> - decode_size(Rest, [Octet | HexList], Info). + ignore_extensions(Rest, remaing_size(MaxHeaderSize, AccHeaderSize), MaxHeaderSize, + {?MODULE, decode_size, [<<>>, HexList, AccHeaderSize, Info]}); +decode_size(<> = Data, HexList, AccHeaderSize, Info) -> + {?MODULE, decode_size, [Data, HexList, AccHeaderSize, Info]}; +decode_size(<>, HexList, AccHeaderSize, Info) -> + decode_size(Rest, [Octet | HexList], AccHeaderSize + 1, Info). %% "All applications MUST ignore chunk-extension extensions they %% do not understand.", see RFC 2616 Section 3.6.1 We don't %% understand any extension... -ignore_extensions(<<>>, NextFunction) -> - {?MODULE, ignore_extensions, [<<>>, NextFunction]}; -ignore_extensions(Data = <>, +ignore_extensions(_, 0, TotalMaxHeaderSize, _) -> + throw({error, {header_too_long, {max, TotalMaxHeaderSize}}}); +ignore_extensions(<<>>, RemainingSize, TotalMaxHeaderSize, NextFunction) -> + {?MODULE, ignore_extensions, [<<>>, RemainingSize, TotalMaxHeaderSize, NextFunction]}; +ignore_extensions(Data = <>, RemainingSize, TotalMaxHeaderSize, {Module, Function, Args}) -> - Module:Function([Data | Args]); -ignore_extensions(<> = Data, NextFunction) -> - {?MODULE, ignore_extensions, [Data, NextFunction]}; -ignore_extensions(<<_Octet, Rest/binary>>, NextFunction) -> - ignore_extensions(Rest, NextFunction). + case Function of + decode_trailer -> + Module:Function([Data | Args ++ [RemainingSize, TotalMaxHeaderSize]]); + _ -> + Module:Function([Data | Args]) + end; +ignore_extensions(<> = Data, RemainingSize, TotalMaxHeaderSize, NextFunction) -> + {?MODULE, ignore_extensions, [Data, RemainingSize, TotalMaxHeaderSize, NextFunction]}; +ignore_extensions(<<_Octet, Rest/binary>>, RemainingSize, TotalMaxHeaderSize, NextFunction) -> + ignore_extensions(Rest, remaing_size(RemainingSize, 1), TotalMaxHeaderSize, NextFunction). decode_data(ChunkSize, TotalChunk, Info = {MaxBodySize, BodySoFar, AccLength, MaxHeaderSize}) @@ -192,83 +205,81 @@ decode_data(ChunkSize, TotalChunk, %% once it ignored all extensions. {?MODULE, ignore_extensions, [<<>>, - {?MODULE, decode_trailer, [<<>>, [],[], MaxHeaderSize, + {?MODULE, decode_trailer, [<<>>, [],[], <>, integer_to_list(AccLength)]}]}; <> -> %% Note ignore_extensions will call decode_trailer/1 %% once it ignored all extensions. - ignore_extensions(Rest, {?MODULE, decode_trailer, - [<<>>, [],[], MaxHeaderSize, + ignore_extensions(Rest, MaxHeaderSize, MaxHeaderSize, + {?MODULE, decode_trailer, + [<<>>, [],[], <>, integer_to_list(AccLength)]}); <> -> - {?MODULE, decode_trailer, [<>, [],[], MaxHeaderSize, + {?MODULE, decode_trailer, [<>, [],[], <>, - integer_to_list(AccLength)]}; + integer_to_list(AccLength), MaxHeaderSize, MaxHeaderSize]}; <> -> - decode_trailer(<>, [],[], MaxHeaderSize, + decode_trailer(<>, [],[], <>, - integer_to_list(AccLength)); - %% There are more chunks, so here we go agin... + integer_to_list(AccLength), MaxHeaderSize, MaxHeaderSize); + %% There are more chunks, so here we go again... <> -> NewBody = <>, - {?MODULE, decode_size, [<<>>, [], {MaxBodySize, NewBody, AccLength, MaxHeaderSize}]}; + {?MODULE, decode_size, [<<>>, [], 0, {MaxBodySize, NewBody, AccLength, MaxHeaderSize}]}; <> when (AccLength < MaxBodySize) or (MaxBodySize == nolimit) -> - decode_size(Rest, [], + decode_size(Rest, [], 0, {MaxBodySize, <>, AccLength, MaxHeaderSize}); <<_:ChunkSize/binary, ?CR, ?LF, _/binary>> -> - throw({error, body_too_big}); + throw({error, {body_too_big, {max, MaxBodySize}}}); _ -> {?MODULE, decode_data, [ChunkSize, TotalChunk, Info]} end; decode_data(ChunkSize, TotalChunk, Info) -> {?MODULE, decode_data, [ChunkSize, TotalChunk, Info]}. -decode_trailer(<<>>, Header, Headers, MaxHeaderSize, Body, BodyLength) -> - {?MODULE, decode_trailer, [<<>>, Header, Headers, MaxHeaderSize, Body, - BodyLength]}; - +decode_trailer(_,_,_,_,_, 0, TotalMaxHeaderSize) -> + throw({error, {header_too_long, {max, TotalMaxHeaderSize}}}); +decode_trailer(<<>>, Header, Headers, Body, BodyLength, RemainingSize, TotalMaxHeaderSize) -> + {?MODULE, decode_trailer, [<<>>, Header, Headers, Body, + BodyLength, RemainingSize, TotalMaxHeaderSize]}; %% Note: If Bin is not empty it is part of a pipelined request/response. -decode_trailer(<>, [], [], _, Body, BodyLength) -> +decode_trailer(<>, [], [], Body, BodyLength, _, _) -> {ok, {["content-length:" ++ BodyLength], <>}}; decode_trailer(<>, - Header, Headers, MaxHeaderSize, Body, BodyLength) -> + Header, Headers, Body, BodyLength, _, _) -> NewHeaders = case Header of [] -> Headers; _ -> [lists:reverse(Header) | Headers] end, - Length = length(NewHeaders), - case Length > MaxHeaderSize of - true -> - throw({error, {header_too_long, MaxHeaderSize, - MaxHeaderSize-Length}}); - false -> - {ok, {["content-length:" ++ BodyLength | NewHeaders], - <>}} - end; -decode_trailer(<> = Data, Header, Headers, MaxHeaderSize, - Body, BodyLength) -> - {?MODULE, decode_trailer, [Data, Header, Headers, MaxHeaderSize, Body, - BodyLength]}; -decode_trailer(<> = Data, Header, Headers, MaxHeaderSize, - Body, BodyLength) -> - {?MODULE, decode_trailer, [Data, Header, Headers, MaxHeaderSize, Body, - BodyLength]}; -decode_trailer(<> = Data, Header, Headers, MaxHeaderSize, - Body, BodyLength) -> - {?MODULE, decode_trailer, [Data, Header, Headers, MaxHeaderSize, Body, - BodyLength]}; -decode_trailer(<>, Header, Headers, - MaxHeaderSize, Body, BodyLength) -> + {ok, {["content-length:" ++ BodyLength | NewHeaders], + <>}}; +decode_trailer(<> = Data, Header, Headers, + Body, BodyLength, RemainingSize, TotalMaxHeaderSize) -> + {?MODULE, decode_trailer, [Data, Header, Headers, Body, + BodyLength, RemainingSize, TotalMaxHeaderSize]}; +decode_trailer(<> = Data, Header, Headers, + Body, BodyLength, RemainingSize, TotalMaxHeaderSize) -> + {?MODULE, decode_trailer, [Data, Header, Headers, Body, + BodyLength, RemainingSize, TotalMaxHeaderSize]}; +decode_trailer(<> = Data, Header, Headers, + Body, BodyLength, RemainingSize, TotalMaxHeaderSize) -> + {?MODULE, decode_trailer, [Data, Header, Headers, Body, + BodyLength, RemainingSize, TotalMaxHeaderSize]}; +decode_trailer(<>, Header, Headers, Body, BodyLength, RemainingSize, TotalMaxHeaderSize) -> decode_trailer(Rest, [], [lists:reverse(Header) | Headers], - MaxHeaderSize, Body, BodyLength); + Body, BodyLength, RemainingSize, TotalMaxHeaderSize); +decode_trailer(<>, Header, Headers, Body, + BodyLength, RemainingSize, TotalMaxHeaderSize) -> + decode_trailer(Rest, [Octet | Header], Headers, + Body, BodyLength, RemainingSize - 1, TotalMaxHeaderSize). -decode_trailer(<>, Header, Headers, MaxHeaderSize, Body, - BodyLength) -> - decode_trailer(Rest, [Octet | Header], Headers, MaxHeaderSize, - Body, BodyLength). +remaing_size(nolimit, _) -> + nolimit; +remaing_size(Total, Consumed) -> + Total - Consumed. diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl index 83ffe259b3..a927adc75e 100644 --- a/lib/inets/test/http_format_SUITE.erl +++ b/lib/inets/test/http_format_SUITE.erl @@ -38,7 +38,7 @@ groups() -> [chunk_decode, chunk_encode, chunk_extensions_otp_6005, chunk_decode_otp_6264, chunk_decode_empty_chunk_otp_6511, - chunk_decode_trailer]}]. + chunk_decode_trailer, chunk_max_headersize, chunk_max_bodysize, chunk_not_hex]}]. init_per_suite(Config) -> Config. @@ -91,9 +91,7 @@ chunk_decode(Config) when is_list(Config) -> ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), {_, Body} = parse(Module, Function, Args, tl(NewChunkedBody)), - "1234567890HEJ!" = binary_to_list(Body), - - ok. + "1234567890HEJ!" = binary_to_list(Body). %%------------------------------------------------------------------------- chunk_extensions_otp_6005() -> @@ -226,9 +224,64 @@ chunk_encode(Config) when is_list(Config) -> <<54, ?CR, ?LF, 102,111,111,98,97,114, ?CR, ?LF>> = http_chunk:encode(list_to_binary("foobar")), ["6", ?CR, ?LF,"foobar", ?CR, ?LF] = http_chunk:encode("foobar"), - <<$0, ?CR, ?LF, ?CR, ?LF >> = http_chunk:encode_last(), - ok. - + <<$0, ?CR, ?LF, ?CR, ?LF >> = http_chunk:encode_last(). +%%------------------------------------------------------------------------- +chunk_max_headersize() -> + [{doc, "Test max header limit"}]. +chunk_max_headersize(Config) when is_list(Config) -> + ChunkedBody = "1a; ignore-stuff-here" ++ ?CRLF ++ + "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF + ++ "1234567890abcdef" ++ ?CRLF ++ "0" ++ ?CRLF + ++ "some-footer:some-value" ++ ?CRLF + ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF, + + {ok, {_, _}} = + http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), + + %% Too long in length header + {error,{header_too_long, {max, 1}}} = + (catch http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, 1)), + + %% Too long in extension field + {error,{header_too_long, {max, 10}}} = + (catch http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, 10)), + + %% Too long in trailer + {error,{header_too_long, {max, 30}}} = + (catch http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, 30)). +%%------------------------------------------------------------------------- +chunk_not_hex() -> + [{doc, "Test bad chunked length header"}]. +chunk_not_hex(Config) when is_list(Config) -> + ChunkedBody = "åäö; ignore-stuff-here" ++ ?CRLF ++ + "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF + ++ "1234567890abcdef" ++ ?CRLF ++ "0" ++ ?CRLF + ++ "some-footer:some-value" ++ ?CRLF + ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF, + {error,{chunk_size, "åäö"}} = + (catch http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE)). +%%------------------------------------------------------------------------- +chunk_max_bodysize() -> + [{doc, "Test max body limit"}]. +chunk_max_bodysize(Config) when is_list(Config) -> + ChunkedBody = "1a; ignore-stuff-here" ++ ?CRLF ++ + "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF + ++ "1234567890abcdef" ++ ?CRLF ++ "0" ++ ?CRLF + ++ "some-footer:some-value" ++ ?CRLF + ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF, + {ok, {_, _}} = + http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), + + %% Too long body + {error,{body_too_big, {max, 10}}} = + (catch http_chunk:decode(list_to_binary(ChunkedBody), + 10, ?HTTP_MAX_HEADER_SIZE)). %%------------------------------------------------------------------------- http_response() -> -- cgit v1.2.3 From f05f7ae5e8ea4b19be5fa49681ad6efb01daae6d Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 9 Nov 2015 15:08:35 +0100 Subject: inets: Remove debug macros that mimic call trace --- lib/inets/src/http_lib/http_transport.erl | 44 ++++++------------------------- 1 file changed, 8 insertions(+), 36 deletions(-) (limited to 'lib/inets') diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index 719dc4c425..484e07cd84 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -40,12 +40,6 @@ -include_lib("inets/src/inets_app/inets_internal.hrl"). -include("http_internal.hrl"). --define(SERVICE, httpl). --define(hlri(Label, Content), ?report_important(Label, ?SERVICE, Content)). --define(hlrv(Label, Content), ?report_verbose(Label, ?SERVICE, Content)). --define(hlrd(Label, Content), ?report_debug(Label, ?SERVICE, Content)). --define(hlrt(Label, Content), ?report_trace(Label, ?SERVICE, Content)). - %%%========================================================================= %%% Internal application API @@ -107,8 +101,6 @@ connect(SocketType, Address, Opts) -> connect(ip_comm = _SocketType, {Host, Port}, Opts0, Timeout) when is_list(Opts0) -> Opts = [binary, {packet, 0}, {active, false}, {reuseaddr, true} | Opts0], - ?hlrt("connect using gen_tcp", - [{host, Host}, {port, Port}, {opts, Opts}, {timeout, Timeout}]), try gen_tcp:connect(Host, Port, Opts, Timeout) of {ok, _} = OK -> OK; @@ -127,11 +119,6 @@ connect({ssl, SslConfig}, Address, Opts, Timeout) -> connect({essl, SslConfig}, {Host, Port}, Opts0, Timeout) -> Opts = [binary, {active, false}, {ssl_imp, new} | Opts0] ++ SslConfig, - ?hlrt("connect using essl", - [{host, Host}, - {port, Port}, - {ssl_config, SslConfig}, - {timeout, Timeout}]), case (catch ssl:connect(Host, Port, Opts, Timeout)) of {'EXIT', Reason} -> {error, {eoptions, Reason}}; @@ -156,29 +143,23 @@ connect({essl, SslConfig}, {Host, Port}, Opts0, Timeout) -> %% reason for this to enable a HTTP-server not running as root to use %% port 80. %%------------------------------------------------------------------------- -listen(ip_comm = _SocketType, Addr, Port, Fd, IpFamily) -> - listen_ip_comm(Addr, Port, Fd, IpFamily); - +listen(ip_comm, Addr, Port, Fd, IpFamily) -> + listen_ip_comm(Addr, Port, [], Fd, IpFamily); + +listen({ip_comm, SockOpts}, Addr, Port, Fd, IpFamily) -> + listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily); + listen({essl, SSLConfig}, Addr, Port, Fd, IpFamily) -> listen_ssl(Addr, Port, Fd, SSLConfig, IpFamily, []). -listen(ip_comm = _SocketType, Addr, Port, IpFamily) -> - listen_ip_comm(Addr, Port, undefined, IpFamily); +listen(ip_comm, Addr, Port, IpFamily) -> + listen_ip_comm(Addr, Port, [], undefined, IpFamily); %% Wrapper for backaward compatibillity listen({ssl, SSLConfig}, Addr, Port, IpFamily) -> - ?hlrt("listen (wrapper)", - [{addr, Addr}, - {port, Port}, - {ssl_config, SSLConfig}]), listen({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Addr, Port, IpFamily); - listen({essl, SSLConfig}, Addr, Port, IpFamily) -> - ?hlrt("listen (essl)", - [{addr, Addr}, - {port, Port}, - {ssl_config, SSLConfig}]), {SSLConfig2, ExtraOpts} = case proplists:get_value(log_alert, SSLConfig, undefined) of undefined -> {SSLConfig, []}; @@ -325,7 +306,6 @@ controlling_process({essl, _}, Socket, NewOwner) -> %% gen_tcp or ssl. %%------------------------------------------------------------------------- setopts(ip_comm, Socket, Options) -> - ?hlrt("ip_comm setopts", [{socket, Socket}, {options, Options}]), inet:setopts(Socket, Options); %% Wrapper for backaward compatibillity @@ -351,7 +331,6 @@ getopts(SocketType, Socket) -> getopts(SocketType, Socket, Opts). getopts(ip_comm, Socket, Options) -> - ?hlrt("ip_comm getopts", [{socket, Socket}, {options, Options}]), case inet:getopts(Socket, Options) of {ok, SocketOpts} -> SocketOpts; @@ -364,7 +343,6 @@ getopts({ssl, SSLConfig}, Socket, Options) -> getopts({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Options); getopts({essl, _}, Socket, Options) -> - ?hlrt("essl getopts", [{socket, Socket}, {options, Options}]), getopts_ssl(Socket, Options). getopts_ssl(Socket, Options) -> @@ -384,7 +362,6 @@ getopts_ssl(Socket, Options) -> %% Description: Gets the socket stats values for the socket %%------------------------------------------------------------------------- getstat(ip_comm = _SocketType, Socket) -> - ?hlrt("ip_comm getstat", [{socket, Socket}]), case inet:getstat(Socket) of {ok, Stats} -> Stats; @@ -555,22 +532,17 @@ sock_opts(Opts) -> %% -- negotiate -- negotiate(ip_comm,_,_) -> - ?hlrt("negotiate(ip_comm)", []), ok; negotiate({ssl, SSLConfig}, Socket, Timeout) -> - ?hlrt("negotiate(ssl)", []), negotiate({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Timeout); negotiate({essl, _}, Socket, Timeout) -> - ?hlrt("negotiate(essl)", []), negotiate_ssl(Socket, Timeout). negotiate_ssl(Socket, Timeout) -> - ?hlrt("negotiate_ssl", [{socket, Socket}, {timeout, Timeout}]), case ssl:ssl_accept(Socket, Timeout) of ok -> ok; {error, Reason} -> - ?hlrd("negotiate_ssl - accept failed", [{reason, Reason}]), %% Look for "valid" error reasons ValidReasons = [timeout, econnreset, esslaccept, esslerrssl], case lists:member(Reason, ValidReasons) of -- cgit v1.2.3 From 4b7be97512d6fc62b337e810f697a6bb0db1773c Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 9 Nov 2015 15:09:40 +0100 Subject: inets: httpd - Add possibility to specify socket options for HTTP Was already possible for HTTPS. Also remove use of legacy option inet6fb4. IPv6 standard moved away from beeing able to fallback to IPv4 so this option makes little sense, will use inet (Ipv4) as default instead of inet6fb4. --- lib/inets/doc/src/httpd.xml | 13 ++-- lib/inets/src/http_lib/http_transport.erl | 116 ++++++++++-------------------- lib/inets/src/http_server/httpd_conf.erl | 101 +++++++++++++------------- lib/inets/src/http_server/httpd_sup.erl | 4 +- lib/inets/src/http_server/httpd_util.erl | 12 +--- lib/inets/test/httpd_SUITE.erl | 24 ++++--- 6 files changed, 115 insertions(+), 155 deletions(-) (limited to 'lib/inets') diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index 2a4aea41c2..0fc3cb1ce7 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -177,21 +177,22 @@ - {socket_type, ip_comm | {essl, Config::proplist()}} + {socket_type, ip_comm | {ip_comm, Config::proplist()} | {essl, Config::proplist()}} +

For ip_comm configuration options, see + gen_tcp:listen/2, some options + that are used internally by httpd can not be set.

For SSL configuration options, see ssl:listen/2.

Default is ip_comm.

- {ipfamily, inet | inet6 | inet6fb4} + {ipfamily, inet | inet6} -

This option is only used when option - socket_type has value ip_comm.

-

Default is inet6fb4.

+

Default is inet, legacy option inet6fb4 no longer makes sense and will be translated + to inet.

- {minimum_bytes_per_second, integer()} diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index 484e07cd84..e3d7382043 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -53,7 +53,8 @@ %%------------------------------------------------------------------------- start(ip_comm) -> do_start_ip_comm(); - +start({ip_comm, _}) -> + do_start_ip_comm(); %% This is just for backward compatibillity start({ssl, _}) -> do_start_ssl(); @@ -97,10 +98,8 @@ do_start_ssl() -> connect(SocketType, Address, Opts) -> connect(SocketType, Address, Opts, infinity). - -connect(ip_comm = _SocketType, {Host, Port}, Opts0, Timeout) - when is_list(Opts0) -> - Opts = [binary, {packet, 0}, {active, false}, {reuseaddr, true} | Opts0], +connect(ip_comm, {Host, Port}, Opts0, Timeout) -> + Opts = [binary, {packet, 0}, {active, false}, {reuseaddr, true} | Opts0 ], try gen_tcp:connect(Host, Port, Opts, Timeout) of {ok, _} = OK -> OK; @@ -168,83 +167,30 @@ listen({essl, SSLConfig}, Addr, Port, IpFamily) -> end, listen_ssl(Addr, Port, undefined, SSLConfig2, IpFamily, ExtraOpts). -listen_ip_comm(Addr, Port, Fd, IpFamily) -> - case (catch do_listen_ip_comm(Addr, Port, Fd, IpFamily)) of +listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily) -> + case (catch do_listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily)) of {'EXIT', Reason} -> {error, {exit, Reason}}; Else -> Else end. -do_listen_ip_comm(Addr, Port, Fd, IpFamily) -> - {NewPort, Opts} = get_socket_info(Addr, Port, Fd), - case IpFamily of - inet6fb4 -> - Opts2 = [inet6 | Opts], - ?hlrt("try ipv6 listen", [{port, NewPort}, {opts, Opts2}]), - case (catch gen_tcp:listen(NewPort, Opts2)) of - {error, Reason} when ((Reason =:= nxdomain) orelse - (Reason =:= eafnosupport)) -> - Opts3 = [inet | Opts], - ?hlrt("ipv6 listen failed - try ipv4 instead", - [{reason, Reason}, {port, NewPort}, {opts, Opts3}]), - gen_tcp:listen(NewPort, Opts3); - - %% This is when a given hostname has resolved to a - %% IPv4-address. The inet6-option together with a - %% {ip, IPv4} option results in badarg - {'EXIT', Reason} -> - Opts3 = [inet | Opts], - ?hlrt("ipv6 listen exit - try ipv4 instead", - [{reason, Reason}, {port, NewPort}, {opts, Opts3}]), - gen_tcp:listen(NewPort, Opts3); - - Other -> - ?hlrt("ipv6 listen done", [{other, Other}]), - Other - end; - _ -> - Opts2 = [IpFamily | Opts], - ?hlrt("listen", [{port, NewPort}, {opts, Opts2}]), - gen_tcp:listen(NewPort, Opts2) - end. +do_listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily) -> + Backlog = proplists:get_value(backlog, SockOpts, 128), + {NewPort, Opts} = get_socket_info(Addr, Port, Fd, + [{backlog, Backlog}, {reuseaddr, true} | SockOpts]), + Opts2 = [IpFamily | Opts], + gen_tcp:listen(NewPort, Opts2). listen_ssl(Addr, Port, Fd, Opts0, IpFamily, ExtraOpts) -> - {NewPort, SockOpt} = get_socket_info(Addr, Port, Fd), + Backlog = proplists:get_value(backlog, Opts0, 128), + {NewPort, SockOpt} = get_socket_info(Addr, Port, Fd, + [{backlog, Backlog}, {reuseaddr, true}]), Opts = SockOpt ++ Opts0, - case IpFamily of - inet6fb4 -> - Opts2 = [inet6 | Opts] ++ ExtraOpts, - ?hlrt("try ipv6 listen", [{opts, Opts2}]), - case (catch ssl:listen(Port, Opts2)) of - {error, Reason} when ((Reason =:= nxdomain) orelse - (Reason =:= eafnosupport)) -> - Opts3 = [inet | Opts] ++ ExtraOpts, - ?hlrt("ipv6 listen failed - try ipv4 instead", - [{reason, Reason}, {opts, Opts3}]), - ssl:listen(NewPort, Opts3); - - {'EXIT', Reason} -> - Opts3 = [inet | Opts] ++ ExtraOpts, - ?hlrt("ipv6 listen exit - try ipv4 instead", - [{reason, Reason}, {opts, Opts3}]), - ssl:listen(NewPort, Opts3); - - Other -> - ?hlrt("ipv6 listen done", [{other, Other}]), - Other - end; - - _ -> - Opts2 = [IpFamily | Opts], - ?hlrt("listen", [{opts, Opts2}]), - ssl:listen(NewPort, Opts2 ++ ExtraOpts) - end. + Opts2 = [IpFamily | Opts], + ssl:listen(NewPort, Opts2 ++ ExtraOpts). - - -get_socket_info(Addr, Port, Fd) -> - BaseOpts = [{backlog, 128}, {reuseaddr, true}], +get_socket_info(Addr, Port, Fd, BaseOpts) -> %% The presence of a file descriptor takes precedence case Fd of undefined -> @@ -269,6 +215,8 @@ accept(SocketType, ListenSocket) -> accept(ip_comm, ListenSocket, Timeout) -> gen_tcp:accept(ListenSocket, Timeout); +accept({ip_comm, _}, ListenSocket, Timeout) -> + gen_tcp:accept(ListenSocket, Timeout); %% Wrapper for backaward compatibillity accept({ssl, SSLConfig}, ListenSocket, Timeout) -> @@ -288,6 +236,8 @@ accept({essl, _SSLConfig}, ListenSocket, Timeout) -> %%------------------------------------------------------------------------- controlling_process(ip_comm, Socket, NewOwner) -> gen_tcp:controlling_process(Socket, NewOwner); +controlling_process({ip_comm, _}, Socket, NewOwner) -> + gen_tcp:controlling_process(Socket, NewOwner); %% Wrapper for backaward compatibillity controlling_process({ssl, SSLConfig}, Socket, NewOwner) -> @@ -307,16 +257,15 @@ controlling_process({essl, _}, Socket, NewOwner) -> %%------------------------------------------------------------------------- setopts(ip_comm, Socket, Options) -> inet:setopts(Socket, Options); +setopts({ip_comm, _}, Socket, Options) -> + inet:setopts(Socket, Options); %% Wrapper for backaward compatibillity setopts({ssl, SSLConfig}, Socket, Options) -> setopts({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Options); setopts({essl, _}, Socket, Options) -> - ?hlrt("[e]ssl setopts", [{socket, Socket}, {options, Options}]), - Reason = (catch ssl:setopts(Socket, Options)), - ?hlrt("[e]ssl setopts result", [{reason, Reason}]), - Reason. + (catch ssl:setopts(Socket, Options)). %%------------------------------------------------------------------------- @@ -330,6 +279,9 @@ getopts(SocketType, Socket) -> Opts = [packet, packet_size, recbuf, sndbuf, priority, tos, send_timeout], getopts(SocketType, Socket, Opts). +getopts({ip_comm, _}, Socket, Options) -> + getopts(ip_comm, Socket, Options); + getopts(ip_comm, Socket, Options) -> case inet:getopts(Socket, Options) of {ok, SocketOpts} -> @@ -386,6 +338,8 @@ getstat({essl, _} = _SocketType, _Socket) -> %%------------------------------------------------------------------------- send(ip_comm, Socket, Message) -> gen_tcp:send(Socket, Message); +send({ip_comm, _}, Socket, Message) -> + gen_tcp:send(Socket, Message); %% Wrapper for backaward compatibillity send({ssl, SSLConfig}, Socket, Message) -> @@ -394,7 +348,6 @@ send({ssl, SSLConfig}, Socket, Message) -> send({essl, _}, Socket, Message) -> ssl:send(Socket, Message). - %%------------------------------------------------------------------------- %% close(SocketType, Socket) -> ok | {error, Reason} %% SocketType = ip_comm | {ssl, _} @@ -404,6 +357,8 @@ send({essl, _}, Socket, Message) -> %%------------------------------------------------------------------------- close(ip_comm, Socket) -> gen_tcp:close(Socket); +close({ip_comm, []}, Socket) -> + gen_tcp:close(Socket); %% Wrapper for backaward compatibillity close({ssl, SSLConfig}, Socket) -> @@ -425,6 +380,8 @@ close({essl, _}, Socket) -> %%------------------------------------------------------------------------- peername(ip_comm, Socket) -> do_peername(inet:peername(Socket)); +peername({ip_comm, _}, Socket) -> + do_peername(inet:peername(Socket)); %% Wrapper for backaward compatibillity peername({ssl, SSLConfig}, Socket) -> @@ -457,7 +414,8 @@ do_peername({error, _}) -> %%------------------------------------------------------------------------- sockname(ip_comm, Socket) -> do_sockname(inet:sockname(Socket)); - +sockname({ip_comm, _}, Socket) -> + do_sockname(inet:sockname(Socket)); %% Wrapper for backaward compatibillity sockname({ssl, SSLConfig}, Socket) -> sockname({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket); @@ -533,6 +491,8 @@ sock_opts(Opts) -> %% -- negotiate -- negotiate(ip_comm,_,_) -> ok; +negotiate({ip_comm, _},_,_) -> + ok; negotiate({ssl, SSLConfig}, Socket, Timeout) -> negotiate({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Timeout); negotiate({essl, _}, Socket, Timeout) -> diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl index 7d31989244..62e8a95b19 100644 --- a/lib/inets/src/http_server/httpd_conf.erl +++ b/lib/inets/src/http_server/httpd_conf.erl @@ -156,7 +156,7 @@ load("BindAddress " ++ Address0, []) -> case string:tokens(Address0, [$|]) of [Address1] -> ?hdrv("load BindAddress", [{address1, Address1}]), - {clean_address(Address1), inet6fb4}; + {clean_address(Address1), inet}; [Address1, IpFamilyStr] -> ?hdrv("load BindAddress", [{address1, Address1}, @@ -353,14 +353,21 @@ clean_address(Addr) -> make_ipfamily(IpFamilyStr) -> - IpFamily = list_to_atom(IpFamilyStr), - case lists:member(IpFamily, [inet, inet6, inet6fb4]) of - true -> - IpFamily; - false -> - throw({error, {bad_ipfamily, IpFamilyStr}}) - end. - + validate_ipfamily(list_to_atom(IpFamilyStr)). + +validate_ipfamily(inet) -> + inet; +validate_ipfamily(inet6) -> + inet6; +%% Backwards compatibility wrapper, +%% fallback to the default, IPV4, +%% as it will most proably work. +%% IPv6 standard moved away from +%% beeing able to fallback to ipv4 +validate_ipfamily(inet6fb4) -> + inet; +validate_ipfamily(IpFamilyStr) -> + throw({error, {bad_ipfamily, IpFamilyStr}}). %% %% load_mime_types/1 -> {ok, MimeTypes} | {error, Reason} @@ -393,20 +400,16 @@ validate_properties2(Properties) -> undefined -> case proplists:get_value(sock_type, Properties, ip_comm) of ip_comm -> - case proplists:get_value(ipfamily, Properties) of - undefined -> - [{bind_address, any}, - {ipfamily, inet6fb4} | Properties]; - _ -> - [{bind_address, any} | Properties] - end; + add_inet_defaults(Properties); + {ip_comm, _} -> + add_inet_defaults(Properties); _ -> [{bind_address, any} | Properties] end; any -> Properties; Address0 -> - IpFamily = proplists:get_value(ipfamily, Properties, inet6fb4), + IpFamily = proplists:get_value(ipfamily, Properties, inet), case httpd_util:ip_address(Address0, IpFamily) of {ok, Address} -> Properties1 = proplists:delete(bind_address, Properties), @@ -418,6 +421,16 @@ validate_properties2(Properties) -> throw(Error) end end. + +add_inet_defaults(Properties) -> + case proplists:get_value(ipfamily, Properties) of + undefined -> + [{bind_address, any}, + {ipfamily, inet} | Properties]; + _ -> + [{bind_address, any} | Properties] + end. + check_minimum_bytes_per_second(Properties) -> case proplists:get_value(minimum_bytes_per_second, Properties, false) of false -> @@ -487,12 +500,11 @@ validate_config_params([{server_tokens, Value} | _]) -> validate_config_params([{socket_type, ip_comm} | Rest]) -> validate_config_params(Rest); -validate_config_params([{socket_type, Value} | Rest]) - when Value == ssl; Value == essl -> - validate_config_params(Rest); - -validate_config_params([{socket_type, {Value, _}} | Rest]) - when Value == essl orelse Value == ssl -> +validate_config_params([{socket_type, {Value, Opts}} | Rest]) when Value == ip_comm; + Value == ssl; + Value == essl -> + %% Make sure not to set socket values used internaly + validate_config_params(Opts), validate_config_params(Rest); validate_config_params([{socket_type, Value} | _]) -> @@ -622,21 +634,32 @@ validate_config_params([{disable_chunked_transfer_encoding_send, Value} | validate_config_params([{disable_chunked_transfer_encoding_send, Value} | _ ]) -> throw({disable_chunked_transfer_encoding_send, Value}); +validate_config_params([{Name, _} = Opt | _]) when Name == packet; + Name == mode; + Name == active; + Name == reuseaddr -> + throw({internaly_handled_opt_can_not_be_set, Opt}); validate_config_params([_| Rest]) -> validate_config_params(Rest). -%% It is actually pointless to check bind_address in this way since -%% we need ipfamily to do it properly... is_bind_address(any) -> true; is_bind_address(Value) -> - case httpd_util:ip_address(Value, inet6fb4) of + case is_bind_address(Value, inet) of + false -> + is_bind_address(Value, inet6); + True -> + True + end. + +is_bind_address(Value, IpFamily) -> + case httpd_util:ip_address(Value, IpFamily) of {ok, _} -> true; _ -> false end. - + store(ConfigList0) -> ?hdrd("store", []), try validate_config_params(ConfigList0) of @@ -776,28 +799,6 @@ remove(ConfigDB) -> ets:delete(ConfigDB), ok. -%% config(ConfigDB) -> -%% case httpd_util:lookup(ConfigDB, socket_type, ip_comm) of -%% ssl -> -%% case ssl_certificate_file(ConfigDB) of -%% undefined -> -%% {error, -%% "Directive SSLCertificateFile " -%% "not found in the config file"}; -%% SSLCertificateFile -> -%% {ssl, -%% SSLCertificateFile++ -%% ssl_certificate_key_file(ConfigDB)++ -%% ssl_verify_client(ConfigDB)++ -%% ssl_ciphers(ConfigDB)++ -%% ssl_password(ConfigDB)++ -%% ssl_verify_depth(ConfigDB)++ -%% ssl_ca_certificate_file(ConfigDB)} -%% end; -%% ip_comm -> -%% ip_comm -%% end. - get_config(Address, Port, Profile) -> Tab = httpd_util:make_name("httpd_conf", Address, Port, Profile), @@ -836,6 +837,8 @@ lookup_socket_type(ConfigDB) -> case httpd_util:lookup(ConfigDB, socket_type, ip_comm) of ip_comm -> ip_comm; + {ip_comm, _} = Type -> + Type; {Tag, Conf} -> {Tag, Conf}; SSL when (SSL =:= ssl) orelse (SSL =:= essl) -> diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl index f0b1942e2f..bf40cedd5c 100644 --- a/lib/inets/src/http_server/httpd_sup.erl +++ b/lib/inets/src/http_server/httpd_sup.erl @@ -241,7 +241,7 @@ listen(Address, Port, Config) -> case http_transport:start(SocketType) of ok -> {ok, Fd} = get_fd(Port), - IpFamily = proplists:get_value(ipfamily, Config, inet6fb4), + IpFamily = proplists:get_value(ipfamily, Config, inet), case http_transport:listen(SocketType, Address, Port, Fd, IpFamily) of {ok, ListenSocket} -> NewConfig = proplists:delete(port, Config), @@ -286,6 +286,8 @@ socket_type(Config) -> socket_type(ip_comm = SocketType, _) -> SocketType; +socket_type({ip_comm, _} = SocketType, _) -> + SocketType; socket_type({essl, _} = SocketType, _) -> SocketType; socket_type(_, Config) -> diff --git a/lib/inets/src/http_server/httpd_util.erl b/lib/inets/src/http_server/httpd_util.erl index fc69baf829..0387d71911 100644 --- a/lib/inets/src/http_server/httpd_util.erl +++ b/lib/inets/src/http_server/httpd_util.erl @@ -42,17 +42,7 @@ ip_address({_,_,_,_,_,_,_,_} = Address, _IpFamily) -> {ok, Address}; ip_address(Host, IpFamily) when ((IpFamily =:= inet) orelse (IpFamily =:= inet6)) -> - inet:getaddr(Host, IpFamily); -ip_address(Host, inet6fb4 = _IpFamily) -> - Inet = case gen_tcp:listen(0, [inet6]) of - {ok, Dummyport} -> - gen_tcp:close(Dummyport), - inet6; - _ -> - inet - end, - inet:getaddr(Host, Inet). - + inet:getaddr(Host, IpFamily). %% lookup diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index b50d31a5c1..9bd6f3636c 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -1434,9 +1434,11 @@ server_config(http_reload, Config) -> server_config(https_reload, Config) -> [{keep_alive_timeout, 2}] ++ server_config(https, Config); server_config(http_limit, Config) -> - [{max_clients, 1}, - %% Make sure option checking code is run - {max_content_length, 100000002}] ++ server_config(http, Config); + Conf = [{max_clients, 1}, + %% Make sure option checking code is run + {max_content_length, 100000002}] ++ server_config(http, Config), + ct:pal("Received message ~p~n", [Conf]), + Conf; server_config(http_custom, Config) -> [{customize, ?MODULE}] ++ server_config(http, Config); server_config(https_custom, Config) -> @@ -1486,6 +1488,7 @@ server_config(http_mime_types, Config0) -> server_config(http, Config) -> ServerRoot = ?config(server_root, Config), [{port, 0}, + {socket_type, {ip_comm, [{nodelay, true}]}}, {server_name,"httpd_test"}, {server_root, ServerRoot}, {document_root, ?config(doc_root, Config)}, @@ -1507,13 +1510,14 @@ server_config(http, Config) -> server_config(https, Config) -> PrivDir = ?config(priv_dir, Config), [{socket_type, {essl, - [{cacertfile, - filename:join(PrivDir, "public_key_cacert.pem")}, - {certfile, - filename:join(PrivDir, "public_key_cert.pem")}, - {keyfile, - filename:join(PrivDir, "public_key_cert_key.pem")} - ]}}] ++ server_config(http, Config). + [{nodelay, true}, + {cacertfile, + filename:join(PrivDir, "public_key_cacert.pem")}, + {certfile, + filename:join(PrivDir, "public_key_cert.pem")}, + {keyfile, + filename:join(PrivDir, "public_key_cert_key.pem")} + ]}}] ++ proplists:delete(socket_type, server_config(http, Config)). init_httpd(Group, Config0) -> Config1 = proplists:delete(port, Config0), -- cgit v1.2.3 From 51a236b296a07ef488e29e4f1b38ee4d707e8a88 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 9 Nov 2015 15:48:32 +0100 Subject: Inets: Clean up code Remove point less instructions looking for return values, that in most cases no long exist, of which the result would anyhow be ignored --- lib/inets/src/http_lib/http_transport.erl | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'lib/inets') diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index e3d7382043..ddfd6a030e 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -499,16 +499,4 @@ negotiate({essl, _}, Socket, Timeout) -> negotiate_ssl(Socket, Timeout). negotiate_ssl(Socket, Timeout) -> - case ssl:ssl_accept(Socket, Timeout) of - ok -> - ok; - {error, Reason} -> - %% Look for "valid" error reasons - ValidReasons = [timeout, econnreset, esslaccept, esslerrssl], - case lists:member(Reason, ValidReasons) of - true -> - {error, normal}; - false -> - {error, Reason} - end - end. + ssl:ssl_accept(Socket, Timeout). -- cgit v1.2.3 From ef9d44bdd378d30dd1233dbb89abc97ecc155868 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Wed, 11 Nov 2015 10:30:23 +0100 Subject: inets: Do not use internal or shell convenience functions in application ssl:start/[1,2] is a shell convenience function and should not be called by other applications. inet_db:start is an internal function that we should not have to call. This was done for legacy reasons and is no longer needed. --- lib/inets/src/http_lib/http_transport.erl | 34 ++++++++++--------------------- 1 file changed, 11 insertions(+), 23 deletions(-) (limited to 'lib/inets') diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index ddfd6a030e..ab6afe9c6c 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -49,39 +49,27 @@ %% start(SocketType) -> ok | {error, Reason} %% SocketType = ip_comm | {ssl, _} %% -%% Description: Makes sure inet_db or ssl is started. +%% Description: Makes sure ssl is started. %%------------------------------------------------------------------------- start(ip_comm) -> - do_start_ip_comm(); + ok; start({ip_comm, _}) -> - do_start_ip_comm(); -%% This is just for backward compatibillity + ok; start({ssl, _}) -> do_start_ssl(); start({essl, _}) -> do_start_ssl(). - -do_start_ip_comm() -> - case inet_db:start() of - {ok, _} -> - ok; - {error, {already_started, _}} -> - ok; - Error -> - Error - end. - do_start_ssl() -> - case ssl:start() of - ok -> - ok; - {error, {already_started,_}} -> - ok; - Error -> - Error + try lists:foreach(fun(App) -> + ok = application:ensure_started(App) + end, + [crypto, asn1, public_key, ssl]) + catch + _:Reason -> + {error, Reason} end. - + %%------------------------------------------------------------------------- %% connect(SocketType, Address, Options, Timeout) -> -- cgit v1.2.3 From 35214e082400c8a0a072308986271a28a7a1cc30 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Wed, 11 Nov 2015 15:00:29 +0100 Subject: inets: Prepare for release --- lib/inets/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/inets') diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index 480caeca4b..7cc95fa6d3 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -19,6 +19,6 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 6.0.2 +INETS_VSN = 6.0.3 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" -- cgit v1.2.3 From b1915f1e807614f0c74af4de6773e4f1656ca1a2 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Thu, 12 Nov 2015 10:50:18 +0100 Subject: Update release notes --- lib/inets/doc/src/notes.xml | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) (limited to 'lib/inets') diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index ef11fdc10c..8c4fdfdf70 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -33,7 +33,40 @@ notes.xml -
Inets 6.0.2 +
Inets 6.0.3 + +
Fixed Bugs and Malfunctions + + +

+ Improved error handling and gracfully termination when an + invalid chunked length header is encountered.

+

+ Own Id: OTP-13061

+
+
+
+ + +
Improvements and New Features + + +

+ Add possibility to set socket options, such as nodelay, + for httpd. Also phase out legacy option value inet6bf4 + for the ipfamily option. This value will be translated to + the value inet.

+

+ *** POTENTIAL INCOMPATIBILITY ***

+

+ Own Id: OTP-13062

+
+
+
+ +
+ +
Inets 6.0.2
Fixed Bugs and Malfunctions -- cgit v1.2.3