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.