Middle men wanted
Peter-Henry Mander
erlang@REDACTED
Wed May 5 11:50:56 CEST 2004
Hi Joe,
Would you be interested in a SIP middleman? (Pending the
outcome of the Open-Source battle I'm currently waging, as you may
recall... Middlemen of a different ilk!)
It's not a C driver though, the codec is pure Erlang. Is there a way to
produce a driver almost directly from Erlang code, like an Erlang
compiler that emits C code? Failing that, any hints about how to
automate the process?
Pete.
On Wed, 5 May 2004 11:25:18 +0200 (CEST)
Joe Armstrong <joe@REDACTED> wrote:
>
>
> I'm collecting middle men - let me explain.
>
> Cheers
>
> /Joe
>
> (warning long text)
>
>
> What is a middle man?
> =====================
>
> TCP application protocols (as defined in RFCs) are a pain to program
> - I want therefore a number of device drivers (I call them middle men)
> to isolate the protocols from the programs which respond to the
> protocols.
>
>
> A middle man sits between a TCP socket and an erlang program. It
> converts the protocol packets - into Erlang terms in a transparnt
> manner. ie for every protocl messgae there is exactly one Erlang term
> and vice. versa.
>
> Note: the same middle_man is used for both the client and the server
> software :-)
>
>
> +-----------------+
> TCP packets | Middle man | Erlang terms
> ---->-----------| |----->------
> | |
> TCP packets | | Erlang terms
> ----<-----------| |-----<------
> +-----------------+
>
>
> The middle man does packet re-assembly etc for TCP packets and
> parsing.
>
> So for example: if a HTTP middle man recives a
>
>
> GET .....
>
> request, it will parse the entire request and send a {get," ..."}
> message
> to it's output.
>
>
> What do I want?
> ===============
>
> I have written the following middle men
>
> eterm (erlang terms)
> http
> estream (erlang binary representaion of terms)
> xml (generic)
>
> I am writing:
>
> xmlrpc
> soap
>
> I want:
>
> IRC, NNTP, POP2, ....
>
> Why do I want middle men and where is this leading?
> ===================================================
>
> I have made a erlang deamon that acts as a "polyport" - ie just open
> port 1234 (say) on the server and start talking in any protocol you
> feel like - the port analyses the input and starts the appropriate
> middle man.
>
> Using the esteam protocol a client can then do the following:
>
> {ok, S} = session:start("host", Port, estream)
>
> This opens a estream connection to Host,Port
>
> session:rpc(S, {apply, [M,F,A]})
>
> do an rpc on the server.
>
> Now the server code is like this:
>
> loop(Client) ->
> receive
> {From, {apply,{M,F,A}} ->
> Val = (catch apply(M,F,A)),
> From ! {send, Val}
> end.
>
> This is *very* powerful (see security later)
>
> So suppose I want to transfer a file from a clien to a server. The
> client code is
>
> {ok, S} = session:start("host", Port, estream),
> {ok, B} = file:read_file(File),
> session:rpc(S, {apply, {file,write_file,[Bin]}}),
> session:close(S)
>
> Which transfers a file from the client to the server - this works
> despite the fact
> there is no file server (ie like FTP) running on the server.
>
> <<security>> I don't allow applies until the client has
> authenticated itself <<>>
>
> Soon - all nodes in planetlab will offer polyport servers :-)
>
> Writing a server
> ================
>
> Middle men make writing servers very easy.
>
> For example, my HTTP server now looks like this:
>
> server(Client) ->
> receive
> {Client, closed} ->
> exit(handler_closed);
> {Client, {msg, Request}} ->
> Response = generate_response(Request),
> Client ! {msg, Response},
> server(Client);
> Other ->
> io:format("Mod http:unexpected message:~p~n",[Other])
> end.
>
> generate_response({_, Vsn, F, Args, Env}) ->
> F1 = "." ++ F,
> case file:read_file(F1) of
> {ok, Bin} ->
> case classify(F) of
> html ->
> {header(html),[Bin]};
> jpg ->
> {header(jpg),[Bin]};
> gif ->
> {header(jpg),[Bin]};
> _ ->
> {header(text),[body("white"),"<pre>",Bin,"</pre>"
> ]}
> end;
> _ ->
> show({no_such_file,F,args,Args,cwd,file:get_cwd()})
> end.
>
>
> Middle men API
> ==============
>
> The middle man is started with a call like this:
>
> M:start(Args, Client, Socket, Bin)
>
> (Args is ignored)
>
> Client is a Pid to whom all decoded messages should be sent.
> Socket is the TCP socket
> Bin is the first data packet that was sent to the socket
> then the connection was extablised.
>
> The middle man protocol is handled in a control loop like this:
>
> The loop handles the following messages
>
> {tcp, Socket, Bin} -- data from the socket
> {tcp_closed, Socket} -- socket closed
> close -- request to close the socket
> {send, Term} -- request to send the term Term
> {'DOWN',_,process,Client,_} -- the Client dies
>
>
> And is something like this
>
> loop(Client, Socket, Cont) ->
> receive
> {tcp, Socket, Bin} ->
> received(Client, Socket, Bin, Cont);
> {tcp_closed, Socket} ->
> Client ! {self(), closed};
> close ->
> gen_tcp:close(Socket);
> {send, Term} ->
> ... format the term ...
> B = format_term(Term),
> gen_tcp:send(Socket ,B),
> loop(Client, Socket, Cont);
> {'DOWN',_,process,Client,_} ->
> gen_tcp:close(Socket);
> Other ->
> ed_log:error({middle_man_eterm,funny,msg,Other})
> end.
>
> At the end I have included a middle man that converts between the
> textual and internal form of Erlang terms.
>
>
>
> Middle man example
> ==================
>
> -module(ed_middle_man_eterm).
>
>
> %% Copyright (C) 2004 by Joe Armstrong (joe@REDACTED)
> %% All rights reserved.
> %% The copyright holder hereby grants the rights of usage,
> distribution%% and modification of this software to everyone and for
> any purpose, as%% long as this license and the copyright notice above
> are preserved and%% not modified. There is no warranty for this
> software.
>
> -compile(export_all).
>
> start(Args, Client, Socket, <<"erl\r\n",B/binary>>) ->
> erlang:monitor(process, Client),
> received(Client, Socket, binary_to_list(B), []);
> start(Args, Client, Socket, <<"erl\r",B/binary>>) ->
> erlang:monitor(process, Client),
> received(Client, Socket, binary_to_list(B), []);
> start(Args, Client, Socket, <<"erl\n",B/binary>>) ->
> erlang:monitor(process, Client),
> received(Client, Socket, binary_to_list(B), []).
>
> received(Client, Socket, Str, Cont) ->
> case erl_scan:tokens(Cont, Str, 1) of
> {more, C1} ->
> loop(Client, Socket, C1);
> {done, Result, Rest} ->
> case Result of
> {ok, Toks, _} ->
> case erl_parse:parse_term(Toks) of
> {ok, Term} ->
> Client ! {self(), Term};
> _ ->
> exit(bad_term)
> end;
> E ->
> exit(bad_term)
> end,
> received(Client, Socket, Rest, [])
> end.
>
> %% ---> {tcp,Socket,Bin}
> %% ---> {tcp_closed, Socket}
> %% ---> close
> %% ---> {send, X}
> %% ---> {'DOWN',_,_,_,_} (from client)
>
> loop(Client, Socket, Cont) ->
> receive
> {tcp, Socket, Bin1} ->
> received(Client, Socket, binary_to_list(Bin1), Cont);
> {tcp_closed, Socket} ->
> Client ! {self(), closed};
> close ->
> gen_tcp:close(Socket);
> {send, Term} ->
> B = io_lib:format("~p",[Term]),
> io:format("Sending:~p~n",[Term]),
> gen_tcp:send(Socket ,[B,".\n"]),
> loop(Client, Socket, Cont);
> {'DOWN',_,process,Client,_} ->
> gen_tcp:close(Socket);
> Other ->
> ed_log:error({middle_man_eterm,funny,msg,Other})
> end.
>
>
>
>
>
--
"The Tao of Programming
flows far away
and returns
on the wind of morning."
More information about the erlang-questions
mailing list