%%% BEGIN openflax/http/response.erl %%% %%% %%% openflax - Open Source web server for Erlang/OTP %%% Copyright (c)2004 Cat's Eye Technologies. All rights reserved. %%% %%% Redistribution and use in source and binary forms, with or without %%% modification, are permitted provided that the following conditions %%% are met: %%% %%% Redistributions of source code must retain the above copyright %%% notice, this list of conditions and the following disclaimer. %%% %%% Redistributions in binary form must reproduce the above copyright %%% notice, this list of conditions and the following disclaimer in %%% the documentation and/or other materials provided with the %%% distribution. %%% %%% Neither the name of Cat's Eye Technologies nor the names of its %%% contributors may be used to endorse or promote products derived %%% from this software without specific prior written permission. %%% %%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND %%% CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, %%% INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF %%% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE %%% DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE %%% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, %%% OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, %%% PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, %%% OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON %%% ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, %%% OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY %%% OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE %%% POSSIBILITY OF SUCH DAMAGE. %% @doc HTTP response generation for OpenFlax. %% %%

This module exports functions which are called %% exclusively from openflax.serve to send an HTTP %% response back to the user agent via the HTTP service.

%% %% @end -module(openflax.http.response). -vsn('$Id: response.erl 31 2004-04-23 07:00:11Z catseye $'). -author('catseye@catseye.mb.ca'). -copyright('Copyright (c)2004 Cat`s Eye Technologies. All rights reserved.'). -export([send_headers/2, send_data/3]). -export([moved/4, error/4]). -import(lists). %% @spec send_headers(http(), conf()) -> ok | {error, Reason} %% @doc Sends an HTTP response header through the HTTP service. %% Note that after doing this, you should not send any body %% if the Request-Method is HEAD. send_headers(Http, Conf) -> Conf0 = add_missing_headers(Conf), send_headers0(Http, Conf0). send_headers0(Http, Conf) -> Response = openflax.conf:get_string(sres_response_code, Conf), Http ! {self(), send_response_line, Response}, ResHeaders = openflax.conf:fold(fun(Key, Value, Acc) -> case catch atom_to_list(Key) of "res_" ++ _ -> [{Key, Value} | Acc]; _ -> Acc end end, [], Conf), % Get cache-control from dictionary, if present % Assume resource is cacheable, if not NoCacheHeaderFields = case openflax.conf:get_value(sres_non_cacheable, Conf) of {ok, true} -> [{res_pragma, "no-cache"}, {res_cache_control, "no-cache"}]; _ -> [] end, % Get persistent connection options from dictionary, if present % Assume connection close (no persistent connection,) if not PersistencyHeaderFields = case {openflax.conf:get_value(sres_keep_alive, Conf), openflax.conf:get_string(req_connection, Conf)} of {_, "close"} -> % other side wants us to close [{res_connection, "close"}]; {{ok, true}, _} -> [{res_connection, "Keep-Alive"}]; _ -> [{res_connection, "close"}] end, HeaderFields = lists:reverse([ {res_date, openflax.string:from_datetime_rfc_1123()} ] ++ NoCacheHeaderFields ++ PersistencyHeaderFields ++ ResHeaders), % openflax.app:debug(sending_headers, HeaderFields), Http ! {self(), send_headers, HeaderFields}, ok. %% @spec send_data(http(), conf(), string()) -> ok | {error, Reason} %% @doc Sends arbitrary text or data to the HTTP service. %% Sends nothing if the Request-Method was HEAD. send_data(Http, Conf, Data) -> case openflax.conf:get_string(sreq_method, Conf) of "HEAD" -> ok; _ -> Http ! {self(), send_data, Data}, ok end. %% @spec moved(http(), conf(), response(), uri()) -> {ok, conf()} | {error, Reason} %% @doc Returns a redirection response (typically 301 or 302) to the user agent %% via the HTTP service. moved(Http, Conf, Response, URI) -> URI0 = qualify_uri(URI, Conf), Conf0 = openflax.conf:put_value(res_content_length, "0", Conf), Conf1 = openflax.conf:put_value(res_content_type, "text/html", Conf0), Conf2 = openflax.conf:put_value(res_location, URI0, Conf1), Conf3 = openflax.conf:put_value(sres_keep_alive, false, Conf2), Conf4 = openflax.conf:put_value(sres_response_code, Response, Conf3), send_headers(Http, Conf4), {ok, Conf3}. %% @spec error(http(), conf(), response(), master_dict()) -> {ok, conf()} %% @doc Returns an error response. If an error-handling module is configured, %% serve_error/3 will attempt to use it to generate an %% error response page. Otherwise, a (very plain) stock response is offered. %% TODO: pass MasterDict, ensure the given module is actually configured. error(Http, Conf, Response, MasterDict) -> Conf0 = openflax.conf:put_value(sres_response_code, Response, Conf), case openflax.conf:get_value(cfg_openflax_error_handler, Conf0) of {ok, Module} -> case openflax.conf:get_value(cfg_openflax_error_mode, Conf0) of {ok, true} -> % an error has already occurred. Let's not go into a loop! error0(Http, Conf0); _ -> Conf1 = openflax.conf:put_value(cfg_openflax_error_mode, true, Conf0), openflax.serve:handle_response(Module, Conf1, Http, MasterDict) end; _ -> error0(Http, Conf0) end. error0(Http, Conf) -> Response = openflax.conf:get_string(sres_response_code, Conf), Body = "" ++ Response ++ "" "

" ++ Response ++ "

", Conf1 = openflax.conf:put_value(sres_response_code, Response, Conf), Conf2 = openflax.conf:put_value(res_content_length, integer_to_list(lists:flatlength(Body)), Conf1), Conf3 = openflax.conf:put_value(res_content_type, "text/html", Conf2), send_headers(Http, Conf3), send_data(Http, Conf3, Body), {ok, Conf3}. %% qualify_uri(URI::string(), conf()) -> string() qualify_uri("/" ++ RestOfURI, Conf) -> Host = openflax.conf:get_string(sreq_host, Conf), Port = openflax.conf:get_string(sreq_port, Conf), case Port of "80" -> "http://" ++ Host ++ "/" ++ RestOfURI; _ -> "http://" ++ Host ++ ":" ++ Port ++ "/" ++ RestOfURI end; qualify_uri("http://" ++ RestOfURI, _Conf) -> "http://" ++ RestOfURI; qualify_uri(URI, Conf) -> qualify_uri("/" ++ URI, Conf). default(Conf, Key, Value) -> case openflax.conf:get_value(Key, Conf) of {ok, _} -> Conf; _ -> openflax.conf:put_value(Key, Value, Conf) end. add_missing_headers(Conf) -> Conf0 = default(Conf, sres_response_code, "200 OK"), Conf1 = default(Conf0, res_content_type, "application/octet-stream"), Conf2 = default(Conf1, res_last_modified, openflax.string:from_datetime_rfc_1123()), Conf3 = default(Conf2, res_server, "OpenFlax/" ++ openflax.conf:get_string(cfg_openflax_version, Conf2)), Conf3. %%% END of openflax/http/response.erl %%%