1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 2008-2013. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: 20: %% Test suite for erlang:decode_packet/3 21: 22: -module(decode_packet_SUITE). 23: 24: -include_lib("test_server/include/test_server.hrl"). 25: 26: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 27: init_per_group/2,end_per_group/2, 28: init_per_testcase/2,end_per_testcase/2, 29: basic/1, packet_size/1, neg/1, http/1, line/1, ssl/1, otp_8536/1, 30: otp_9389/1, otp_9389_line/1]). 31: 32: suite() -> [{ct_hooks,[ts_install_cth]}]. 33: 34: all() -> 35: [basic, packet_size, neg, http, line, ssl, otp_8536, 36: otp_9389, otp_9389_line]. 37: 38: groups() -> 39: []. 40: 41: init_per_suite(Config) -> 42: Config. 43: 44: end_per_suite(_Config) -> 45: ok. 46: 47: init_per_group(_GroupName, Config) -> 48: Config. 49: 50: end_per_group(_GroupName, Config) -> 51: Config. 52: 53: 54: init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> 55: Seed = {S1,S2,S3} = now(), 56: random:seed(S1,S2,S3), 57: io:format("*** SEED: ~p ***\n", [Seed]), 58: Dog=?t:timetrap(?t:minutes(1)), 59: [{watchdog, Dog}|Config]. 60: 61: end_per_testcase(_Func, Config) -> 62: Dog=?config(watchdog, Config), 63: ?t:timetrap_cancel(Dog). 64: 65: basic(doc) -> []; 66: basic(suite) -> []; 67: basic(Config) when is_list(Config) -> 68: ?line Packet = <<101,22,203,54,175>>, 69: ?line Rest = <<123,34,0,250>>, 70: ?line Bin = <<Packet/binary,Rest/binary>>, 71: ?line {ok, Bin, <<>>} = decode_pkt(raw,Bin), 72: 73: ?line {more, 5+1} = decode_pkt(1,<<5,1,2,3,4>>), 74: ?line {more, 5+2} = decode_pkt(2,<<0,5,1,2,3,4>>), 75: ?line {more, 5+4} = decode_pkt(4,<<0,0,0,5,1,2,3,4>>), 76: 77: ?line {more, undefined} = decode_pkt(1,<<>>), 78: ?line {more, undefined} = decode_pkt(2,<<0>>), 79: ?line {more, undefined} = decode_pkt(4,<<0,0,0>>), 80: 81: Types = [1,2,4,asn1,sunrm,cdr,fcgi,tpkt,ssl_tls], 82: 83: %% Run tests for different header types and bit offsets. 84: 85: lists:foreach(fun({Type,Bits})->basic_pack(Type,Packet,Rest,Bits), 86: more_length(Type,Packet,Bits) end, 87: [{T,B} || T<-Types, B<-lists:seq(0,32)]), 88: ok. 89: 90: basic_pack(Type,Body,Rest,BitOffs) -> 91: ?line {Bin,Unpacked,_} = pack(Type,Body,Rest,BitOffs), 92: ?line {ok, Unpacked, Rest} = decode_pkt(Type,Bin), 93: case Rest of 94: <<>> -> ok; 95: _ -> 96: ?line <<_:1,NRest/bits>> = Rest, 97: basic_pack(Type,Body,NRest,BitOffs) 98: end. 99: 100: more_length(Type,Body,BitOffs) -> 101: ?line {Bin,_,_} = pack(Type,Body,<<>>,BitOffs), 102: HdrSize = byte_size(Bin) - byte_size(Body), 103: more_length_do(Type,HdrSize,Bin,byte_size(Bin)). 104: 105: more_length_do(_,_,_,0) -> 106: ok; 107: more_length_do(Type,HdrSize,Bin,Size) -> 108: TrySize = (Size*3) div 4, 109: NSize = if TrySize < HdrSize -> Size - 1; 110: true -> TrySize 111: end, 112: {B1,_} = split_binary(Bin,NSize), 113: ?line {more, Length} = decode_pkt(Type,B1), 114: case Length of 115: L when L=:=byte_size(Bin) -> ok; 116: undefined when NSize<HdrSize -> ok 117: end, 118: more_length_do(Type,HdrSize,Bin,NSize). 119: 120: 121: 122: pack(Type,Packet,Rest) -> 123: {Bin,Unpacked} = pack(Type,Packet), 124: {<<Bin/binary,Rest/bits>>,Unpacked}. 125: 126: %pack(0,B,R,Bits) -> 127: % pack(raw,B,R,Bits); 128: %pack(raw,Body,Rest,BitOffs) -> 129: % Orig = <<0:BitOffs,Body/binary,Rest/bits>>, 130: % <<_:BitOffs,Bin/bits>> = Orig, 131: % {Bin,<<Bin/binary,Rest/bits>>,Orig}; 132: pack(Type,Body,Rest,BitOffs) -> 133: {Packet,Unpacked} = pack(Type,Body), 134: 135: %% Make Bin a sub-bin with an arbitrary bitoffset within Orig 136: Prefix = random:uniform(1 bsl BitOffs) - 1, 137: Orig = <<Prefix:BitOffs,Packet/binary,Rest/bits>>, 138: <<_:BitOffs,Bin/bits>> = Orig, 139: {Bin,Unpacked,Orig}. 140: 141: pack(1,Bin) -> 142: Psz = byte_size(Bin), 143: {<<Psz:8,Bin/binary>>, Bin}; 144: pack(2,Bin) -> 145: Psz = byte_size(Bin), 146: {<<Psz:16,Bin/binary>>, Bin}; 147: pack(4,Bin) -> 148: Psz = byte_size(Bin), 149: {<<Psz:32,Bin/binary>>, Bin}; 150: pack(asn1,Bin) -> 151: Ident = case random:uniform(3) of 152: 1 -> <<17>>; 153: 2 -> <<16#1f,16#81,17>>; 154: 3 -> <<16#1f,16#81,16#80,16#80,17>> 155: end, 156: Psz = byte_size(Bin), 157: Length = case random:uniform(4) of 158: 1 when Psz < 128 -> 159: <<Psz:8>>; 160: R when R=<2 andalso Psz < 16#10000 -> 161: <<16#82,Psz:16>>; 162: R when R=<3 andalso Psz < 16#1000000 -> 163: <<16#83,Psz:24>>; 164: _ when Psz < 16#100000000 -> 165: <<16#84,Psz:32>> 166: end, 167: Res = <<Ident/binary,Length/binary,Bin/binary>>, 168: {Res,Res}; 169: pack(sunrm,Bin) -> 170: Psz = byte_size(Bin), 171: Res = if Psz < 16#80000000 -> 172: <<Psz:32,Bin/binary>> 173: end, 174: {Res,Res}; 175: pack(cdr,Bin) -> 176: GIOP = <<"GIOP">>, 177: Major = random:uniform(256) - 1, 178: Minor = random:uniform(256) - 1, 179: MType = random:uniform(256) - 1, 180: Psz = byte_size(Bin), 181: Res = case random:uniform(2) of 182: 1 -> <<GIOP/binary,Major:8,Minor:8,0:8,MType:8,Psz:32/big,Bin/binary>>; 183: 2 -> <<GIOP/binary,Major:8,Minor:8,1:8,MType:8,Psz:32/little,Bin/binary>> 184: end, 185: {Res,Res}; 186: pack(fcgi,Bin) -> 187: Ver = 1, 188: Type = random:uniform(256) - 1, 189: Id = random:uniform(65536) - 1, 190: PaddSz = random:uniform(16) - 1, 191: Psz = byte_size(Bin), 192: Reserv = random:uniform(256) - 1, 193: Padd = case PaddSz of 194: 0 -> <<>>; 195: _ -> list_to_binary([random:uniform(256)-1 196: || _<- lists:seq(1,PaddSz)]) 197: end, 198: Res = <<Ver:8,Type:8,Id:16,Psz:16/big,PaddSz:8,Reserv:8,Bin/binary>>, 199: {<<Res/binary,Padd/binary>>, Res}; 200: pack(tpkt,Bin) -> 201: Ver = 3, 202: Reserv = random:uniform(256) - 1, 203: Size = byte_size(Bin) + 4, 204: Res = <<Ver:8,Reserv:8,Size:16,Bin/binary>>, 205: {Res, Res}; 206: pack(ssl_tls,Bin) -> 207: Content = case (random:uniform(256) - 1) of 208: C when C<128 -> C; 209: _ -> v2hello 210: end, 211: Major = random:uniform(256) - 1, 212: Minor = random:uniform(256) - 1, 213: pack_ssl(Content,Major,Minor,Bin). 214: 215: pack_ssl(Content, Major, Minor, Body) -> 216: case Content of 217: v2hello -> 218: Size = byte_size(Body), 219: Res = <<1:1,(Size+3):15, 1:8, Major:8, Minor:8, Body/binary>>, 220: C = 22, 221: Data = <<1:8, (Size+2):24, Major:8, Minor:8, Body/binary>>; 222: C when is_integer(C) -> 223: Size = byte_size(Body), 224: Res = <<Content:8, Major:8, Minor:8, Size:16, Body/binary>>, 225: Data = Body 226: end, 227: {Res, {ssl_tls,[],C,{Major,Minor}, Data}}. 228: 229: 230: packet_size(doc) -> []; 231: packet_size(suite) -> []; 232: packet_size(Config) when is_list(Config) -> 233: ?line Packet = <<101,22,203,54,175>>, 234: ?line Rest = <<123,34,0,250>>, 235: 236: F = fun({Type,Max})-> 237: ?line {Bin,Unpacked} = pack(Type,Packet,Rest), 238: ?line case decode_pkt(Type,Bin,[{packet_size,Max}]) of 239: {ok,Unpacked,Rest} when Max=:=0; Max>=byte_size(Packet) -> 240: ok; 241: {error,_} when Max<byte_size(Packet), Max=/=0 -> 242: ok; 243: {error,_} when Type=:=fcgi, Max=/=0 -> 244: %% packet includes random amount of padding 245: ok 246: end 247: end, 248: ?line lists:foreach(F, [{T,D} || T<-[1,2,4,asn1,sunrm,cdr,fcgi,tpkt,ssl_tls], 249: D<-lists:seq(0, byte_size(Packet)*2)]), 250: 251: %% Test OTP-8102, "negative" 4-byte sizes. 252: lists:foreach(fun(Size) -> 253: ?line {error,_} = decode_pkt(4,<<Size:32,Packet/binary>>) 254: end, 255: lists:seq(-10,-1)), 256: 257: %% Test OTP-9389, long HTTP header lines. 258: Opts = [{packet_size, 128}], 259: Pkt = list_to_binary(["GET / HTTP/1.1\r\nHost: localhost\r\nLink: /", 260: string:chars($Y, 64), "\r\n\r\n"]), 261: <<Pkt1:50/binary, Pkt2/binary>> = Pkt, 262: ?line {ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest1} = 263: erlang:decode_packet(http, Pkt1, Opts), 264: ?line {ok, {http_header,_,'Host',_,"localhost"}, Rest2} = 265: erlang:decode_packet(httph, Rest1, Opts), 266: ?line {more, undefined} = erlang:decode_packet(httph, Rest2, Opts), 267: ?line {ok, {http_header,_,"Link",_,_}, _} = 268: erlang:decode_packet(httph, list_to_binary([Rest2, Pkt2]), Opts), 269: 270: Pkt3 = list_to_binary(["GET / HTTP/1.1\r\nHost: localhost\r\nLink: /", 271: string:chars($Y, 129), "\r\n\r\n"]), 272: ?line {ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest3} = 273: erlang:decode_packet(http, Pkt3, Opts), 274: ?line {ok, {http_header,_,'Host',_,"localhost"}, Rest4} = 275: erlang:decode_packet(httph, Rest3, Opts), 276: ?line {error, invalid} = erlang:decode_packet(httph, Rest4, Opts), 277: 278: ok. 279: 280: 281: neg(doc) -> []; 282: neg(suite) -> []; 283: neg(Config) when is_list(Config) -> 284: ?line Bin = <<"dummy">>, 285: Fun = fun()->dummy end, 286: 287: BadargF = fun(T,B,Opts)-> {'EXIT',{badarg,_}} = (catch decode_pkt(T,B,Opts)) end, 288: 289: %% Invalid Type args 290: lists:foreach(fun(T)-> BadargF(T,Bin,[]) end, 291: [3,-1,5,2.0,{2},unknown,[],"line",Bin,Fun,self()]), 292: 293: %% Invalid Bin args 294: lists:foreach(fun(B)-> BadargF(0,B,[]) end, 295: [3,2.0,unknown,[],"Bin",[Bin],{Bin},Fun,self()]), 296: 297: %% Invalid options 298: InvOpts = [2,false,self(),Bin,"Options",Fun, 299: packet_size,{packet_size},{packet_size,0,false}, 300: {packet_size,-1},{packet_size,100.0},{packet_size,false}, 301: {line_length,-1},{line_length,100.0},{line_length,false}], 302: 303: lists:foreach(fun(Opt)-> BadargF(0,Bin,Opt), 304: BadargF(0,Bin,[Opt]), 305: BadargF(0,Bin,[Opt,{packet_size,1000}]), 306: BadargF(0,Bin,[{packet_size,1000},Opt]) end, 307: InvOpts), 308: ok. 309: 310: 311: http(doc) -> []; 312: http(suite) -> []; 313: http(Config) when is_list(Config) -> 314: ?line <<"foo">> = http_do(http_request("foo")), 315: ?line <<" bar">> = http_do(http_request(" bar")), 316: ?line <<"Hello!">> = http_do(http_response("Hello!")), 317: 318: %% Test all known header atoms 319: Val = "dummy value", 320: ValB = list_to_binary(Val), 321: Rest = <<"Rest">>, 322: HdrF = fun(Str,N) -> 323: ?line StrA = list_to_atom(Str), 324: ?line StrB = list_to_binary(Str), 325: ?line Bin = <<StrB/binary,": ",ValB/binary,"\r\n",Rest/binary>>, 326: ?line {ok, {http_header,N,StrA,undefined,Val}, Rest} = decode_pkt(httph,Bin), 327: ?line {ok, {http_header,N,StrA,undefined,ValB}, Rest} = decode_pkt(httph_bin,Bin), 328: ?line N + 1 329: end, 330: ?line lists:foldl(HdrF, 1, http_hdr_strings()), 331: 332: %% Test all known method atoms 333: MethF = fun(Meth) -> 334: ?line MethA = list_to_atom(Meth), 335: ?line MethB = list_to_binary(Meth), 336: ?line Bin = <<MethB/binary," /invalid/url HTTP/1.0\r\n",Rest/binary>>, 337: ?line {ok, {http_request,MethA,{abs_path,"/invalid/url"},{1,0}}, 338: Rest} = decode_pkt(http,Bin), 339: ?line {ok, {http_request,MethA,{abs_path,<<"/invalid/url">>},{1,0}}, 340: Rest} = decode_pkt(http_bin,Bin) 341: end, 342: ?line lists:foreach(MethF, http_meth_strings()), 343: 344: %% Test all uri variants 345: UriF = fun({Str,ResL,ResB}) -> 346: Bin = <<"GET ",(list_to_binary(Str))/binary," HTTP/1.1\r\n",Rest/binary>>, 347: {ok, {http_request, 'GET', ResL, {1,1}}, Rest} = decode_pkt(http,Bin), 348: {ok, {http_request, 'GET', ResB, {1,1}}, Rest} = decode_pkt(http_bin,Bin) 349: end, 350: lists:foreach(UriF, http_uri_variants()), 351: 352: %% Response with empty phrase 353: ?line {ok,{http_response,{1,1},200,[]},<<>>} = decode_pkt(http, <<"HTTP/1.1 200\r\n">>, []), 354: ?line {ok,{http_response,{1,1},200,<<>>},<<>>} = decode_pkt(http_bin, <<"HTTP/1.1 200\r\n">>, []), 355: ok. 356: 357: http_with_bin(http) -> 358: http_bin; 359: http_with_bin(httph) -> 360: httph_bin. 361: 362: http_do(Tup) -> 363: http_do(Tup,http). 364: http_do({Bin, []}, _) -> 365: Bin; 366: http_do({Bin,[{_Line,PL,PB}|Tail]}, Type) -> 367: ?line {ok, PL, Rest} = decode_pkt(Type,Bin), 368: ?line {ok, PB, Rest} = decode_pkt(http_with_bin(Type),Bin), 369: 370: %% Same tests again but as SubBin 371: PreLen = random:uniform(64), 372: Prefix = random:uniform(1 bsl PreLen) - 1, 373: SufLen = random:uniform(64), 374: Suffix = random:uniform(1 bsl SufLen) - 1, 375: Orig = <<Prefix:PreLen, Bin/bits, Suffix:SufLen>>, 376: BinLen = bit_size(Bin), 377: <<_:PreLen, SubBin:BinLen/bits, _/bits>> = Orig, % Make SubBin 378: ?line SubBin = Bin, % just to make sure 379: 380: ?line {ok, PL, Rest} = decode_pkt(Type,SubBin), 381: ?line {ok, PB, Rest} = decode_pkt(http_with_bin(Type),SubBin), 382: http_do({Rest, Tail}, httph). 383: 384: http_request(Msg) -> 385: QnA = [{"POST /invalid/url HTTP/1.1\r\n", 386: {http_request, 'POST', {abs_path, "/invalid/url" }, {1,1}}, 387: {http_request, 'POST', {abs_path,<<"/invalid/url">>}, {1,1}}}, 388: {"Connection: close\r\n", 389: {http_header,2,'Connection',undefined, "close"}, 390: {http_header,2,'Connection',undefined,<<"close">>}}, 391: {"Host\t : localhost:8000\r\n", % white space before : 392: {http_header,14,'Host',undefined, "localhost:8000"}, 393: {http_header,14,'Host',undefined,<<"localhost:8000">>}}, 394: {"User-Agent: perl post\r\n", 395: {http_header,24,'User-Agent',undefined, "perl post"}, 396: {http_header,24,'User-Agent',undefined,<<"perl post">>}}, 397: {"Content-Length: 4\r\n", 398: {http_header,38,'Content-Length',undefined, "4"}, 399: {http_header,38,'Content-Length',undefined,<<"4">>}}, 400: {"Content-Type: text/xml; charset=utf-8\r\n", 401: {http_header,42,'Content-Type',undefined, "text/xml; charset=utf-8"}, 402: {http_header,42,'Content-Type',undefined,<<"text/xml; charset=utf-8">>}}, 403: {"Other-Field: with some text\r\n", 404: {http_header,0, "Other-Field" ,undefined, "with some text"}, 405: {http_header,0,<<"Other-Field">>,undefined,<<"with some text">>}}, 406: {"Make-sure-a-LONG-HEaDer-fIeLd-is-fORMATTED-NicelY: with some text\r\n", 407: {http_header,0, "Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely" ,undefined, "with some text"}, 408: {http_header,0,<<"Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely">>,undefined,<<"with some text">>}}, 409: {"Multi-Line: Once upon a time in a land far far away,\r\n" 410: " there lived a princess imprisoned in the highest tower\r\n" 411: " of the most haunted castle.\r\n", 412: {http_header,0, "Multi-Line" ,undefined, "Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle."}, 413: {http_header,0,<<"Multi-Line">>,undefined,<<"Once upon a time in a land far far away,\r\n there lived a princess imprisoned in the highest tower\r\n of the most haunted castle.">>}}, 414: {"\r\n", 415: http_eoh, 416: http_eoh}], 417: Bin = lists:foldl(fun({Line,_,_},Acc) -> LineBin = list_to_binary(Line), 418: <<Acc/binary,LineBin/binary>> end, 419: <<"">>, QnA), 420: MsgBin = list_to_binary(Msg), 421: {<<Bin/binary,MsgBin/binary>>, QnA}. 422: 423: 424: http_response(Msg) -> 425: QnA = [{"HTTP/1.0 404 Object Not Found\r\n", 426: {http_response, {1,0}, 404, "Object Not Found"}, 427: {http_response, {1,0}, 404, <<"Object Not Found">>}}, 428: {"Server: inets/4.7.16\r\n", 429: {http_header, 30, 'Server', undefined, "inets/4.7.16"}, 430: {http_header, 30, 'Server', undefined, <<"inets/4.7.16">>}}, 431: {"Date: Fri, 04 Jul 2008 17:16:22 GMT\r\n", 432: {http_header, 3, 'Date', undefined, "Fri, 04 Jul 2008 17:16:22 GMT"}, 433: {http_header, 3, 'Date', undefined, <<"Fri, 04 Jul 2008 17:16:22 GMT">>}}, 434: {"Content-Type: text/html\r\n", 435: {http_header, 42, 'Content-Type', undefined, "text/html"}, 436: {http_header, 42, 'Content-Type', undefined, <<"text/html">>}}, 437: {"Content-Length: 207\r\n", 438: {http_header, 38, 'Content-Length', undefined, "207"}, 439: {http_header, 38, 'Content-Length', undefined, <<"207">>}}, 440: {"\r\n", 441: http_eoh, 442: http_eoh}], 443: 444: 445: 446: Bin = lists:foldl(fun({Line,_,_},Acc) -> LineBin = list_to_binary(Line), 447: <<Acc/binary,LineBin/binary>> end, 448: <<"">>, QnA), 449: MsgBin = list_to_binary(Msg), 450: {<<Bin/binary,MsgBin/binary>>, QnA}. 451: 452: http_hdr_strings() -> 453: %% Must be correct order 454: ["Cache-Control","Connection","Date","Pragma","Transfer-Encoding", 455: "Upgrade","Via","Accept", "Accept-Charset", "Accept-Encoding", 456: "Accept-Language", "Authorization","From","Host","If-Modified-Since", 457: "If-Match","If-None-Match","If-Range","If-Unmodified-Since","Max-Forwards", 458: "Proxy-Authorization","Range","Referer","User-Agent","Age","Location", 459: "Proxy-Authenticate","Public","Retry-After","Server","Vary","Warning", 460: "Www-Authenticate","Allow","Content-Base","Content-Encoding", 461: "Content-Language","Content-Length","Content-Location","Content-Md5", 462: "Content-Range","Content-Type","Etag","Expires","Last-Modified", 463: "Accept-Ranges","Set-Cookie","Set-Cookie2","X-Forwarded-For","Cookie", 464: "Keep-Alive","Proxy-Connection"]. 465: 466: http_meth_strings() -> 467: ["OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE"]. 468: 469: http_uri_variants() -> 470: [{"*", '*', '*'}, 471: {"http://tools.ietf.org/html/rfc3986", 472: {absoluteURI,http, "tools.ietf.org", undefined, "/html/rfc3986"}, 473: {absoluteURI,http,<<"tools.ietf.org">>,undefined,<<"/html/rfc3986">>}}, 474: {"http://otp.ericsson.se:8000/product/internal/", 475: {absoluteURI,http, "otp.ericsson.se" ,8000, "/product/internal/"}, 476: {absoluteURI,http,<<"otp.ericsson.se">>,8000,<<"/product/internal/">>}}, 477: {"https://example.com:8042/over/there?name=ferret#nose", 478: {absoluteURI,https, "example.com", 8042, "/over/there?name=ferret#nose"}, 479: {absoluteURI,https,<<"example.com">>,8042,<<"/over/there?name=ferret#nose">>}}, 480: {"ftp://cnn.example.com&story=breaking_news@10.0.0.1/top_story.htm", 481: {scheme, "ftp", "//cnn.example.com&story=breaking_news@10.0.0.1/top_story.htm"}, 482: {scheme,<<"ftp">>,<<"//cnn.example.com&story=breaking_news@10.0.0.1/top_story.htm">>}}, 483: {"/some/absolute/path", 484: {abs_path, "/some/absolute/path"}, 485: {abs_path,<<"/some/absolute/path">>}}, 486: {"something_else", "something_else", <<"something_else">>}]. 487: 488: 489: line(doc) -> []; 490: line(suite) -> []; 491: line(Config) when is_list(Config) -> 492: Text = <<"POST /invalid/url HTTP/1.1\r\n" 493: "Connection: close\r\n" 494: "Host\t : localhost:8000\r\n" 495: "User-Agent: perl post\r\n" 496: "Content-Length: 4\r\n" 497: "Content-Type: text/xml; charset=utf-8\r\n" 498: "Other-Field: with some text\r\n" 499: "Multi-Line: Once upon a time in a land far far away,\r\n" 500: " there lived a princess imprisoned in the highest tower\r\n" 501: " of the most haunted castle.\r\n" 502: "\r\nThe residue">>, 503: 504: lists:foreach(fun(MaxLen) -> line_do(Text,MaxLen) end, 505: [0,7,19,29,37]), 506: ok. 507: 508: line_do(Bin,MaxLen) -> 509: Res = decode_pkt(line,Bin,[{line_length,MaxLen}]), 510: MyRes = decode_line(Bin,MaxLen), 511: ?line MyRes = Res, 512: case Res of 513: {ok,_,Rest} -> 514: line_do(Rest,MaxLen); 515: {more,undefined} -> 516: ok 517: end. 518: 519: % Emulates decode_packet(line,Bin,[{line_length,MaxLen}]) 520: decode_line(Bin,MaxLen) -> 521: ?line case find_in_binary($\n,Bin) of 522: notfound when MaxLen>0 andalso byte_size(Bin) >= MaxLen -> 523: {LineB,Rest} = split_binary(Bin,MaxLen), 524: {ok,LineB,Rest}; 525: notfound -> 526: {more,undefined}; 527: Pos when MaxLen>0 andalso Pos > MaxLen -> 528: {LineB,Rest} = split_binary(Bin,MaxLen), 529: {ok,LineB,Rest}; 530: Pos -> 531: {LineB,Rest} = split_binary(Bin,Pos), 532: {ok,LineB,Rest} 533: end. 534: 535: find_in_binary(Byte, Bin) -> 536: case string:chr(binary_to_list(Bin),Byte) of 537: 0 -> notfound; 538: P -> P 539: end. 540: 541: ssl(doc) -> []; 542: ssl(suite) -> []; 543: ssl(Config) when is_list(Config) -> 544: Major = 34, 545: Minor = 17, 546: Body = <<234,189,73,199,1,32,4,0,254>>, 547: Rest = <<23,123,203,12,234>>, 548: 549: F = fun(Content) -> 550: {Packet,Unpacked} = pack_ssl(Content, Major, Minor, Body), 551: Bin = <<Packet/binary,Rest/binary>>, 552: ?line {ok, Unpacked, Rest} = decode_pkt(ssl_tls, Bin) 553: end, 554: F(25), 555: F(v2hello), 556: ok. 557: 558: otp_8536(doc) -> ["Corrupt sub-binary-strings from httph_bin"]; 559: otp_8536(Config) when is_list(Config) -> 560: lists:foreach(fun otp_8536_do/1, lists:seq(1,50)), 561: ok. 562: 563: otp_8536_do(N) -> 564: Data = <<"some data 123">>, 565: Letters = <<"bcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba">>, 566: <<HdrTail:N/binary,_/binary>> = Letters, 567: Hdr = <<$A, HdrTail/binary>>, 568: Bin = <<Hdr/binary, ": ", Data/binary, "\r\n\r\n">>, 569: 570: io:format("Bin='~p'\n",[Bin]), 571: ?line {ok,{http_header,0,Hdr2,undefined,Data2},<<"\r\n">>} = decode_pkt(httph_bin, Bin, []), 572: 573: %% Do something to trash the C-stack, how about another decode_packet: 574: decode_pkt(httph_bin,<<Letters/binary, ": ", Data/binary, "\r\n\r\n">>, []), 575: 576: %% Now check that we got the expected binaries 577: {Hdr, Data} = {Hdr2, Data2}. 578: 579: decode_pkt(Type,Bin) -> 580: decode_pkt(Type,Bin,[]). 581: decode_pkt(Type,Bin,Opts) -> 582: %%io:format("decode_packet(~p,~p,~p)\n",[Type,Bin,Opts]), 583: Res = erlang:decode_packet(Type,Bin,Opts), 584: %%io:format(" -> ~p\n",[Res]), 585: Res. 586: 587: otp_9389(doc) -> ["Verify line_length works correctly for HTTP headers"]; 588: otp_9389(suite) -> []; 589: otp_9389(Config) when is_list(Config) -> 590: Opts = [{packet_size, 16384}, {line_length, 3000}], 591: Pkt = list_to_binary(["GET / HTTP/1.1\r\nHost: localhost\r\nLink: /", 592: string:chars($X, 8192), 593: "\r\nContent-Length: 0\r\n\r\n"]), 594: <<Pkt1:5000/binary, Pkt2/binary>> = Pkt, 595: {ok, {http_request,'GET',{abs_path,"/"},{1,1}}, Rest1} = 596: erlang:decode_packet(http, Pkt1, Opts), 597: {ok, {http_header,_,'Host',_,"localhost"}, Rest2} = 598: erlang:decode_packet(httph, Rest1, Opts), 599: {more, undefined} = erlang:decode_packet(httph, Rest2, Opts), 600: {ok, {http_header,_,"Link",_,Link}, Rest3} = 601: erlang:decode_packet(httph, list_to_binary([Rest2, Pkt2]), Opts), 602: true = (length(Link) > 8000), 603: {ok, {http_header,_,'Content-Length',_,"0"}, <<"\r\n">>} = 604: erlang:decode_packet(httph, Rest3, Opts), 605: ok. 606: 607: otp_9389_line(doc) -> ["Verify packet_size works correctly for line mode"]; 608: otp_9389_line(suite) -> []; 609: otp_9389_line(Config) when is_list(Config) -> 610: Opts = [{packet_size, 20}], 611: Line1 = <<"0123456789012345678\n">>, 612: Line2 = <<"0123456789\n">>, 613: Line3 = <<"01234567890123456789\n">>, 614: Pkt = list_to_binary([Line1, Line2, Line3]), 615: ?line {ok, Line1, Rest1} = erlang:decode_packet(line, Pkt, Opts), 616: ?line {ok, Line2, Rest2} = erlang:decode_packet(line, Rest1, Opts), 617: ?line {error, invalid} = erlang:decode_packet(line, Rest2, Opts), 618: ok.