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		 {unknown, 408} ->
179		     %% This will proably never happen! It means the
180		     %% server has timed out the request without
181		     %% receiving a version for the request!  Send the
182		     %% lowest version so to ensure that the client
183		     %% will be able to handle it, probably the
184		     %% sensible thing to do!
185		     httpd_request:default_version();
186		 {undefined,_} ->
187		     httpd_request:default_version(); %% See rfc2145 2.3 last paragraph
188		 _ ->
189		     Ver
190	     end,
191    NewStatusCode = map_status_code(NewVer, StatusCode),
192    StatusLine = [NewVer, " ", io_lib:write(NewStatusCode), " ",
193		  httpd_util:reason_phrase(NewStatusCode), ?CRLF],
194    ConnectionHeader = get_connection(Conn, NewVer),
195    Head = [StatusLine, Headers, ConnectionHeader , ?CRLF],
196    httpd_socket:deliver(Type, Sock, Head).
197
198map_status_code("HTTP/1.0", Code)
199  when ((Code div 100) =:= 2) andalso (Code > 204) ->
200    403;
201map_status_code("HTTP/1.0", Code)
202  when ((Code div 100) =:= 3) andalso (Code > 304) ->
203    403;
204map_status_code("HTTP/1.0", Code)
205  when ((Code div 100) =:= 4) andalso (Code > 404) ->
206    403;
207map_status_code("HTTP/1.0", Code)
208  when ((Code div 100) =:= 5) andalso (Code > 503) ->
209    403;
210map_status_code(_, Code) ->
211    Code.
212
213send_body(#mod{socket_type = Type, socket = Socket}, _, nobody) ->
214    httpd_socket:close(Type, Socket),
215    ok;
216
217send_body(#mod{socket_type = Type, socket = Sock},
218	  _StatusCode, Body) when is_list(Body); is_binary(Body) ->
219    case httpd_socket:deliver(Type, Sock, Body) of
220	socket_closed ->
221	    done;
222	Else ->
223	    Else
224    end;
225
226send_body(#mod{socket_type = Type, socket = Sock} = ModData,
227	  StatusCode, {Fun, Args}) ->
228    case (catch apply(Fun, Args)) of
229	close ->
230	    httpd_socket:close(Type, Sock),
231	    done;
232
233	sent ->
234	    {proceed,[{response,{already_sent, StatusCode,
235				 proplists:get_value(content_length,
236						     ModData#mod.data)}}]};
237	{ok, Body} ->
238	    case httpd_socket:deliver(Type, Sock, Body) of
239		ok ->
240		    {proceed,[{response,
241			       {already_sent, StatusCode,
242				proplists:get_value(content_length,
243						    ModData#mod.data)}}]};
244		_ ->
245		    done
246	    end;
247
248	_ ->
249	    done
250    end.
251
252split_header([$: | Value], AccName) ->
253    Name = http_util:to_lower(string:strip(AccName)),
254    {lists:reverse(Name),
255     string:strip(string:strip(string:strip(Value, right, ?LF), right, ?CR))};
256split_header([Char | Rest], AccName) ->
257    split_header(Rest, [Char | AccName]).
258
259send_chunk(_, <<>>, _) ->
260    ok;
261send_chunk(_, [], _) ->
262    ok;
263send_chunk(#mod{http_version = "HTTP/1.1",
264		socket_type = Type, socket = Sock}, Response0, false) ->
265    Response = http_chunk:encode(Response0),
266    httpd_socket:deliver(Type, Sock, Response);
267
268send_chunk(#mod{socket_type = Type, socket = Sock} = _ModData, Response, _) ->
269    httpd_socket:deliver(Type, Sock, Response).
270
271send_final_chunk(Mod, IsDisableChunkedSend) ->
272    send_final_chunk(Mod, [], IsDisableChunkedSend).
273
274send_final_chunk(#mod{http_version = "HTTP/1.1",
275		      socket_type = Type, socket = Sock}, Trailers, false) ->
276    httpd_socket:deliver(Type, Sock, http_chunk:encode_last(Trailers));
277send_final_chunk(#mod{socket_type = Type, socket = Sock}, _, _) ->
278    httpd_socket:close(Type, Sock).
279
280is_disable_chunked_send(Db) ->
281    httpd_util:lookup(Db, disable_chunked_transfer_encoding_send, false).
282
283%% Return a HTTP-header field that indicates that the
284%% connection will be inpersistent
285get_connection(true,"HTTP/1.0")->
286    "Connection:close\r\n";
287get_connection(false,"HTTP/1.1") ->
288    "Connection:close\r\n";
289get_connection(_,_) ->
290    "".
291
292cache_headers(#mod{config_db = Db}, NoCacheType) ->
293    case httpd_util:lookup(Db, NoCacheType, false) of
294	true ->
295	    Date = httpd_util:rfc1123_date(),
296	    [{"cache-control", "no-cache"},
297	     {"pragma", "no-cache"},
298	     {"expires", Date}];
299	false ->
300	    []
301    end.
302
303create_header(ConfigDb, KeyValueTupleHeaders) ->
304    Date        = httpd_util:rfc1123_date(),
305    ContentType = "text/html",
306    Server      = server(ConfigDb),
307    CustomizeCB = httpd_util:lookup(ConfigDb, customize, httpd_custom),
308
309    CustomDefaults = httpd_custom:response_default_headers(CustomizeCB),
310    SystemDefaultes = ([{"date", Date},
311			{"content-type", ContentType}
312			| if Server=="" -> [];
313			     true -> [{"server", Server}]
314			  end
315		       ]),
316
317    %% System defaults not present in custom defaults will be added
318    %% to defaults
319    Defaults = add_default_headers(SystemDefaultes, CustomDefaults),
320
321    Headers0 = add_default_headers(Defaults, KeyValueTupleHeaders),
322    lists:filtermap(fun(H) ->
323			    httpd_custom:customize_headers(CustomizeCB, response_header, H)
324		    end,
325		    [Header || Header <- Headers0]).
326server(ConfigDb) ->
327    httpd_util:lookup(ConfigDb, server, ?SERVER_SOFTWARE).
328
329add_default_headers([], Headers) ->
330    Headers;
331
332add_default_headers([Header = {Default, _} | Defaults], Headers) ->
333    case lists:keysearch(Default, 1, Headers) of
334	{value, _} ->
335	    add_default_headers(Defaults, Headers);
336	_ ->
337	    add_default_headers(Defaults, [Header | Headers])
338    end.
339
340transform({content_type, Value}) ->
341    {"content-type", Value};
342transform({accept_ranges, Value}) ->
343     {"accept-ranges", Value};
344transform({cache_control, Value}) ->
345     {"cache-control",Value};
346transform({transfer_encoding, Value}) ->
347    {"transfer-encoding", Value};
348transform({content_encoding, Value}) ->
349    {"content-encoding", Value};
350transform({content_language, Value}) ->
351    {"content-language", Value};
352transform({retry_after, Value}) ->
353    {"retry-after", Value};
354transform({content_location, Value}) ->
355    {"Content-Location:", Value};
356transform({content_length, Value}) ->
357    {"content-length", Value};
358transform({content_MD5, Value}) ->
359    {"content-md5", Value};
360transform({content_range, Value}) ->
361    {"content-range", Value};
362transform({last_modified, Value}) ->
363    {"last-modified", Value};
364transform({Field, Value}) when is_atom(Field) ->
365    {atom_to_list(Field), Value};
366transform({Field, Value}) when is_list(Field) ->
367    {Field, Value}.
368
369%%----------------------------------------------------------------------
370%% This is the old way of sending data it is strongly encouraged to
371%% Leave this method and go on to the newer form of response
372%% OTP-4408
373%%----------------------------------------------------------------------
374send_response_old(#mod{method = "HEAD"} = ModData,
375		  StatusCode, Response) ->
376
377    NewResponse = lists:flatten(Response),
378
379    case httpd_util:split(NewResponse, [?CR, ?LF, ?CR, ?LF],2) of
380	{ok, [Head, Body]} ->
381	    {ok, NewHead} = handle_headers(string:tokens(Head, [?CR,?LF]), []),
382	    send_header(ModData, StatusCode, [{content_length,
383					    content_length(Body)} | NewHead]);
384	{ok, [NewResponse]} ->
385	    send_header(ModData, StatusCode, [{content_length,
386					       content_length(NewResponse)}]);
387	_Error ->
388	    send_status(ModData, 500, "Internal Server Error")
389    end;
390
391send_response_old(#mod{socket_type = Type,
392		       socket      = Sock} = ModData,
393		  StatusCode, Response) ->
394
395    NewResponse = lists:flatten(Response),
396
397    case httpd_util:split(NewResponse, [?CR, ?LF, ?CR, ?LF], 2) of
398	{ok, [Head, Body]} ->
399	    {ok, NewHead} = handle_headers(string:tokens(Head,
400							 [?CR,?LF]), []),
401	    send_header(ModData, StatusCode, [{content_length,
402					       content_length(Body)} |
403					      NewHead]),
404	    httpd_socket:deliver(Type, Sock, Body);
405	{ok, [NewResponse]} ->
406	    send_header(ModData, StatusCode, [{content_length,
407					       content_length(NewResponse)}]),
408	    httpd_socket:deliver(Type, Sock, NewResponse);
409	_ ->
410	    send_status(ModData, 500, "Internal Server Error")
411    end.
412
413content_length(Body)->
414    integer_to_list(erlang:iolist_size(Body)).
415
416handle_headers([], NewHeaders) ->
417    {ok, NewHeaders};
418
419handle_headers([Header | Headers], NewHeaders) ->
420    {FieldName, FieldValue} = split_header(Header, []),
421    handle_headers(Headers,
422		   [{FieldName, FieldValue}| NewHeaders]).
423
424