2011年11月10日木曜日

Erlang+TCP

ErlangでTCPサーバを書いてみます。
ローカルプロキシを作ってみたいです。




-module(serv).
-compile(export_all).
-compile([start/1]).

-define(TCP_OPTIONS, [binary, {packet, raw}, {active, false}, {reuseaddr, true}]).

start (Port) ->
spawn(?MODULE, init, [Port, self()]).

init(Port, Pid) ->
{ok, Listen} = gen_tcp:listen(Port, ?TCP_OPTIONS),
accept_loop(Listen, Pid).

accept_loop(Listen, Pid) ->
case gen_tcp:accept(Listen) of
{ok, Accept} ->
spawn(?MODULE, accept_handler, [Accept, Pid]),
accept_loop(Listen, Pid)
end.

accept_handler(Accept, Pid) ->
inet:setopts(Accept, [{packet, http}]),
{ok, {http_request, Method, URL, {Major, Minor}}} = gen_tcp:recv(Accept, 0),
Headers = recv_request_headers(Accept),
inet:setopts(Accept, [{packet, raw}]),
Body =
case lists:keyfind('Content-Length', 1, Headers) of
{'Content-Length', Len} when is_integer(Len) ->
gen_tcp:recv(Accept, Len);
_ -> <<>>
end,
io:format("~p, ~p, ~p/~p~n~p~n~p~n",
[Method, URL, Major, Minor, Headers, Body]),
case URL of
{abs_path, Path} ->
Txt = "<html><body><a>not found</a></body></html>",
gen_tcp:send(Accept, "HTTP/1.1 404 not-found\r\nContent-Length: "),
gen_tcp:send(Accept, integer_to_list(length(Txt))),
gen_tcp:send(Accept, "\r\n\r\n"),
gen_tcp:send(Accept, Txt);
{absoluteUrl, Protocol, Host, Port, Path} ->
Txt = "<html><body><a>not found</a></body></html>",
gen_tcp:send(Accept, "HTTP/1.1 404 not-found\r\nContent-Length: "),
gen_tcp:send(Accept, integer_to_list(length(Txt))),
gen_tcp:send(Accept, "\r\n\r\n"),
gen_tcp:send(Accept, Txt)
end,
case lists:keyfind('Connection', 1, Headers) of
{'Connection', "keep-alive"} ->
accept_handler(Accept, Pid);
_ ->
gen_tcp:close(Accept)
end.


recv_request_headers(Accept) ->
recv_request_headers(Accept, []).

recv_request_headers(Accept, Hs) ->
case gen_tcp:recv(Accept, 0) of
{ok, http_eoh} -> % end of header
Hs;
{ok, {http_header, _, Key, _, Val}} ->
recv_request_headers(Accept, [{Key,Val} | Hs])
end.



0 件のコメント:

コメントを投稿