1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-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(httpd_response).
22-export([generate_and_send_response/1, send_status/3, send_status/4, send_header/3,
23	 send_body/3, send_chunk/3, send_final_chunk/2, send_final_chunk/3,
24	 split_header/2, is_disable_chunked_send/1, cache_headers/2, handle_continuation/1]).
25-export([map_status_code/2]).
26
27-include_lib("inets/src/inets_app/inets_internal.hrl").
28-include_lib("inets/include/httpd.hrl").
29-include_lib("inets/src/http_lib/http_internal.hrl").
30-include_lib("inets/src/http_server/httpd_internal.hrl").
31-include_lib("kernel/include/logger.hrl").
32
33-define(VMODULE,"RESPONSE").
34
35handle_continuation(Mod) ->
36    generate_and_send_response(Mod).
37
38%% If peername does not exist the client already discarded the
39%% request so we do not need to send a reply.
40generate_and_send_response(#mod{init_data =
41				#init_data{peername = {_,"unknown"}}}) ->
42    ok;
43generate_and_send_response(#mod{config_db = ConfigDB} = ModData) ->
44    Modules = httpd_util:lookup(ConfigDB, modules, ?DEFAULT_MODS),
45    case traverse_modules(ModData, Modules) of
46        {continue, _} = Continue ->
47            Continue;
48	done ->
49	    ok;
50	{proceed, Data} ->
51	    case proplists:get_value(status, Data) of
52		{StatusCode, PhraseArgs, _Reason} ->
53		    send_status(ModData, StatusCode, PhraseArgs),
54		    ok;
55		undefined ->
56		    case proplists:get_value(response, Data) of
57			{already_sent, _StatusCode, _Size} ->
58			    ok;
59			{response, Header, Body} -> %% New way
60			    send_response(ModData, Header, Body),
61			    ok;
62			{StatusCode, Response} ->   %% Old way
63			    send_response_old(ModData, StatusCode, Response),
64			    ok;
65			undefined ->
66                            %% Happens when no mod_*
67                            %% handles the request
68			    send_status(ModData, 501, {ModData#mod.method,
69                                                       ModData#mod.request_uri,
70                                                       ModData#mod.http_version}),
71			    ok
72		    end
73	    end
74    end.
75
76
77%% traverse_modules
78
79traverse_modules(ModData,[]) ->
80  {proceed,ModData#mod.data};
81traverse_modules(ModData,[Module|Rest]) ->
82    try apply(Module, do, [ModData]) of
83        {continue, _} = Continue ->
84            Continue;
85	done ->
86            done;
87	{break, NewData} ->
88            {proceed, NewData};
89	{proceed, NewData} ->
90            traverse_modules(ModData#mod{data = NewData}, Rest)
91    catch
92	T:E:Stacktrace ->
93	    httpd_util:error_log(ModData#mod.config_db,
94                                 httpd_logger:error_report('HTTP',
95                                                           [{module, Module},
96                                                            {class, T},
97                                                            {error, E},
98                                                            {stacktrace, Stacktrace}], ModData, ?LOCATION)),
99	    send_status(ModData, 500, none),
100	    done
101    end.
102
103%% send_status %%
104
105
106send_status(ModData, 100, _PhraseArgs) ->
107    send_header(ModData, 100, [{content_length, "0"}]);
108
109send_status(ModData, StatusCode, PhraseArgs) ->
110    send_status(ModData, StatusCode, PhraseArgs, undefined).
111
112send_status(#mod{socket_type = SocketType,
113		 socket      = Socket,
114		 config_db   = ConfigDB} = ModData, StatusCode, PhraseArgs, Details) ->
115
116    ReasonPhrase = httpd_util:reason_phrase(StatusCode),
117    Message      = httpd_util:message(StatusCode, PhraseArgs, ConfigDB),
118    Body         = get_body(ReasonPhrase, Message),
119
120    send_header(ModData, StatusCode,
121		[{content_type,   "text/html"},
122		 {content_length, integer_to_list(length(Body))}]),
123
124    if StatusCode >= 400 ->
125            case Details of
126                undefined ->
127                    httpd_util:error_log(ConfigDB, httpd_logger:error_report('HTTP',
128                                                                   [{statuscode, StatusCode}, {description, ReasonPhrase}],
129                                                                   ModData, ?LOCATION));
130                _ ->
131                    httpd_util:error_log(ConfigDB, httpd_logger:error_report('HTTP',
132                                                                   [{statuscode,StatusCode}, {description, ReasonPhrase},
133                                                                    {details, Details}], ModData,
134                                                                   ?LOCATION))
135            end;
136       true ->
137            ok
138    end,
139    httpd_socket:deliver(SocketType, Socket, Body).
140
141
142get_body(ReasonPhrase, Message)->
143    "<HTML>
144       <HEAD>
145           <TITLE>"++ReasonPhrase++"</TITLE>
146      </HEAD>
147      <BODY>
148      <H1>"++ReasonPhrase++"</H1>\n"++Message++"\n</BODY>
149      </HTML>\n".
150
151
152send_response(ModData, Header, Body) ->
153    case proplists:get_value(code, Header) of
154	undefined ->
155	    %% No status code
156	    %% Ooops this must be very bad:
157	    %% generate a 404 content not availible
158	    send_status(ModData, 404, "The file is not availible");
159	StatusCode ->
160	    case send_header(ModData, StatusCode, lists:keydelete(code, 1,
161								  Header)) of
162		ok ->
163		    send_body(ModData, StatusCode, Body);
164		_ ->
165		    done
166	    end
167    end.
168
169send_header(#mod{socket_type  = Type,
170		 socket       = Sock,
171		 http_version = Ver,
172		 connection   = Conn,
173		 config_db    = ConfigDb} = _ModData,
174	    StatusCode, KeyValueTupleHeaders) ->
175    Headers = create_header(ConfigDb,
176			    lists:map(fun transform/1, KeyValueTupleHeaders)),
177    NewVer = case {Ver, StatusCode} of
178		 {[], _} ->
179		     %% May be implicit!
180		     "HTTP/0.9";
181		 {unknown, 408} ->
182		     %% This will proably never happen! It means the
183		     %% server has timed out the request without
184		     %% receiving a version for the request!  Send the
185		     %% lowest version so to ensure that the client
186		     %% will be able to handle it, probably the
187		     %% sensible thing to do!
188		     "HTTP/0.9";
189		 {undefined,_} ->
190		     "HTTP/1.0"; %% See rfc2145 2.3 last paragraph
191		 _ ->
192		     Ver
193	     end,
194    NewStatusCode = map_status_code(NewVer, StatusCode),
195    StatusLine = [NewVer, " ", io_lib:write(NewStatusCode), " ",
196		  httpd_util:reason_phrase(NewStatusCode), ?CRLF],
197    ConnectionHeader = get_connection(Conn, NewVer),
198    Head = [StatusLine, Headers, ConnectionHeader , ?CRLF],
199    httpd_socket:deliver(Type, Sock, Head).
200
201map_status_code("HTTP/1.0", Code)
202  when ((Code div 100) =:= 2) andalso (Code > 204) ->
203    403;
204map_status_code("HTTP/1.0", Code)
205  when ((Code div 100) =:= 3) andalso (Code > 304) ->
206    403;
207map_status_code("HTTP/1.0", Code)
208  when ((Code div 100) =:= 4) andalso (Code > 404) ->
209    403;
210map_status_code("HTTP/1.0", Code)
211  when ((Code div 100) =:= 5) andalso (Code > 503) ->
212    403;
213map_status_code(_, Code) ->
214    Code.
215
216send_body(#mod{socket_type = Type, socket = Socket}, _, nobody) ->
217    httpd_socket:close(Type, Socket),
218    ok;
219
220send_body(#mod{socket_type = Type, socket = Sock},
221	  _StatusCode, Body) when is_list(Body) ->
222    case httpd_socket:deliver(Type, Sock, Body) of
223	socket_closed ->
224	    done;
225	Else ->
226	    Else
227    end;
228
229send_body(#mod{socket_type = Type, socket = Sock} = ModData,
230	  StatusCode, {Fun, Args}) ->
231    case (catch apply(Fun, Args)) of
232	close ->
233	    httpd_socket:close(Type, Sock),
234	    done;
235
236	sent ->
237	    {proceed,[{response,{already_sent, StatusCode,
238				 proplists:get_value(content_length,
239						     ModData#mod.data)}}]};
240	{ok, Body} ->
241	    case httpd_socket:deliver(Type, Sock, Body) of
242		ok ->
243		    {proceed,[{response,
244			       {already_sent, StatusCode,
245				proplists:get_value(content_length,
246						    ModData#mod.data)}}]};
247		_ ->
248		    done
249	    end;
250
251	_ ->
252	    done
253    end.
254
255split_header([$: | Value], AccName) ->
256    Name = http_util:to_lower(string:strip(AccName)),
257    {lists:reverse(Name),
258     string:strip(string:strip(string:strip(Value, right, ?LF), right, ?CR))};
259split_header([Char | Rest], AccName) ->
260    split_header(Rest, [Char | AccName]).
261
262send_chunk(_, <<>>, _) ->
263    ok;
264send_chunk(_, [], _) ->
265    ok;
266send_chunk(#mod{http_version = "HTTP/1.1",
267		socket_type = Type, socket = Sock}, Response0, false) ->
268    Response = http_chunk:encode(Response0),
269    httpd_socket:deliver(Type, Sock, Response);
270
271send_chunk(#mod{socket_type = Type, socket = Sock} = _ModData, Response, _) ->
272    httpd_socket:deliver(Type, Sock, Response).
273
274send_final_chunk(Mod, IsDisableChunkedSend) ->
275    send_final_chunk(Mod, [], IsDisableChunkedSend).
276
277send_final_chunk(#mod{http_version = "HTTP/1.1",
278		      socket_type = Type, socket = Sock}, Trailers, false) ->
279    httpd_socket:deliver(Type, Sock, http_chunk:encode_last(Trailers));
280send_final_chunk(#mod{socket_type = Type, socket = Sock}, _, _) ->
281    httpd_socket:close(Type, Sock).
282
283is_disable_chunked_send(Db) ->
284    httpd_util:lookup(Db, disable_chunked_transfer_encoding_send, false).
285
286%% Return a HTTP-header field that indicates that the
287%% connection will be inpersistent
288get_connection(true,"HTTP/1.0")->
289    "Connection:close\r\n";
290get_connection(false,"HTTP/1.1") ->
291    "Connection:close\r\n";
292get_connection(_,_) ->
293    "".
294
295cache_headers(#mod{config_db = Db}, NoCacheType) ->
296    case httpd_util:lookup(Db, NoCacheType, false) of
297	true ->
298	    Date = httpd_util:rfc1123_date(),
299	    [{"cache-control", "no-cache"},
300	     {"pragma", "no-cache"},
301	     {"expires", Date}];
302	false ->
303	    []
304    end.
305
306create_header(ConfigDb, KeyValueTupleHeaders) ->
307    Date        = httpd_util:rfc1123_date(),
308    ContentType = "text/html",
309    Server      = server(ConfigDb),
310    CustomizeCB = httpd_util:lookup(ConfigDb, customize, httpd_custom),
311
312    CustomDefaults = httpd_custom:response_default_headers(CustomizeCB),
313    SystemDefaultes = ([{"date", Date},
314			{"content-type", ContentType}
315			| if Server=="" -> [];
316			     true -> [{"server", Server}]
317			  end
318		       ]),
319
320    %% System defaults not present in custom defaults will be added
321    %% to defaults
322    Defaults = add_default_headers(SystemDefaultes, CustomDefaults),
323
324    Headers0 = add_default_headers(Defaults, KeyValueTupleHeaders),
325    lists:filtermap(fun(H) ->
326			    httpd_custom:customize_headers(CustomizeCB, response_header, H)
327		    end,
328		    [Header || Header <- Headers0]).
329server(ConfigDb) ->
330    httpd_util:lookup(ConfigDb, server, ?SERVER_SOFTWARE).
331
332add_default_headers([], Headers) ->
333    Headers;
334
335add_default_headers([Header = {Default, _} | Defaults], Headers) ->
336    case lists:keysearch(Default, 1, Headers) of
337	{value, _} ->
338	    add_default_headers(Defaults, Headers);
339	_ ->
340	    add_default_headers(Defaults, [Header | Headers])
341    end.
342
343transform({content_type, Value}) ->
344    {"content-type", Value};
345transform({accept_ranges, Value}) ->
346     {"accept-ranges", Value};
347transform({cache_control, Value}) ->
348     {"cache-control",Value};
349transform({transfer_encoding, Value}) ->
350    {"transfer-encoding", Value};
351transform({content_encoding, Value}) ->
352    {"content-encoding", Value};
353transform({content_language, Value}) ->
354    {"content-language", Value};
355transform({retry_after, Value}) ->
356    {"retry-after", Value};
357transform({content_location, Value}) ->
358    {"Content-Location:", Value};
359transform({content_length, Value}) ->
360    {"content-length", Value};
361transform({content_MD5, Value}) ->
362    {"content-md5", Value};
363transform({content_range, Value}) ->
364    {"content-range", Value};
365transform({last_modified, Value}) ->
366    {"last-modified", Value};
367transform({Field, Value}) when is_atom(Field) ->
368    {atom_to_list(Field), Value};
369transform({Field, Value}) when is_list(Field) ->
370    {Field, Value}.
371
372%%----------------------------------------------------------------------
373%% This is the old way of sending data it is strongly encouraged to
374%% Leave this method and go on to the newer form of response
375%% OTP-4408
376%%----------------------------------------------------------------------
377send_response_old(#mod{method = "HEAD"} = ModData,
378		  StatusCode, Response) ->
379
380    NewResponse = lists:flatten(Response),
381
382    case httpd_util:split(NewResponse, [?CR, ?LF, ?CR, ?LF],2) of
383	{ok, [Head, Body]} ->
384	    {ok, NewHead} = handle_headers(string:tokens(Head, [?CR,?LF]), []),
385	    send_header(ModData, StatusCode, [{content_length,
386					    content_length(Body)} | NewHead]);
387	{ok, [NewResponse]} ->
388	    send_header(ModData, StatusCode, [{content_length,
389					       content_length(NewResponse)}]);
390	_Error ->
391	    send_status(ModData, 500, "Internal Server Error")
392    end;
393
394send_response_old(#mod{socket_type = Type,
395		       socket      = Sock} = ModData,
396		  StatusCode, Response) ->
397
398    NewResponse = lists:flatten(Response),
399
400    case httpd_util:split(NewResponse, [?CR, ?LF, ?CR, ?LF], 2) of
401	{ok, [Head, Body]} ->
402	    {ok, NewHead} = handle_headers(string:tokens(Head,
403							 [?CR,?LF]), []),
404	    send_header(ModData, StatusCode, [{content_length,
405					       content_length(Body)} |
406					      NewHead]),
407	    httpd_socket:deliver(Type, Sock, Body);
408	{ok, [NewResponse]} ->
409	    send_header(ModData, StatusCode, [{content_length,
410					       content_length(NewResponse)}]),
411	    httpd_socket:deliver(Type, Sock, NewResponse);
412	_ ->
413	    send_status(ModData, 500, "Internal Server Error")
414    end.
415
416content_length(Body)->
417    integer_to_list(httpd_util:flatlength(Body)).
418
419handle_headers([], NewHeaders) ->
420    {ok, NewHeaders};
421
422handle_headers([Header | Headers], NewHeaders) ->
423    {FieldName, FieldValue} = split_header(Header, []),
424    handle_headers(Headers,
425		   [{FieldName, FieldValue}| NewHeaders]).
426
427