summaryrefslogtreecommitdiff
path: root/src/mochiweb/mochiweb_http.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mochiweb/mochiweb_http.erl')
-rw-r--r--src/mochiweb/mochiweb_http.erl273
1 files changed, 0 insertions, 273 deletions
diff --git a/src/mochiweb/mochiweb_http.erl b/src/mochiweb/mochiweb_http.erl
deleted file mode 100644
index ab0af7e8..00000000
--- a/src/mochiweb/mochiweb_http.erl
+++ /dev/null
@@ -1,273 +0,0 @@
-%% @author Bob Ippolito <bob@mochimedia.com>
-%% @copyright 2007 Mochi Media, Inc.
-
-%% @doc HTTP server.
-
--module(mochiweb_http).
--author('bob@mochimedia.com').
--export([start/0, start/1, stop/0, stop/1]).
--export([loop/2, default_body/1]).
--export([after_response/2, reentry/1]).
--export([parse_range_request/1, range_skip_length/2]).
-
--define(REQUEST_RECV_TIMEOUT, 300000). % timeout waiting for request line
--define(HEADERS_RECV_TIMEOUT, 30000). % timeout waiting for headers
-
--define(MAX_HEADERS, 1000).
--define(DEFAULTS, [{name, ?MODULE},
- {port, 8888}]).
-
-parse_options(Options) ->
- {loop, HttpLoop} = proplists:lookup(loop, Options),
- Loop = fun (S) ->
- ?MODULE:loop(S, HttpLoop)
- end,
- Options1 = [{loop, Loop} | proplists:delete(loop, Options)],
- mochilists:set_defaults(?DEFAULTS, Options1).
-
-stop() ->
- mochiweb_socket_server:stop(?MODULE).
-
-stop(Name) ->
- mochiweb_socket_server:stop(Name).
-
-start() ->
- start([{ip, "127.0.0.1"},
- {loop, {?MODULE, default_body}}]).
-
-start(Options) ->
- mochiweb_socket_server:start(parse_options(Options)).
-
-frm(Body) ->
- ["<html><head></head><body>"
- "<form method=\"POST\">"
- "<input type=\"hidden\" value=\"message\" name=\"hidden\"/>"
- "<input type=\"submit\" value=\"regular POST\">"
- "</form>"
- "<br />"
- "<form method=\"POST\" enctype=\"multipart/form-data\""
- " action=\"/multipart\">"
- "<input type=\"hidden\" value=\"multipart message\" name=\"hidden\"/>"
- "<input type=\"file\" name=\"file\"/>"
- "<input type=\"submit\" value=\"multipart POST\" />"
- "</form>"
- "<pre>", Body, "</pre>"
- "</body></html>"].
-
-default_body(Req, M, "/chunked") when M =:= 'GET'; M =:= 'HEAD' ->
- Res = Req:ok({"text/plain", [], chunked}),
- Res:write_chunk("First chunk\r\n"),
- timer:sleep(5000),
- Res:write_chunk("Last chunk\r\n"),
- Res:write_chunk("");
-default_body(Req, M, _Path) when M =:= 'GET'; M =:= 'HEAD' ->
- Body = io_lib:format("~p~n", [[{parse_qs, Req:parse_qs()},
- {parse_cookie, Req:parse_cookie()},
- Req:dump()]]),
- Req:ok({"text/html",
- [mochiweb_cookies:cookie("mochiweb_http", "test_cookie")],
- frm(Body)});
-default_body(Req, 'POST', "/multipart") ->
- Body = io_lib:format("~p~n", [[{parse_qs, Req:parse_qs()},
- {parse_cookie, Req:parse_cookie()},
- {body, Req:recv_body()},
- Req:dump()]]),
- Req:ok({"text/html", [], frm(Body)});
-default_body(Req, 'POST', _Path) ->
- Body = io_lib:format("~p~n", [[{parse_qs, Req:parse_qs()},
- {parse_cookie, Req:parse_cookie()},
- {parse_post, Req:parse_post()},
- Req:dump()]]),
- Req:ok({"text/html", [], frm(Body)});
-default_body(Req, _Method, _Path) ->
- Req:respond({501, [], []}).
-
-default_body(Req) ->
- default_body(Req, Req:get(method), Req:get(path)).
-
-loop(Socket, Body) ->
- mochiweb_socket:setopts(Socket, [{packet, http}]),
- request(Socket, Body).
-
-request(Socket, Body) ->
- case mochiweb_socket:recv(Socket, 0, ?REQUEST_RECV_TIMEOUT) of
- {ok, {http_request, Method, Path, Version}} ->
- mochiweb_socket:setopts(Socket, [{packet, httph}]),
- headers(Socket, {Method, Path, Version}, [], Body, 0);
- {error, {http_error, "\r\n"}} ->
- request(Socket, Body);
- {error, {http_error, "\n"}} ->
- request(Socket, Body);
- {error, closed} ->
- mochiweb_socket:close(Socket),
- exit(normal);
- {error, timeout} ->
- mochiweb_socket:close(Socket),
- exit(normal);
- _Other ->
- handle_invalid_request(Socket)
- end.
-
-reentry(Body) ->
- fun (Req) ->
- ?MODULE:after_response(Body, Req)
- end.
-
-headers(Socket, Request, Headers, _Body, ?MAX_HEADERS) ->
- %% Too many headers sent, bad request.
- mochiweb_socket:setopts(Socket, [{packet, raw}]),
- handle_invalid_request(Socket, Request, Headers);
-headers(Socket, Request, Headers, Body, HeaderCount) ->
- case mochiweb_socket:recv(Socket, 0, ?HEADERS_RECV_TIMEOUT) of
- {ok, http_eoh} ->
- mochiweb_socket:setopts(Socket, [{packet, raw}]),
- Req = mochiweb:new_request({Socket, Request,
- lists:reverse(Headers)}),
- call_body(Body, Req),
- ?MODULE:after_response(Body, Req);
- {ok, {http_header, _, Name, _, Value}} ->
- headers(Socket, Request, [{Name, Value} | Headers], Body,
- 1 + HeaderCount);
- {error, closed} ->
- mochiweb_socket:close(Socket),
- exit(normal);
- _Other ->
- handle_invalid_request(Socket, Request, Headers)
- end.
-
-call_body({M, F}, Req) ->
- M:F(Req);
-call_body(Body, Req) ->
- Body(Req).
-
-handle_invalid_request(Socket) ->
- handle_invalid_request(Socket, {'GET', {abs_path, "/"}, {0,9}}, []).
-
-handle_invalid_request(Socket, Request, RevHeaders) ->
- mochiweb_socket:setopts(Socket, [{packet, raw}]),
- Req = mochiweb:new_request({Socket, Request,
- lists:reverse(RevHeaders)}),
- Req:respond({400, [], []}),
- mochiweb_socket:close(Socket),
- exit(normal).
-
-after_response(Body, Req) ->
- Socket = Req:get(socket),
- case Req:should_close() of
- true ->
- mochiweb_socket:close(Socket),
- exit(normal);
- false ->
- Req:cleanup(),
- ?MODULE:loop(Socket, Body)
- end.
-
-parse_range_request(RawRange) when is_list(RawRange) ->
- try
- "bytes=" ++ RangeString = RawRange,
- Ranges = string:tokens(RangeString, ","),
- lists:map(fun ("-" ++ V) ->
- {none, list_to_integer(V)};
- (R) ->
- case string:tokens(R, "-") of
- [S1, S2] ->
- {list_to_integer(S1), list_to_integer(S2)};
- [S] ->
- {list_to_integer(S), none}
- end
- end,
- Ranges)
- catch
- _:_ ->
- fail
- end.
-
-range_skip_length(Spec, Size) ->
- case Spec of
- {none, R} when R =< Size, R >= 0 ->
- {Size - R, R};
- {none, _OutOfRange} ->
- {0, Size};
- {R, none} when R >= 0, R < Size ->
- {R, Size - R};
- {_OutOfRange, none} ->
- invalid_range;
- {Start, End} when 0 =< Start, Start =< End, End < Size ->
- {Start, End - Start + 1};
- {_OutOfRange, _End} ->
- invalid_range
- end.
-
-%%
-%% Tests
-%%
--include_lib("eunit/include/eunit.hrl").
--ifdef(TEST).
-
-range_test() ->
- %% valid, single ranges
- ?assertEqual([{20, 30}], parse_range_request("bytes=20-30")),
- ?assertEqual([{20, none}], parse_range_request("bytes=20-")),
- ?assertEqual([{none, 20}], parse_range_request("bytes=-20")),
-
- %% trivial single range
- ?assertEqual(undefined, parse_range_request("bytes=0-")),
-
- %% invalid, single ranges
- ?assertEqual(fail, parse_range_request("")),
- ?assertEqual(fail, parse_range_request("garbage")),
- ?assertEqual(fail, parse_range_request("bytes=-20-30")),
-
- %% valid, multiple range
- ?assertEqual(
- [{20, 30}, {50, 100}, {110, 200}],
- parse_range_request("bytes=20-30,50-100,110-200")),
- ?assertEqual(
- [{20, none}, {50, 100}, {none, 200}],
- parse_range_request("bytes=20-,50-100,-200")),
-
- %% no ranges
- ?assertEqual([], parse_range_request("bytes=")),
- ok.
-
-range_skip_length_test() ->
- Body = <<"012345678901234567890123456789012345678901234567890123456789">>,
- BodySize = byte_size(Body), %% 60
- BodySize = 60,
-
- %% these values assume BodySize =:= 60
- ?assertEqual({1,9}, range_skip_length({1,9}, BodySize)), %% 1-9
- ?assertEqual({10,10}, range_skip_length({10,19}, BodySize)), %% 10-19
- ?assertEqual({40, 20}, range_skip_length({none, 20}, BodySize)), %% -20
- ?assertEqual({30, 30}, range_skip_length({30, none}, BodySize)), %% 30-
-
- %% valid edge cases for range_skip_length
- ?assertEqual({BodySize, 0}, range_skip_length({none, 0}, BodySize)),
- ?assertEqual({0, BodySize}, range_skip_length({none, BodySize}, BodySize)),
- ?assertEqual({0, BodySize}, range_skip_length({0, none}, BodySize)),
- BodySizeLess1 = BodySize - 1,
- ?assertEqual({BodySizeLess1, 1},
- range_skip_length({BodySize - 1, none}, BodySize)),
-
- %% out of range, return whole thing
- ?assertEqual({0, BodySize},
- range_skip_length({none, BodySize + 1}, BodySize)),
- ?assertEqual({0, BodySize},
- range_skip_length({none, -1}, BodySize)),
-
- %% invalid ranges
- ?assertEqual(invalid_range,
- range_skip_length({-1, 30}, BodySize)),
- ?assertEqual(invalid_range,
- range_skip_length({0, BodySize + 1}, BodySize)),
- ?assertEqual(invalid_range,
- range_skip_length({-1, BodySize + 1}, BodySize)),
- ?assertEqual(invalid_range,
- range_skip_length({BodySize, 40}, BodySize)),
- ?assertEqual(invalid_range,
- range_skip_length({-1, none}, BodySize)),
- ?assertEqual(invalid_range,
- range_skip_length({BodySize, none}, BodySize)),
- ok.
-
--endif.