Permalink
Browse files

header functions moved to utils (wip, not working)

  • Loading branch information...
1 parent 4e1d981 commit 3c255e63c1d85ccbbfc729852631da7b930d1544 @choptastic choptastic committed Mar 30, 2013
@@ -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),
@@ -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.
@@ -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(
@@ -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,
View
@@ -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) ->
@@ -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.
@@ -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,
@@ -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),

0 comments on commit 3c255e6

Please sign in to comment.