Ivory Socket Proxy Example

This example extends the functionality of the Socket Demo to a limited useful application.

A rule is defined with an interpreted method to monitor the addition or removal of a socket to or from the listener socket connections list. For an addition, a proxy socket is created to relay any input to and from a remote host. Removal simply causes the proxy socket to be destroyed - the client property is removed first for reasons of referential integrity.

This example also demonstrates the overloaded (.) method of the class Select for built-in socket properties such as connect, send, receive and close. For these, a number of type constraints are needed where the context is such that a type can't be inferred. For example, the constraint

client.close::Void

is needed to determine the result type of the enclosing if.

Note how a reference to the newly created proxy object is added to the accepted socket dynamically. Again, there is no need for a new type or subclass to achieve the desired functionality.

-------------------------------------------------------------------------------
--
-- Name:    socketProxy.is
--
-------------------------------------------------------------------------------
--
-- Description:
--
--    Demonstration IvoryScript configuration file
-- 
--    This script defines a simple application to relay and display any
--    input received on a listening socket to a proxy socket.
--
-------------------------------------------------------------------------------

let acceptedSocketRuleMethod rule::Rule event::Expr _::ADS eventPhase::EventPhase =
   case event of
   {
      AddRefEvent client::Socket listener::Socket #connections ->
         if listener::Ref = (rule.listenerSocket::Ref) then
            let proxy::Socket = .commsStore.Socket[
               host:          "remotehost",  -- To be set appropriately
               client:        client,
               port:          80,
               mode:          Connect,
               onConnected:   (\socket::Socket ok::Bool ->
                                 if not ok then {
                                    show "Proxy socket connection failed\n";
                                    (socket.client::Socket).close::Void
                                 }),
               onReceive:     (\socket::Socket -> let bs::ByteString = socket.receive
                                              in {
                                                 show (bytesToString bs);
                                                 ignore (((socket.client::Socket).send bs)::Int)
                                                })]
            in
               if proxy.connect then {
                  client.proxy := proxy;
                  client.onReceive :=
                             (\socket::Socket ->
                                 ignore ((socket.proxy::Socket).send (socket.receive::ByteString))::Int)
            } else { 
               show "Can\'t connect proxy socket\n";
               destroyObject proxy; 
               client.close::Void
            };

      RemoveRefEvent client::Socket listener::Socket #connections ->
         if listener::Ref = (rule.listenerSocket::Ref) and
            hasProperty client #proxy then
            let proxy::Ref = client.proxy
            in {
               removeProperty client #proxy;
               destroyObject proxy
            }
   }
in
   def {
      .socketRule = .Rule [
         method:              monitorSocket];

      .commsStore =.ADS [
         ruleList:           !RefList[]];

      .commsStore.listenerSocket = .commsStore.Socket [
         port:                81,
         mode:                Listener,
         onAccept:            acceptSocket,
         connectionLimit:     2,
         connections:         !RefList[]];

      .acceptedSocketRule = .Rule [
         listenerSocket:      !.commsStore.listenerSocket,
         method:              acceptedSocketRuleMethod];

      .startADSGroup = !RefList [.commsStore];
      .start = mapProcRefs (raiseEvent (StartEvent 0)) (.startADSGroup)
   };
addRef (.socketRule)          (.commsStore)     #ruleList;
addRef (.acceptedSocketRule)  (.commsStore)     #ruleList;

home

Last update: 11 October, 2005