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