1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2004-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21-module(httpc_response). 22 23-include_lib("inets/src/http_lib/http_internal.hrl"). 24-include("httpc_internal.hrl"). 25 26%% API 27%% Avoid warning for local function error/2 clashing with autoimported BIF. 28-compile({no_auto_import,[error/2]}). 29-export([parse/1, result/2, send/2, error/2, is_server_closing/1, 30 stream_start/3]). 31 32%% Callback API - used for example if the header/body is received a 33%% little at a time on a socket. 34-export([parse_version/1, parse_status_code/1, parse_reason_phrase/1, 35 parse_headers/1, whole_body/1, whole_body/2]). 36 37%%%========================================================================= 38%%% API 39%%%========================================================================= 40 41parse([Bin, MaxHeaderSize, Relaxed]) -> 42 parse_version(Bin, [], MaxHeaderSize, [], Relaxed). 43 44whole_body([Bin, Body, Length]) -> 45 whole_body(<<Body/binary, Bin/binary>>, Length). 46 47%% Functions that may be returned during the decoding process 48%% if the input data is incompleate. 49parse_version([Bin, Version, MaxHeaderSize, Result, Relaxed]) -> 50 parse_version(Bin, Version, MaxHeaderSize, Result, Relaxed). 51 52parse_status_code([Bin, Code, MaxHeaderSize, Result, Relaxed]) -> 53 parse_status_code(Bin, Code, MaxHeaderSize, Result, Relaxed). 54 55parse_reason_phrase([Bin, Rest, Phrase, MaxHeaderSize, Result, Relaxed]) -> 56 parse_reason_phrase(<<Rest/binary, Bin/binary>>, Phrase, 57 MaxHeaderSize, Result, Relaxed). 58 59parse_headers([Bin, Rest,Header, Headers, MaxHeaderSize, Result, Relaxed]) -> 60 parse_headers(<<Rest/binary, Bin/binary>>, Header, Headers, 61 MaxHeaderSize, Result, Relaxed). 62 63whole_body(Body, Length) -> 64 case size(Body) of 65 N when (N < Length) andalso (N > 0) -> 66 {?MODULE, whole_body, [Body, Length]}; 67 %% OBS! The Server may close the connection to indicate that the 68 %% whole body is now sent instead of sending a lengh 69 %% indicator.In this case the lengh indicator will be 70 %% -1. 71 N when (N >= Length) andalso (Length >= 0) -> 72 %% Potential trailing garbage will be thrown away in 73 %% format_response/1 Some servers may send a 100-continue 74 %% response without the client requesting it through an 75 %% expect header in this case the trailing bytes may be 76 %% part of the real response message. 77 {ok, Body}; 78 _ -> %% Length == -1 79 {?MODULE, whole_body, [Body, Length]} 80 end. 81 82%%------------------------------------------------------------------------- 83%% result(Response, Request) -> 84%% Response - {StatusLine, Headers, Body} 85%% Request - #request{} 86%% 87%% Description: Checks the status code ... 88%%------------------------------------------------------------------------- 89result(Response = {{_, Code,_}, _, _}, 90 Request = #request{stream = Stream}) 91 when ((Code =:= 200) orelse (Code =:= 206)) andalso (Stream =/= none) -> 92 stream_end(Response, Request); 93 94result(Response = {{_,100,_}, _, _}, Request) -> 95 status_continue(Response, Request); 96 97%% In redirect loop 98result(Response = {{_, Code, _}, _, _}, Request = 99 #request{redircount = Redirects, 100 settings = #http_options{autoredirect = true}}) 101 when ((Code div 100) =:= 3) andalso (Redirects > ?HTTP_MAX_REDIRECTS) -> 102 transparent(Response, Request); 103 104%% multiple choices 105result(Response = {{_, 300, _}, _, _}, 106 Request = #request{settings = 107 #http_options{autoredirect = 108 true}}) -> 109 redirect(Response, Request); 110 111result(Response = {{_, Code, _}, _, _}, 112 Request = #request{settings = 113 #http_options{autoredirect = true}, 114 method = post}) when (Code =:= 301) orelse 115 (Code =:= 302) orelse 116 (Code =:= 303) -> 117 redirect(Response, Request#request{method = get}); 118result(Response = {{_, Code, _}, _, _}, 119 Request = #request{settings = 120 #http_options{autoredirect = true}, 121 method = post}) when (Code =:= 307) -> 122 redirect(Response, Request); 123result(Response = {{_, Code, _}, _, _}, 124 Request = #request{settings = 125 #http_options{autoredirect = true}, 126 method = Method}) when (Code =:= 301) orelse 127 (Code =:= 302) orelse 128 (Code =:= 303) orelse 129 (Code =:= 307) -> 130 case lists:member(Method, [get, head, options, trace]) of 131 true -> 132 redirect(Response, Request); 133 false -> 134 transparent(Response, Request) 135 end; 136 137result(Response = {{_,503,_}, _, _}, Request) -> 138 status_service_unavailable(Response, Request); 139result(Response = {{_,Code,_}, _, _}, Request) when (Code div 100) =:= 5 -> 140 status_server_error_50x(Response, Request); 141 142result(Response, Request) -> 143 transparent(Response, Request). 144 145send(Receiver, Msg) when is_pid(Receiver) -> 146 Receiver ! {http, Msg}; 147send(Receiver, Msg) when is_function(Receiver) -> 148 (catch Receiver(Msg)); 149send({Module, Function, Args}, Msg) -> 150 (catch apply(Module, Function, [Msg | Args])). 151 152 153%%%======================================================================== 154%%% Internal functions 155%%%======================================================================== 156parse_version(<<>>, Version, MaxHeaderSize, Result, Relaxed) -> 157 {?MODULE, parse_version, [Version, MaxHeaderSize,Result, Relaxed]}; 158parse_version(<<?SP, Rest/binary>>, Version, 159 MaxHeaderSize, Result, Relaxed) -> 160 case lists:reverse(Version) of 161 "HTTP/" ++ _ = Newversion -> 162 parse_status_code(Rest, [], MaxHeaderSize, 163 [Newversion | Result], Relaxed); 164 NewVersion -> 165 throw({error, {invalid_version, NewVersion}}) 166 end; 167 168parse_version(<<Octet, Rest/binary>>, Version, 169 MaxHeaderSize, Result, Relaxed) -> 170 parse_version(Rest, [Octet | Version], MaxHeaderSize,Result, Relaxed). 171 172parse_status_code(<<>>, StatusCodeStr, MaxHeaderSize, Result, Relaxed) -> 173 {?MODULE, parse_status_code, 174 [StatusCodeStr, MaxHeaderSize, Result, Relaxed]}; 175 176%% Some Apache servers has been known to leave out the reason phrase, 177%% in relaxed mode we will allow this. 178parse_status_code(<<?CR>> = Data, StatusCodeStr, 179 MaxHeaderSize, Result, true) -> 180 {?MODULE, parse_status_code, 181 [Data, StatusCodeStr, MaxHeaderSize, Result, true]}; 182parse_status_code(<<?LF>>, StatusCodeStr, 183 MaxHeaderSize, Result, true) -> 184 %% If ?CR is is missing RFC2616 section-19.3 185 parse_status_code(<<?CR, ?LF>>, StatusCodeStr, 186 MaxHeaderSize, Result, true); 187 188parse_status_code(<<?CR, ?LF, Rest/binary>>, StatusCodeStr, 189 MaxHeaderSize, Result, true) -> 190 parse_headers(Rest, [], [], MaxHeaderSize, 191 [" ", list_to_integer(lists:reverse( 192 string:trim(StatusCodeStr))) 193 | Result], true); 194 195parse_status_code(<<?SP, Rest/binary>>, StatusCodeStr, 196 MaxHeaderSize, Result, Relaxed) -> 197 parse_reason_phrase(Rest, [], MaxHeaderSize, 198 [list_to_integer(lists:reverse(StatusCodeStr)) | 199 Result], Relaxed); 200 201parse_status_code(<<Octet, Rest/binary>>, StatusCodeStr, 202 MaxHeaderSize,Result, Relaxed) -> 203 parse_status_code(Rest, [Octet | StatusCodeStr], MaxHeaderSize, Result, 204 Relaxed). 205 206parse_reason_phrase(<<>>, Phrase, MaxHeaderSize, Result, Relaxed) -> 207 {?MODULE, parse_reason_phrase, 208 [<<>>, Phrase, MaxHeaderSize, Result, Relaxed]}; 209 210parse_reason_phrase(<<?CR, ?LF, ?LF, Body/binary>>, Phrase, 211 MaxHeaderSize, Result, Relaxed) -> 212 %% If ?CR is is missing RFC2616 section-19.3 213 parse_reason_phrase(<<?CR, ?LF, ?CR, ?LF, Body/binary>>, Phrase, 214 MaxHeaderSize, Result, Relaxed); 215 216parse_reason_phrase(<<?CR, ?LF, ?CR, ?LF, Body/binary>>, Phrase, 217 _, Result, _) -> 218 ResponseHeaderRcord = 219 http_response:headers([], #http_response_h{}), 220 {ok, list_to_tuple( 221 lists:reverse([Body, ResponseHeaderRcord | 222 [lists:reverse(Phrase) | Result]]))}; 223 224parse_reason_phrase(<<?CR, ?LF, ?CR>> = Data, Phrase, MaxHeaderSize, Result, 225 Relaxed) -> 226 {?MODULE, parse_reason_phrase, [Data, Phrase, MaxHeaderSize, Result], 227 Relaxed}; 228 229parse_reason_phrase(<<?CR, ?LF>> = Data, Phrase, MaxHeaderSize, Result, 230 Relaxed) -> 231 {?MODULE, parse_reason_phrase, [Data, Phrase, MaxHeaderSize, Result, 232 Relaxed]}; 233parse_reason_phrase(<<?LF, Rest/binary>>, Phrase, 234 MaxHeaderSize, Result, Relaxed) -> 235 %% If ?CR is is missing RFC2616 section-19.3 236 parse_reason_phrase(<<?CR, ?LF, Rest/binary>>, Phrase, 237 MaxHeaderSize, Result, Relaxed); 238parse_reason_phrase(<<?CR, ?LF, Rest/binary>>, Phrase, 239 MaxHeaderSize, Result, Relaxed) -> 240 parse_headers(Rest, [], [], MaxHeaderSize, 241 [lists:reverse(Phrase) | Result], Relaxed); 242parse_reason_phrase(<<?LF>>, Phrase, MaxHeaderSize, Result, Relaxed) -> 243 %% If ?CR is is missing RFC2616 section-19.3 244 parse_reason_phrase(<<?CR, ?LF>>, Phrase, MaxHeaderSize, Result, 245 Relaxed); 246parse_reason_phrase(<<?CR>> = Data, Phrase, MaxHeaderSize, Result, Relaxed) -> 247 {?MODULE, parse_reason_phrase, 248 [Data, Phrase, MaxHeaderSize, Result, Relaxed]}; 249parse_reason_phrase(<<Octet, Rest/binary>>, Phrase, MaxHeaderSize, Result, 250 Relaxed) -> 251 parse_reason_phrase(Rest, [Octet | Phrase], MaxHeaderSize, 252 Result, Relaxed). 253 254parse_headers(<<>>, Header, Headers, MaxHeaderSize, Result, Relaxed) -> 255 {?MODULE, parse_headers, [<<>>, Header, Headers, MaxHeaderSize, Result, 256 Relaxed]}; 257 258parse_headers(<<?CR,?LF,?LF,Body/binary>>, Header, Headers, 259 MaxHeaderSize, Result, Relaxed) -> 260 %% If ?CR is is missing RFC2616 section-19.3 261 parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, 262 MaxHeaderSize, Result, Relaxed); 263 264parse_headers(<<?LF,?LF,Body/binary>>, Header, Headers, 265 MaxHeaderSize, Result, Relaxed) -> 266 %% If ?CR is is missing RFC2616 section-19.3 267 parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, 268 MaxHeaderSize, Result, Relaxed); 269 270parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, 271 MaxHeaderSize, Result, Relaxed) -> 272 HTTPHeaders = [lists:reverse(Header) | Headers], 273 Length = lists:foldl(fun(H, Acc) -> length(H) + Acc end, 274 0, HTTPHeaders), 275 case ((Length =< MaxHeaderSize) or (MaxHeaderSize == nolimit)) of 276 true -> 277 ResponseHeaderRcord = 278 http_response:headers(HTTPHeaders, #http_response_h{}), 279 280 %% RFC7230, Section 3.3.3 281 %% If a message is received with both a Transfer-Encoding and a 282 %% Content-Length header field, the Transfer-Encoding overrides the 283 %% Content-Length. Such a message might indicate an attempt to 284 %% perform request smuggling (Section 9.5) or response splitting 285 %% (Section 9.4) and ought to be handled as an error. A sender MUST 286 %% remove the received Content-Length field prior to forwarding such 287 %% a message downstream. 288 case ResponseHeaderRcord#http_response_h.'transfer-encoding' of 289 undefined -> 290 {ok, list_to_tuple( 291 lists:reverse([Body, ResponseHeaderRcord | Result]))}; 292 Value -> 293 TransferEncoding = string:lowercase(Value), 294 ContentLength = ResponseHeaderRcord#http_response_h.'content-length', 295 if 296 %% Respond without error but remove Content-Length field in relaxed mode 297 (Relaxed =:= true) 298 andalso (TransferEncoding =:= "chunked") 299 andalso (ContentLength =/= "-1") -> 300 ResponseHeaderRcordFixed = 301 ResponseHeaderRcord#http_response_h{'content-length' = "-1"}, 302 {ok, list_to_tuple( 303 lists:reverse([Body, ResponseHeaderRcordFixed | Result]))}; 304 %% Respond with error in default (not relaxed) mode 305 (Relaxed =:= false) 306 andalso (TransferEncoding =:= "chunked") 307 andalso (ContentLength =/= "-1") -> 308 throw({error, {headers_conflict, {'content-length', 309 'transfer-encoding'}}}); 310 true -> 311 {ok, list_to_tuple( 312 lists:reverse([Body, ResponseHeaderRcord | Result]))} 313 end 314 end; 315 false -> 316 throw({error, {header_too_long, MaxHeaderSize, 317 MaxHeaderSize-Length}}) 318 end; 319parse_headers(<<?CR,?LF,?CR>> = Data, Header, Headers, 320 MaxHeaderSize, Result, Relaxed) -> 321 {?MODULE, parse_headers, [Data, Header, Headers, 322 MaxHeaderSize, Result, Relaxed]}; 323parse_headers(<<?CR,?LF>> = Data, Header, Headers, 324 MaxHeaderSize, Result, Relaxed) -> 325 {?MODULE, parse_headers, [Data, Header, Headers, MaxHeaderSize, 326 Result, Relaxed]}; 327parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, 328 MaxHeaderSize, Result, Relaxed) -> 329 parse_headers(Rest, [Octet], 330 [lists:reverse(Header) | Headers], MaxHeaderSize, 331 Result, Relaxed); 332parse_headers(<<?CR>> = Data, Header, Headers, 333 MaxHeaderSize, Result, Relaxed) -> 334 {?MODULE, parse_headers, [Data, Header, Headers, MaxHeaderSize, 335 Result, Relaxed]}; 336 337parse_headers(<<?LF>>, Header, Headers, 338 MaxHeaderSize, Result, Relaxed) -> 339 %% If ?CR is is missing RFC2616 section-19.3 340 parse_headers(<<?CR, ?LF>>, Header, Headers, 341 MaxHeaderSize, Result, Relaxed); 342 343parse_headers(<<Octet, Rest/binary>>, Header, Headers, 344 MaxHeaderSize, Result, Relaxed) -> 345 parse_headers(Rest, [Octet | Header], Headers, MaxHeaderSize, 346 Result, Relaxed). 347 348 349%% RFC2616, Section 10.1.1 350%% Note: 351%% - Only act on the 100 status if the request included the 352%% "Expect:100-continue" header, otherwise just ignore this response. 353status_continue(_, #request{headers = 354 #http_request_h{expect = "100-continue"}}) -> 355 continue; 356 357status_continue({_,_, Data}, _) -> 358 %% The data in the body in this case is actually part of the real 359 %% response sent after the "fake" 100-continue. 360 {ignore, Data}. 361 362status_service_unavailable(Response = {_, Headers, _}, Request) -> 363 case Headers#http_response_h.'retry-after' of 364 undefined -> 365 status_server_error_50x(Response, Request); 366 Time when (length(Time) < 3) -> % Wait only 99 s or less 367 NewTime = list_to_integer(Time) * 1000, % time in ms 368 {_, Data} = format_response(Response), 369 {retry, {NewTime, Request}, Data}; 370 _ -> 371 status_server_error_50x(Response, Request) 372 end. 373 374status_server_error_50x(Response, Request) -> 375 {Msg, _} = format_response(Response), 376 {stop, {Request#request.id, Msg}}. 377 378 379redirect(Response = {_, Headers, _}, Request) -> 380 {_, Data} = format_response(Response), 381 case Headers#http_response_h.location of 382 undefined -> 383 transparent(Response, Request); 384 RedirUrl -> 385 Brackets = Request#request.ipv6_host_with_brackets, 386 case uri_string:parse(RedirUrl) of 387 {error, Reason, _} -> 388 {ok, error(Request, Reason), Data}; 389 %% Automatic redirection 390 URI -> 391 {Host, Port0} = Request#request.address, 392 Port = maybe_to_integer(Port0), 393 Path = Request#request.path, 394 Scheme = atom_to_list(Request#request.scheme), 395 Query = Request#request.pquery, 396 URIMap = resolve_uri(Scheme, Host, Port, Path, Query, URI), 397 TScheme = list_to_atom(maps:get(scheme, URIMap)), 398 THost = http_util:maybe_add_brackets(maps:get(host, URIMap), Brackets), 399 TPort = maps:get(port, URIMap), 400 TPath = maps:get(path, URIMap), 401 TQuery = add_question_mark(maps:get(query, URIMap, "")), 402 NewURI = uri_string:normalize( 403 uri_string:recompose(URIMap)), 404 HostPort = http_request:normalize_host(TScheme, THost, TPort), 405 NewHeaders = 406 (Request#request.headers)#http_request_h{host = HostPort}, 407 NewRequest = 408 Request#request{redircount = 409 Request#request.redircount+1, 410 scheme = TScheme, 411 headers = NewHeaders, 412 address = {THost,TPort}, 413 path = TPath, 414 pquery = TQuery, 415 abs_uri = NewURI}, 416 {redirect, NewRequest, Data} 417 end 418 end. 419 420add_question_mark(<<>>) -> 421 <<>>; 422add_question_mark([]) -> 423 []; 424add_question_mark(Comp) when is_binary(Comp) -> 425 <<$?, Comp/binary>>; 426add_question_mark(Comp) when is_list(Comp) -> 427 [$?|Comp]. 428 429%% RFC3986 - 5.2.2. Transform References 430resolve_uri(Scheme, Host, Port, Path, Query, URI) -> 431 resolve_uri(Scheme, Host, Port, Path, Query, URI, #{}). 432%% 433resolve_uri(Scheme, Host, Port, Path, Query, URI, Map0) -> 434 case maps:get(scheme, URI, undefined) of 435 undefined -> 436 Port0 = get_port(Scheme, URI), 437 Map = Map0#{scheme => Scheme, 438 port => Port0}, 439 resolve_authority(Host, Port, Path, Query, URI, Map); 440 URIScheme -> 441 Port0 = get_port(URIScheme, URI), 442 maybe_add_query( 443 Map0#{scheme => URIScheme, 444 host => maps:get(host, URI), 445 port => Port0, 446 path => maps:get(path, URI)}, 447 URI) 448 end. 449 450 451get_port(Scheme, URI) -> 452 case maps:get(port, URI, undefined) of 453 undefined -> 454 get_default_port(Scheme); 455 Port -> 456 Port 457 end. 458 459 460get_default_port("http") -> 461 80; 462get_default_port("https") -> 463 443. 464 465 466resolve_authority(Host, Port, Path, Query, RelURI, Map) -> 467 case maps:is_key(host, RelURI) of 468 true -> 469 maybe_add_query( 470 Map#{host => maps:get(host, RelURI), 471 path => maps:get(path, RelURI)}, 472 RelURI); 473 false -> 474 Map1 = Map#{host => Host, 475 port => Port}, 476 resolve_path(Path, Query, RelURI, Map1) 477 end. 478 479 480maybe_add_query(Map, RelURI) -> 481 case maps:is_key(query, RelURI) of 482 true -> 483 Map#{query => maps:get(query, RelURI)}; 484 false -> 485 Map 486 end. 487 488 489resolve_path(Path, Query, RelURI, Map) -> 490 case maps:is_key(path, RelURI) of 491 true -> 492 Path1 = calculate_path(Path, maps:get(path, RelURI)), 493 maybe_add_query( 494 Map#{path => Path1}, 495 RelURI); 496 false -> 497 Map1 = Map#{path => Path}, 498 resolve_query(Query, RelURI, Map1) 499 end. 500 501 502calculate_path(BaseP, RelP) -> 503 case starts_with_slash(RelP) of 504 true -> 505 RelP; 506 false -> 507 merge_paths(BaseP, RelP) 508 end. 509 510 511starts_with_slash([$/|_]) -> 512 true; 513starts_with_slash(<<$/,_/binary>>) -> 514 true; 515starts_with_slash(_) -> 516 false. 517 518 519%% RFC3986 - 5.2.3. Merge Paths 520merge_paths("", RelP) -> 521 [$/|RelP]; 522merge_paths(BaseP, RelP) when is_list(BaseP) -> 523 do_merge_paths(lists:reverse(BaseP), RelP); 524merge_paths(BaseP, RelP) when is_binary(BaseP) -> 525 B = binary_to_list(BaseP), 526 R = binary_to_list(RelP), 527 Res = merge_paths(B, R), 528 list_to_binary(Res). 529 530 531do_merge_paths([$/|_] = L, RelP) -> 532 lists:reverse(L) ++ RelP; 533do_merge_paths([_|T], RelP) -> 534 do_merge_paths(T, RelP). 535 536 537resolve_query(Query, RelURI, Map) -> 538 case maps:is_key(query, RelURI) of 539 true -> 540 Map#{query => maps:get(query, RelURI)}; 541 false -> 542 Map#{query => Query} 543 end. 544 545 546maybe_to_integer(Port) when is_list(Port) -> 547 {Port1, _} = string:to_integer(Port), 548 Port1; 549maybe_to_integer(Port) when is_integer(Port) -> 550 Port. 551 552 553error(#request{id = Id}, Reason) -> 554 {Id, {error, Reason}}. 555 556transparent(Response, Request) -> 557 {Msg, Data} = format_response(Response), 558 {ok, {Request#request.id, Msg}, Data}. 559 560stream_start(Headers, Request, ignore) -> 561 {Request#request.id, stream_start, http_response:header_list(Headers)}; 562 563stream_start(Headers, Request, Pid) -> 564 {Request#request.id, stream_start, 565 http_response:header_list(Headers), Pid}. 566 567stream_end(Response, Request = #request{stream = Self}) 568 when (Self =:= self) orelse (Self =:= {self, once}) -> 569 {{_, Headers, _}, Data} = format_response(Response), 570 {ok, {Request#request.id, stream_end, Headers}, Data}; 571 572stream_end(Response, Request) -> 573 {_, Data} = format_response(Response), 574 {ok, {Request#request.id, saved_to_file}, Data}. 575 576is_server_closing(Headers) when is_record(Headers, http_response_h) -> 577 case Headers#http_response_h.connection of 578 "close" -> 579 true; 580 _ -> 581 false 582 end. 583 584format_response({{"HTTP/0.9", _, _} = StatusLine, _, Body}) -> 585 {{StatusLine, [], Body}, <<>>}; 586format_response({StatusLine, Headers, Body = <<>>}) -> 587 {{StatusLine, http_response:header_list(Headers), Body}, <<>>}; 588 589format_response({StatusLine, Headers, Body}) -> 590 Length = list_to_integer(Headers#http_response_h.'content-length'), 591 {NewBody, Data} = 592 case Length of 593 -1 -> % When no length indicator is provided 594 {Body, <<>>}; 595 Length when (Length =< size(Body)) -> 596 <<BodyThisReq:Length/binary, Next/binary>> = Body, 597 {BodyThisReq, Next}; 598 _ -> %% Connection prematurely ended. 599 {Body, <<>>} 600 end, 601 {{StatusLine, http_response:header_list(Headers), NewBody}, Data}. 602