Created
October 7, 2017 15:45
-
-
Save teamon/f845f8afd220a241a88a52709e89a000 to your computer and use it in GitHub Desktop.
HTTP 1.0 web server in Erlang with persistent connections (Connection: keep-alive)
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
#!/usr/bin/env escript | |
%% Original author - Steve Vinoski <[email protected]> | |
%% Modifications by - Tymon Tobolski <[email protected]> | |
%% This is a modified version of https://gist.github.com/vinoski/4996859 | |
%% with support for keeping TCP connection open. | |
%% Useful for testing HTTP clients and their Connection: keep-alive support | |
%% Usage: ./httpd.erl [PORT] | |
main([]) -> | |
run(7777); | |
main([String]) -> | |
Port = list_to_integer(String), | |
run(Port). | |
run(Port) -> | |
start(fun(S, Req) -> handler(S, Req) end, Port). | |
handler(_S, _Req) -> | |
{200, [{"Connection", "keep-alive"}, {"Content-Length", "6"}], "Hello\n"}. | |
start(Handler, Port) -> | |
io:format("Starting server at http://localhost:~w\n", [Port]), | |
{ok, LS} = gen_tcp:listen(Port, [{reuseaddr, true}, binary, {backlog, 1024}]), | |
spawn(fun() -> accept(LS, Handler) end), | |
receive stop -> gen_tcp:close(LS) end. | |
accept(LS, Handler) -> | |
io:format("Awaiting connection\n"), | |
{ok, S} = gen_tcp:accept(LS), | |
io:format("Connection opened\n"), | |
ok = inet:setopts(S, [{packet, http_bin}]), | |
serve(S, Handler, [{headers, []}]), | |
accept(LS, Handler). | |
serve(S, Handler, Req) -> | |
ok = inet:setopts(S, [{active, once}]), | |
HttpMsg = receive | |
{http, S, Msg} -> | |
io:format("HttpMsg: ~p\n", [Msg]), | |
Msg; | |
Other -> | |
io:format("HttpMsg: ~p\n", [Other]), | |
io:format("Connection closed\n"), | |
gen_tcp:close(S) | |
end, | |
case HttpMsg of | |
{http_request, M, {abs_path, Uri}, Vsn} -> | |
NReq = [{method, M}, {uri, Uri}, {version, Vsn}|Req], | |
serve(S, Handler, NReq); | |
{http_header, _, Hdr, _, Val} -> | |
{headers, Hdrs} = lists:keyfind(headers, 1, Req), | |
serve(S, Handler, lists:keystore(headers, 1, Req, | |
{headers, [{Hdr,Val}|Hdrs]})); | |
http_eoh -> | |
ok = inet:setopts(S, [{packet, raw}]), | |
{Status, Hdrs, Resp} = try Handler(S, Req) catch _:_ -> {500, [], <<>>} end, | |
ok = gen_tcp:send(S, ["HTTP/1.1 ", integer_to_list(Status), "\r\n", | |
[[H, ": ", V, "\r\n"] || {H,V} <- Hdrs], | |
"\r\n", Resp]), | |
ok = inet:setopts(S, [{packet, http_bin}]), | |
serve(S, Handler, [{headers, []}]); | |
{http_error, Error} -> | |
exit(Error); | |
ok -> ok | |
end. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment