-- File: PupListeners.mesa,  Last Edit: Andrew Birrell  May 12, 1983 10:56 am

DIRECTORY
  IO USING [STREAM],
  Process USING [Detach],
  PupRouterDefs USING [GetFirstPupSocket, PupRouterSocket],
  PupStream USING [ PupByteStreamMake, RejectThisRequest],
  PupDefs USING [
    PupAddress, PupBuffer, PupSocketID, Tocks, PupRouterSendThis, SendErrorPup,
    SwapPupSourceAndDest, ReturnFreePupBuffer, PupSocket, PupSocketMake,
    PupSocketKick, PupSocketDestroy, veryLongWait, SetPupContentsBytes,
    AppendRopeToPupBuffer],
  PupTypes USING [fillInPupAddress];

PupListeners: MONITOR
  IMPORTS Process, PupRouterDefs, PupStream, PupDefs
  EXPORTS PupStream =
  BEGIN OPEN PupRouterDefs, PupStream, PupDefs;

  PupListener: TYPE = REF PupListenerObject;
  
  PupListenerObject: PUBLIC TYPE = RECORD[
    socket: PupSocket,
    timeout: Tocks,
    stop: BOOLEAN,
    who: PROCEDURE [IO.STREAM, PupAddress],
    check: PROCEDURE [PupAddress],
    proc: PROCESS
    ];
  
  DontReject: PUBLIC SAFE PROCEDURE [PupAddress] = CHECKED BEGIN END;

  CreatePupByteStreamListener: PUBLIC SAFE PROCEDURE [
    local: PupSocketID, proc: PROCEDURE [IO.STREAM, PupAddress], ticks: Tocks,
    filter: PROCEDURE [PupAddress]] RETURNS [PupListener] =
    TRUSTED BEGIN
    him: PupListener = NEW[PupListenerObject];
    him.socket ← PupSocketMake[local, PupTypes.fillInPupAddress, veryLongWait];
    him.timeout ← ticks;
    him.stop ← FALSE;
    him.who ← proc;
    him.check ← filter;
    him.proc ← FORK Listen[him, local];
    RETURN[him];
    END;

  DestroyPupListener: PUBLIC SAFE PROCEDURE [listener: PupListener] =
    TRUSTED BEGIN
    listener.stop ← TRUE;
    PupSocketKick[listener.socket];
    JOIN listener.proc;
    PupSocketDestroy[listener.socket];
    END;

  Listen: PROCEDURE [listener: PupListener, local: PupSocketID] =
    BEGIN
    soc: PupRouterSocket;
    arg: IO.STREAM;
    b: PupBuffer;
    UNTIL listener.stop DO
      b ← listener.socket.get[];
      IF b # NIL THEN
        BEGIN
        SELECT b.pupType FROM
          rfc =>
            BEGIN OPEN PupStream;
            FOR soc ← GetFirstPupSocket[], soc.next UNTIL soc = NIL DO
              -- check for duplicate
              IF soc.remote # b.source THEN LOOP;
              IF soc.id # b.pupID THEN LOOP;
              b.address ← soc.local;
              SwapPupSourceAndDest[b];
              PupRouterSendThis[b];
              EXIT;
              ENDLOOP;
            IF soc = NIL THEN
              BEGIN -- not a duplicate, make a new connection
              him: PupAddress ← b.address;
              listener.check[
                him !
                RejectThisRequest =>
                  BEGIN
                  b.pupType ← abort;
                  SwapPupSourceAndDest[b];
                  SetPupContentsBytes[b, 2];
                  AppendRopeToPupBuffer[b, error];
                  PupRouterSendThis[b];
                  GOTO Reject;
                  END];
              ReturnFreePupBuffer[b];
              arg ← PupByteStreamMake[ local, him, listener.timeout, alreadyOpened, b.pupID];
              Process.Detach[FORK listener.who[arg, him]];
              END;
            EXITS Reject => NULL;
            END;
          echoMe =>
            BEGIN
            b.pupType ← iAmEcho;
            SwapPupSourceAndDest[b];
            PupRouterSendThis[b];
            END;
          ENDCASE => SendErrorPup[b, LOOPHOLE[100B], "RFC expected"];
        END;
      ENDLOOP;
    END;


  -- initialization

  END. -- of PupListeners