Skip to content

Commit

Permalink
header functions moved to utils (wip, not working)
Browse files Browse the repository at this point in the history
  • Loading branch information
choptastic committed Mar 30, 2013
1 parent 4e1d981 commit 3c255e6
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 61 deletions.
74 changes: 38 additions & 36 deletions src/cowboy_bridge_modules/cowboy_response_bridge.erl
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,13 @@ build_response(ReqKey, Res) ->
% Some values...
Code = Res#response.statuscode,

%% assemble headers...
Headers = lists:flatten([[{X#header.name, X#header.value} || X <- Res#response.headers]]),

case Res#response.data of
{data, Body} ->
% Assemble headers...
Headers = lists:flatten([[{X#header.name, X#header.value} || X <- Res#response.headers]]),

% Ensure content type...
F = fun(Key) -> lists:keymember(Key, 1, Headers) end,
HasContentType = lists:any(F, ["content-type", "Content-Type", "CONTENT-TYPE"]),
Headers2 =
case HasContentType of
true -> Headers;
false -> [{"Content-Type", "text/html"} | Headers]
end,
Headers2 = simple_bridge_util:ensure_header(Headers,"Content-Type","text/html"),

% Send the cowboy cookies
{ok, FinReq} = send(Code, Headers2, Res#response.cookies, Body, Req),
Expand All @@ -53,34 +47,42 @@ build_response(ReqKey, Res) ->
%% See https://github.com/nitrogen/nitrogen/blob/master/rel/overlay/cowboy/etc/cowboy.config
%% and
%% https://github.com/nitrogen/nitrogen/blob/master/rel/overlay/cowboy/site/src/nitrogen_sup.erl

throw(wip)
%% Path = strip_leading_slash(P),
%% Mimetype = get_mimetype(Path),
%%
%% Headers2 = simple_bridge_util:ensure_header(Headers,{"Content-Type",Mimetype}),
%% Headers3 = simple_bridge_util:ensure_expires_header(Headers),
%%
%% FullPath = filename:join(DocRoot, Path),
%% {ok, FinReq} =
%% case file:read_file(FullPath) of
%% {error,enoent} ->
%% {ok, _R} = send(404, [], [], "Not Found", Req);
%% {ok,Bin} ->
%% {ok, _R} = send(200, Headers, [], Bin, Req)
%% end,
%% cowboy_request_server:set(ReqKey, RequestCache#request_cache{request = FinReq}),
%% generate_static_error(P),
%% {ok, FinReq}
end.

%% % Cowboy path starts with / so we need to remove it
%% Path = strip_leading_slash(P),
%% ExpireDate = simple_bridge_util:expires(years, 10),
%% [$. | Ext] = filename:extension(Path),
%% Mimetype = mimetypes:extension(Ext),
%% Headers = [{"Expires", ExpireDate}, {"Content-Type",Mimetype}],

%% io:format("Serving static file ~p from docroot of ~p ~n",[Path, DocRoot]),

%% FullPath = filename:join(DocRoot, Path),
%% {ok, FinReq} =
%% case file:read_file(FullPath) of
%% {error,enoent} -> {ok, _R} = send(404, [], [], "Not Found", Req);
%% {ok,Bin} -> {ok, _R} = send(200, Headers, [], Bin, Req)
%% end,
%% cowboy_request_server:set(ReqKey, RequestCache#request_cache{request = FinReq}),
%% {ok, FinReq}
throw({unrouted_static_file, [
{requested_file, P},
{description, "Simple Bridge through Cowboy is not set up to handle static files. Static Files should be handled by Cowboy through the routing table."},
{see_also, [
"https://github.com/nitrogen/nitrogen/blob/master/rel/overlay/cowboy/site/src/nitrogen_sup.erl",
"https://github.com/nitrogen/nitrogen/blob/master/rel/overlay/cowboy/etc/cowboy.config"
]}
generate_static_error(P) ->
error_logger:warning_msg("~p",[
{unrouted_static_file, [
{requested_file, P},
{description, "Simple Bridge through Cowboy is not set up to handle static files. Static Files should be handled by Cowboy through the routing table."},
{see_also, [
"https://github.com/nitrogen/nitrogen/blob/master/rel/overlay/cowboy/site/src/nitrogen_sup.erl",
"https://github.com/nitrogen/nitrogen/blob/master/rel/overlay/cowboy/etc/cowboy.config"
]}
]}
]).

]})
end.
get_mimetype(Path) ->
[$. | Ext] = filename:extension(Path),
mimetypes:extension(Ext).

%% %% Just to strip leading slash, as cowboy tends to do this.
%% %% If no leading slash, just return the path.
Expand Down
1 change: 1 addition & 0 deletions src/inets_bridge_modules/inets_response_bridge.erl
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ to_cookie_expire(SecondsToLive) ->

%% Inets wants some headers as lowercase atoms, so we
%% need to do some special massage here.
%% TODO: This needs to be reworked. We shouldn't be making atoms from every request
massage(Header) ->
X = list_to_atom(
binary_to_list(
Expand Down
16 changes: 5 additions & 11 deletions src/mochiweb_bridge_modules/mochiweb_response_bridge.erl
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,21 @@ build_response({Req, DocRoot}, Res) ->
Headers = lists:flatten([
[{X#header.name, X#header.value} || X <- Res#response.headers],
[create_cookie_header(X) || X <- Res#response.cookies]
]),

%% Ensure content type...
F = fun(Key) ->
lists:keymember(Key, 1, Headers)
end,
HasContentType = lists:any(F, ["content-type", "Content-Type", "CONTENT-TYPE"]),
Headers2 = case HasContentType of
true -> Headers;
false -> [{"Content-Type", "text/html"}|Headers]
end,
]),

case Res#response.data of
{data, Body} ->
% Send the mochiweb response...
Headers2 = simple_bridge_util:ensure_header(Headers,{"Content-Type","text/html"}),
Req:respond({Code, Headers2, Body});
{file, Path} ->
%% Create the response telling Mochiweb to serve the file...
Headers2 = simple_bridge_util:ensure_expires_header(Headers),
Req:serve_file(tl(Path), DocRoot, Headers2)
end.



create_cookie_header(Cookie) ->
SecondsToLive = Cookie#cookie.minutes_to_live * 60,
Name = Cookie#cookie.name,
Expand Down
136 changes: 125 additions & 11 deletions src/simple_bridge_util.erl
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
% vim: ts=4 sw=4 et
-module(simple_bridge_util).
-export([
atomize_header/1,
expires/2,
b2l/1
]).
atomize_header/1,
expires/2,
b2l/1,
has_header/2,
has_any_header/2,
ensure_header/3,
ensure_header/2,
ensure_headers/2,
default_static_expires_header/0,
ensure_expires_header/1,
needs_expires_header/1
]).

-type header_key() :: string() | binary().
-type header() :: {header_key(), string()}.
-type header_list() :: [header()].

%% converts a Header to a lower-case, underscored version
%% ie. "X-Forwarded-For" -> x_forwarded_for

atomize_header(Header) when is_binary(Header) ->
atomize_header(binary_to_list(Header));
atomize_header(Header) when is_atom(Header) ->
Expand All @@ -25,14 +37,116 @@ atomize_header(Header) when is_list(Header) ->
end,
list_to_atom(lists:map(LowerUnderscore,Header)).

%% TODO: Make this flexibile beyond just years
expires(years, Years) when is_integer(Years) ->
%% Calculate expire date far into future...
%% This method copied from Evan Miller's implementation
{{Y, _, _}, _} = calendar:local_time(),

ExpireDate = httpd_util:rfc1123_date(),
_FinalExpiresDate = re:replace(ExpireDate, " \\d\\d\\d\\d ", io_lib:format(" ~4.4.0w ", [Y + Years])).
%% Checks if `Header` exists as a key in `HeaderList`
%% if it doesn't, inserts it with the value `Value`
-spec ensure_header(header_list(), {header_key(), term()}) -> header_list().
ensure_header(HeaderList,{Header,Value}) ->
ensure_header(HeaderList,Header,Value).

-spec ensure_header(header_list(), header_key(), term()) -> header_list().
ensure_header(HeaderList,Header,Value) ->
case has_header(HeaderList,Header) of
true -> HeaderList;
false -> [{Header,Value} | HeaderList]
end.

-spec ensure_headers(header_list(), header_list()) -> header_list().
ensure_headers(HeaderList,HeadersToEnsure) ->
LowerList = lower_keys(HeaderList),
lists:foldl(fun({Header,Value},NewHeaderList) ->
case has_lower_header(LowerList, Header) of
true -> NewHeaderList;
false -> [{Header,Value} | NewHeaderList]
end
end,HeaderList,HeadersToEnsure).

-spec ensure_expires_header(header_list()) -> header_list().
ensure_expires_header(HeaderList) ->
case needs_expires_header(HeaderList) of
true ->
Expires = default_static_expires_header(),
[Expires | HeaderList];
false ->
HeaderList
end.

-spec needs_expires_header(header_list()) -> boolean().
needs_expires_header(HeaderList) ->
not(has_any_header(HeaderList,["Expires","Cache-Control"])).

-spec has_header(header_list(), header_key()) -> boolean().
has_header(HeaderList,Header) ->
LowerKeys = lower_keys(HeaderList),
has_lower_header(LowerKeys, Header).

-spec has_any_header(header_list(), [header_key()]) -> boolean().
has_any_header(HeaderList,HeadersToCheck) ->
LowerKeys = lower_keys(HeaderList),
lists:any(fun(Key) -> has_lower_header(LowerKeys,Key) end,HeadersToCheck).

-spec has_lower_header([string()], header_key()) -> boolean().
has_lower_header(HeaderLowerKeyList, Header) ->
HeaderLower = to_lower(Header),
lists:member(HeaderLower, HeaderLowerKeyList).

-spec lower_keys(header_list()) -> [string()].
lower_keys(HeaderList) ->
[to_lower(Key) || {Key,_} <- HeaderList].

-spec to_lower(header_key()) -> string().
to_lower(Header) when is_binary(Header) ->
to_lower(b2l(Header));
to_lower(Header) when is_list(Header) ->
string:to_lower(Header).

-spec default_static_expires_header() -> header().
default_static_expires_header() ->
case application:get_env(simple_bridge,default_expires) of
{ok, immediate} ->
{"Cache-control","no-cache"};

{ok, Seconds} when is_integer(Seconds) ->
Expires = expires(seconds,Seconds),
{"Expires", Expires};

{ok, {Unit, Value}} when Unit==years orelse
Unit==months orelse
Unit==weeks orelse
Unit==days orelse
Unit==hours orelse
Unit==minutes orelse
Unit==seconds ->
Expires = expires(Unit,Value),
{"Expires", Expires};
_ ->
Expires = expires(years,10),
{"Expires", Expires}
end.

-type unit_of_time() :: years|months|weeks|days|hours|minuites|seconds.
-spec expires(unit_of_time(), integer()) -> string().
expires(years,X) when is_integer(X) ->
make_expires_from_seconds(X*31536000);
expires(months,X) when is_integer(X) ->
make_expires_from_seconds(X*2592000);
expires(weeks,X) when is_integer(X) ->
make_expires_from_seconds(X*604800);
expires(days,X) when is_integer(X) ->
make_expires_from_seconds(X*86400);
expires(hours,X) when is_integer(X) ->
make_expires_from_seconds(X*3600);
expires(minutes,X) when is_integer(X) ->
make_expires_from_seconds(X*60);
expires(seconds,X) when is_integer(X) ->
make_expires_from_seconds(X).

-spec make_expires_from_seconds(integer()) -> string().
make_expires_from_seconds(Seconds) ->
{NowMegaSec,NowSec,_} = now(),
ExpiresDate = calendar:now_to_datetime({NowMegaSec,NowSec+Seconds,0}),
httpd_util:rfc1123_date(ExpiresDate).

-spec b2l(binary() | string()) -> string().
b2l(B) when is_binary(B) -> binary_to_list(B);
b2l(B) -> B.
18 changes: 15 additions & 3 deletions src/yaws_bridge_modules/yaws_response_bridge.erl
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,24 @@ build_response(_Arg, Res) ->
%% completely back to Yaws, or 2) how the streamcontent return types work as define in
%% yaws_server:handle_out_reply

ExpireDate = simple_bridge_util:expires(years, 10),
%% Static Content should have an expires date. If not, we're going to make one
Headers2 = ensure_expires_header(Res, Headers),

%% Docroot needed to find file in Path
Docroot = yaws_api:arg_docroot(_Arg),
FullPath = [Docroot,Path],

%% Get the content type as defined by yaws
ContentType = yaws_api:mime_type(Path),

%% Get the file content
FullResponse = case file:read_file(FullPath) of
{error,enoent} ->
yaws_outmod:out404(_Arg);
{ok,Bin} ->
[
{status, Code},
[{header, {"Expires", ExpireDate}} | Headers],
Headers2,
{content, ContentType, Bin}
]
end,
Expand All @@ -72,7 +73,18 @@ assemble_headers(Res) ->
[{header, {X#header.name, X#header.value}} || X <- Res#response.headers],
[create_cookie(X) || X <- Res#response.cookies]
]).

%% This is slightly different from the one in simple_bridge_util due to the
%% formatting of the yaws headers isn't just a simple proplist.
ensure_expires_header(Res,Headers) ->
case simple_bridge_util:needs_expires_header(Res#response.headers) of
true ->
Expires = simple_bridge_util:default_static_expires_header(),
[{header, Expires} | Headers];
false -> Headers
end.


get_content_type(Res) ->
coalesce([
kvl3(content_type, Res#response.headers),
Expand Down

0 comments on commit 3c255e6

Please sign in to comment.