Skip to content

Instantly share code, notes, and snippets.

@stuartloxton
Created January 19, 2010 16:49
Show Gist options
  • Save stuartloxton/281076 to your computer and use it in GitHub Desktop.
Save stuartloxton/281076 to your computer and use it in GitHub Desktop.
-module (shttp).
-export ([bench_test_start/0, bench_test_stop/0, start/1, start/2]).
bench_test_start() ->
Pid = start(8000, fun handle_page/3),
register(http_server, Pid).
bench_test_stop() ->
bench_test_stop(lists:member( http_server, registered() )).
bench_test_stop(true) ->
http_server ! shutdown,
unregister(http_server),
ok;
bench_test_stop(false) -> ok.
handle_page(Method, Url, Headers) ->
io_lib:format("Url: ~p<br />Method: ~p", [Url, Method]).
start(Callback) ->
start(80, Callback).
start(Port, Callback) ->
inets:start(),
Opts = [{active, false},
binary,
{backlog, 100},
{packet, raw},
{reuseaddr, true}],
{ok, Socket} = gen_tcp:listen(Port, Opts),
Acceptor = spawn(fun() -> accept(Socket, Callback) end),
spawn(fun() -> control(Socket, Acceptor) end).
control(Socket, Acceptor) ->
receive
shutdown ->
exit(Acceptor, shutdown),
gen_tcp:close(Socket);
{callback, Callback} ->
exit(Acceptor, new_callback),
accept(Socket, Callback);
X ->
io:format("Controller got: ~p~n", [X])
end,
control(Socket, Acceptor).
accept(Socket, Callback) ->
case gen_tcp:accept(Socket) of
{ok, Connection} -> spawn(fun() -> receive_data(Connection, Callback) end),
accept(Socket, Callback);
{error, closed} -> ok;
Error -> io:format("Weird Error: ~p~n", [Error])
end.
receive_data(Connection, Callback) ->
case gen_tcp:recv(Connection, 0) of
{ok, Data} ->
spawn(fun() -> handle_http(binary_to_list(Data), Connection, Callback) end);
Error ->
Error
end,
receive_data(Connection, Callback).
handle_http(Request, Connection, Callback) ->
[FirstLine | Rest] = string:tokens(Request, "\r\n"),
[Method, Path, Version] = string:tokens(FirstLine, " "),
ParsedHeaders = string_to_headers(string:join(Rest, "\r\n")),
Headers = lists:append([ParsedHeaders,
[{"method", Method},
{"url", lists:concat(["http://", proplists:get_value("host", ParsedHeaders), Path])},
{"version", Version}]
]),
Response = Callback(Method, "/" ++ Path, Headers),
http_response(Response, Connection),
ok.
http_response({Code, Body}, Connection) ->
Length = integer_to_list(length(Body)),
Response = lists:concat(["HTTP/1.1 ", integer_to_list(Code), " OK\r\n",
"Content-Length: ", Length, "\r\n\r\n", Body]),
gen_tcp:send(Connection, Response),
gen_tcp:close(Connection);
http_response(Body, Connection) ->
Length = integer_to_list(length(Body)),
Response = lists:concat(["HTTP/1.1 200 OK\r\n",
"Content-Length: ", Length, "\r\n\r\n", Body]),
gen_tcp:send(Connection, Response),
gen_tcp:close(Connection).
headers_to_string(Headers) ->
Seperated = lists:map(fun(Pair) ->
{Key, Value} = Pair,
string:join([Key, Value], ": ")
end, Headers),
string:join(Seperated, "\r\n") ++ "\r\n".
string_to_headers(String) ->
Lines = string:tokens(String, "\r\n"),
KeyList = lists:map(fun(Elem) ->
ColonIndex = string:chr(Elem, $:),
{ string:to_lower(string:substr(Elem, 1, ColonIndex - 1)),
string:substr(Elem, ColonIndex + 2, length(Elem)) }
end, Lines),
KeyList.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment