forked from filippo/ewgi
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Copied the middleware examples from ewgi_examples to src/middleware.
- Loading branch information
Showing
7 changed files
with
375 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
%% @author Filippo Pacini <filippo.pacini@gmail.com> | ||
%% @copyright 2009 S.G. Consulting. | ||
|
||
%% @doc deflate ewgi middleware. | ||
|
||
-module(ewgi_deflate). | ||
-author('Filippo Pacini <filippo.pacini@gmail.com>'). | ||
|
||
-export([run/2]). | ||
|
||
-define(ENCODABLE, ["text/plain", "text/html", "text/xml"]). | ||
|
||
run(Ctx, []) -> | ||
%% get the accept encoding header | ||
AcceptEnc = ewgi_api:get_header_value("accept-encoding", Ctx), | ||
Hdrs = ewgi_api:response_headers(Ctx), | ||
%% check gzip/deflate | ||
Ctx1 = case can_encode_response(Hdrs) of | ||
true -> | ||
case parse_encoding(AcceptEnc) of | ||
false -> | ||
Ctx; | ||
gzip -> | ||
%% FIXME: does not work if response_message_body streams a file | ||
Body1 = zlib:gzip(ewgi_api:response_message_body(Ctx)), | ||
Hdrs1 = [{"Content-encoding", "gzip"}|Hdrs], | ||
ewgi_api:response_headers( | ||
Hdrs1, | ||
ewgi_api:response_message_body(Body1, Ctx) | ||
); | ||
deflate -> | ||
%% FIXME: does not work if response_message_body streams a file | ||
Body1 = zlib:compress(ewgi_api:response_message_body(Ctx)), | ||
Hdrs1 = [{"Content-encoding", "deflate"}|Hdrs], | ||
ewgi_api:response_headers( | ||
Hdrs1, | ||
ewgi_api:response_message_body(Body1, Ctx) | ||
) | ||
end; | ||
false -> | ||
Ctx | ||
end, | ||
Ctx1. | ||
|
||
can_encode_response(Headers) -> | ||
ContentType = proplists:get_value("Content-type", Headers), | ||
ContentEnc = proplists:get_value("Content-encoding", Headers), | ||
can_encode_response1(ContentType, ContentEnc). | ||
|
||
can_encode_response1(ContentType, undefined) -> | ||
lists:member(ContentType, ?ENCODABLE); | ||
can_encode_response1(_ContentType, _ContentEncoding) -> | ||
%% the response is already encoded in some way so we don't do anything | ||
false. | ||
|
||
parse_encoding(undefined) -> | ||
false; | ||
parse_encoding(Encoding) -> | ||
%% FIXME: read HTTP specs to see how to do this properly (e.g. check the q=X and the order) | ||
case string:str(Encoding, "gzip") of | ||
X when X>0 -> | ||
gzip; | ||
_ -> | ||
case string:str(Encoding, "deflate") of | ||
Y when Y>0 -> | ||
deflate; | ||
_ -> | ||
false | ||
end | ||
end. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
%% @author Filippo Pacini <filippo.pacini@gmail.com> | ||
%% @copyright 2009 S.G. Consulting. | ||
|
||
%% @doc Hello world application and to_upper middleware | ||
|
||
-module(ewgi_hello). | ||
-author('Filippo Pacini <filippo.pacini@gmail.com>'). | ||
|
||
-export([run/2]). | ||
|
||
run({ewgi_context, Request, _Response}, []) -> | ||
ResponseHeaders = [{"Content-type", "text/plain"}], | ||
Response = {ewgi_response, {200, "OK"}, ResponseHeaders, | ||
[<<"Hello world!">>], undefined}, | ||
{ewgi_context, Request, Response}. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
%% @author Filippo Pacini <filippo.pacini@gmail.com> | ||
%% @copyright 2009 S.G. Consulting. | ||
|
||
%% @doc Example Index. List of examples with links. | ||
|
||
-module(ewgi_index). | ||
-author('Filippo Pacini <filippo.pacini@gmail.com>'). | ||
|
||
-export([run/2]). | ||
|
||
run({ewgi_context, Request, _Response}, []) -> | ||
Body = "<html><head><title>Ewgi Examples</title></head> | ||
<body> | ||
<h2>Ewgi Examples</h2> | ||
<ul> | ||
<li><a href=\"/hello\">Hello World</a>: simple hello world (source file: src/ewgi_hello.erl)</li> | ||
<li><a href=\"/HELLO\">HELLO WORD</a>: simple middleware transforming all the body in uppercase (source file: src/ewgi_to_upper.erl)</li> | ||
<li><a href=\"/test.txt\">File streaming</a>: streams the the file priv/www/test.txt (source file: src/ewgi_stream_file.erl)</li> | ||
<li><a href=\"/gzhello\">Gzip encodes the Hello World example</a>: if the browser accepts gzip encoding the result of the hello_app is gzipped (source file: src/ewgi_deflate.erl)</li> | ||
<li><a href=\"/postex\">Post example</a>: middleware handling of POST data (source file: src/ewgi_post.erl)</li> | ||
<li>Session examples (src/ewgi_session.erl): | ||
<ul><li><a href=\"/session/cookie\">client-side</a>: encrypted client-side session storage using cookies (source file: src/ewgi_session_cookie_store.erl)</li> | ||
<li><a href=\"/session/server\">server-side</a>: server-side session storage using an ets table (source file: src/ewgi_session_server_store.erl)</li> | ||
</ul></li> | ||
</ul> | ||
</body> | ||
</html>", | ||
ResponseHeaders = [{"Content-type", "text/html"}], | ||
Response = {ewgi_response, {200, "OK"}, ResponseHeaders, | ||
Body, undefined}, | ||
{ewgi_context, Request, Response}. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
%% @author Filippo Pacini <filippo.pacini@gmail.com> | ||
%% @copyright 2009 S.G. Consulting. | ||
|
||
%% @doc Post example | ||
|
||
-module(ewgi_post). | ||
-author('Filippo Pacini <filippo.pacini@gmail.com>'). | ||
|
||
-export([run/2]). | ||
-export([post_app_example/1]). | ||
|
||
-define(DEFAULT_MAX_LENGTH, 2097152). %% 2 MB maximum | ||
|
||
run(Ctx, [NoPostApp, FailedPostApp, SuccessPostApp]) -> | ||
run(Ctx, [NoPostApp, FailedPostApp, SuccessPostApp, ?DEFAULT_MAX_LENGTH]); | ||
run(Ctx, [NoPostApp, FailedPostApp, SuccessPostApp, MaxLength]) -> | ||
Parser = post_parse_middleware(MaxLength, SuccessPostApp, FailedPostApp), | ||
case ewgi_api:request_method(Ctx) of | ||
'GET' -> | ||
NoPostApp(Ctx); | ||
'POST' -> | ||
Parser(Ctx) | ||
end. | ||
|
||
%% MaxLength is the maximum size (in bytes) that the server will | ||
%% receive from the client. App should be the application called when | ||
%% the parse is successful (or unnecessary). ErrApp should be an | ||
%% error application when the content length exceeds the maximum | ||
%% specified limit. | ||
post_parse_middleware(MaxLength, App, ErrApp) | ||
when is_integer(MaxLength), MaxLength > 0, is_function(App, 1) -> | ||
fun(Ctx) -> | ||
case ewgi_api:request_method(Ctx) of | ||
Method when Method =:= 'POST'; | ||
Method =:= 'PUT' -> | ||
case ewgi_api:remote_user_data(Ctx) of | ||
undefined -> | ||
%% Check content-type first | ||
Ct = ewgi_api:content_type(Ctx), | ||
parse_post(Ctx, App, ErrApp, parse_ct(Ct), MaxLength); | ||
_ -> | ||
App(Ctx) | ||
end; | ||
_ -> | ||
App(Ctx) | ||
end | ||
end. | ||
|
||
%% Parse content-type (ignoring additional vars for now) | ||
%% Should look like "major/minor; var=val" | ||
parse_ct(L) when is_list(L) -> | ||
case string:tokens(L, ";") of | ||
[H|_] -> | ||
H; | ||
_ -> | ||
undefined | ||
end. | ||
|
||
parse_post(Ctx, App, ErrApp, "application/x-www-form-urlencoded", Max) -> | ||
case ewgi_api:content_length(Ctx) of | ||
L when is_integer(L), L > Max -> | ||
%% shouldn't we set an error message here? | ||
ErrApp(Ctx); | ||
L when is_integer(L), L > 0 -> | ||
Input = read_input_string(Ctx, L), | ||
Vals = ewgi_api:parse_post(Input), | ||
Ctx1 = ewgi_api:remote_user_data(Vals, Ctx), | ||
App(Ctx1); | ||
_ -> | ||
ErrApp(Ctx) | ||
end; | ||
parse_post(Ctx, App, _, _, _) -> | ||
%% Silently ignore other content-types | ||
App(Ctx). | ||
|
||
read_input_string(Ctx, L) when is_integer(L), L > 0 -> | ||
R = ewgi_api:read_input(Ctx), | ||
iolist_to_binary(R(read_input_string_cb([]), L)). | ||
|
||
read_input_string_cb(Acc) -> | ||
fun(eof) -> | ||
lists:reverse(Acc); | ||
({data, B}) -> | ||
read_input_string_cb([B|Acc]) | ||
end. | ||
|
||
%% | ||
%% example functions on how to use the post handling middleware | ||
%% | ||
post_app_example(Ctx) -> | ||
run(Ctx, [fun display_form/1, | ||
fun post_app_error/1, | ||
fun display_form_data/1]). | ||
|
||
post_app_error({ewgi_context, Request, _}) -> | ||
Response = {ewgi_response, {400, "BAD REQUEST"}, [], | ||
[<<"Maximum content-length exceeded.">>], | ||
undefined}, | ||
{ewgi_context, Request, Response}. | ||
|
||
display_form_data({ewgi_context, Request, _Response}=Ctx) -> | ||
Body = | ||
case ewgi_api:remote_user_data(Ctx) of | ||
undefined -> | ||
"undefined"; | ||
Body1 -> | ||
io_lib:format("~p", [Body1]) | ||
end, | ||
ResponseHeaders = [{"Content-type", "text/plain"}], | ||
Response = {ewgi_response, | ||
{200, "OK"}, | ||
ResponseHeaders, | ||
[Body], undefined}, | ||
{ewgi_context, Request, Response}. | ||
|
||
display_form({ewgi_context, Request, _Response}) -> | ||
Body = <<"<form action=\"/postex\" method=\"post\"> | ||
Un: <input type=\"text\" name=\"un\" value=\"\"/> | ||
<br/> | ||
Pw: <input type=\"text\" name=\"pw\" value=\"\"/> | ||
<br/><br/> | ||
<input type=\"submit\" name=\"submit\" value=\"Login\"/> | ||
</form>">>, | ||
ResponseHeaders = [{"Content-type", "text/html"}], | ||
Response = {ewgi_response, | ||
{200, "OK"}, | ||
ResponseHeaders, | ||
[Body], undefined}, | ||
{ewgi_context, Request, Response}. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
%% @author Hunter Morris <hunter.morris@smarkets.com> | ||
%% @author Filippo Pacini <filippo.pacini@gmail.com> | ||
|
||
%% @doc ewgi stream file application | ||
-module(ewgi_stream_file). | ||
|
||
-export([run/2]). | ||
|
||
%% | ||
%% Files opened in raw mode cannot be read in processes distinct | ||
%% from the one that opened the file! So we need to make sure | ||
%% that both the open and read operations are done in the same | ||
%% process. | ||
%% | ||
%% Since a webserver might need to spawn a new process to handle | ||
%% our stream (Yaws and the old Inets implementation did) | ||
%% we'll get around that by delaying the open operation to the | ||
%% head of the stream. | ||
%% | ||
run(Ctx, [File]) -> | ||
case file:read_file_info(File) of | ||
{ok, _} -> | ||
Mime = guess_mime(File), | ||
LoadIoDevice = {open, File, [raw, binary]}, | ||
%% Set ChunkSize to an optimal value | ||
ChunkSize = 1024, | ||
Stream = iodevice_stream(LoadIoDevice, ChunkSize), | ||
ewgi_api:response_status( | ||
{200, "OK"}, | ||
ewgi_api:response_headers( | ||
[{"Content-type", Mime}], | ||
ewgi_api:response_message_body(Stream, Ctx) | ||
) | ||
); | ||
_ -> | ||
%% Respond with 404... | ||
ewgi_api:response_message_body( | ||
"404 NOT FOUND", | ||
ewgi_api:response_status({404, "NOT FOUND"}, Ctx) | ||
) | ||
end. | ||
|
||
iodevice_stream({open, File, Modes}, ChunkSize) -> | ||
fun() -> | ||
case file:open(File, Modes) of | ||
{ok, IoDevice} -> | ||
{<<>>, iodevice_stream(IoDevice, ChunkSize)}; | ||
_ -> | ||
{} | ||
end | ||
end; | ||
iodevice_stream(IoDevice, ChunkSize) -> | ||
fun() -> | ||
case file:read(IoDevice, ChunkSize) of | ||
eof -> | ||
file:close(IoDevice), | ||
{}; | ||
{ok, Data} -> | ||
{Data, iodevice_stream(IoDevice, ChunkSize)}; | ||
{error, Reason} -> | ||
io:format("got error: ~p~n", [Reason]), | ||
{} | ||
end | ||
end. | ||
|
||
|
||
%% @spec guess_mime(string()) -> string() | ||
%% @doc Guess the mime type of a file by the extension of its filename. | ||
guess_mime(File) -> | ||
%% Taken from webmachine. | ||
case filename:extension(File) of | ||
".html" -> | ||
"text/html"; | ||
".xhtml" -> | ||
"application/xhtml+xml"; | ||
".xml" -> | ||
"application/xml"; | ||
".css" -> | ||
"text/css"; | ||
".js" -> | ||
"application/x-javascript"; | ||
".jpg" -> | ||
"image/jpeg"; | ||
".jpeg" -> | ||
"image/jpeg"; | ||
".gif" -> | ||
"image/gif"; | ||
".png" -> | ||
"image/png"; | ||
".ico" -> | ||
"image/x-icon"; | ||
".swf" -> | ||
"application/x-shockwave-flash"; | ||
".zip" -> | ||
"application/zip"; | ||
".bz2" -> | ||
"application/x-bzip2"; | ||
".gz" -> | ||
"application/x-gzip"; | ||
".tar" -> | ||
"application/x-tar"; | ||
".tgz" -> | ||
"application/x-gzip"; | ||
".htc" -> | ||
"text/x-component"; | ||
_ -> | ||
"text/plain" | ||
end. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
%% @author Filippo Pacini <filippo.pacini@gmail.com> | ||
%% @copyright 2009 S.G. Consulting. | ||
|
||
%% @doc to_upper middleware | ||
|
||
-module(ewgi_to_upper). | ||
-author('Filippo Pacini <filippo.pacini@gmail.com>'). | ||
|
||
-export([run/2]). | ||
|
||
run(Ctx, []) -> | ||
Body = ewgi_api:response_message_body(Ctx), | ||
Body1 = [string:to_upper(erlang:binary_to_list(B)) || B <- Body], | ||
ewgi_api:response_message_body(Body1, Ctx). |