Skip to content

Instantly share code, notes, and snippets.

@teamon
Created October 7, 2017 15:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save teamon/f845f8afd220a241a88a52709e89a000 to your computer and use it in GitHub Desktop.
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)
#!/usr/bin/env escript
%% Original author - Steve Vinoski <vinoski@ieee.org>
%% Modifications by - Tymon Tobolski <i@teamon.eu>
%% 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