Middle men wanted

Peter-Henry Mander <>
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 <> 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 ()
> %% 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