-- EchoServerImpl.mesa (last edited by: Garlick on: January 23, 1981  5:43 PM
-- Function: The implementation module for the Pilot OISCP Echo Server.

DIRECTORY
  BufferDefs USING [OisBuffer],
  CommunicationInternal USING [],
  CommFlags USING [doStats],
  Echo USING [echoRequest, echoResponse],
  OISCP USING [ReturnFreeOisBuffer, unknownNetID],
  OISCPConstants USING [echoerSocket],
  Router USING [FindMyHostID],
  Socket USING [
    Abort, ChannelAborted, Create, Delete, GetPacket, PutPacket, SetWaitTime,
    TimeOut, TransferStatus, WaitTime],
  SocketInternal USING [SocketHandle],
  SpecialSystem USING [NetworkAddress],
  StatsDefs USING [StatIncr, StatBump],
  System USING [];

EchoServerImpl: PROGRAM
  IMPORTS OISCP, Router, Socket, StatsDefs
  EXPORTS CommunicationInternal, Echo, Socket, System
  SHARES BufferDefs =
  BEGIN

  -- EXPORTED TYPE(s)
  NetworkAddress: PUBLIC TYPE = SpecialSystem.NetworkAddress;
  ChannelHandle: PUBLIC TYPE = SocketInternal.SocketHandle;

  -- global constants to all
  nBuffers: CARDINAL = 2;
  -- global variables to all
  cH: ChannelHandle;
  localAddr: NetworkAddress;
  echoServerFork: PROCESS;
  echoerTimeout: Socket.WaitTime = 7777777777B; -- in msec (about 10 days)

  -- Cold Procedures
  -- This procedure creates an echo server.
  CreateServer: PUBLIC PROCEDURE =
    BEGIN
    localAddr ←
      [net: OISCP.unknownNetID, host: Router.FindMyHostID[],
	socket: OISCPConstants.echoerSocket];
    cH ← Socket.Create[localAddr, 0, nBuffers, 0, FALSE];
    -- no send or reserve buffers, 2 receive buffers, FALSE -> share buffers from system pool.
    echoServerFork ← FORK EchoServer[];
    END; -- CreateServer

  -- This procedure deletes the echo server.

  DeleteServer: PUBLIC PROCEDURE =
    BEGIN Socket.Abort[cH]; JOIN echoServerFork; Socket.Delete[cH]; END;
  -- DeleteListener

  -- Hot Procedures
  -- This procedure echos Echo protocol packets back to the remote end.

  EchoServer: PROCEDURE =
    BEGIN
    buf: BufferDefs.OisBuffer;
    Socket.SetWaitTime[cH, echoerTimeout];
    -- init the PhysicalRecords and start the gets
    DO
      -- wait for an echo request packet
      buf ← NIL;
      buf ← Socket.GetPacket[
	cH ! Socket.TimeOut => RETRY; Socket.ChannelAborted => EXIT];
      -- b.status can be one of aborted or goodCompletion
      SELECT LOOPHOLE[buf.status, Socket.TransferStatus] FROM
	goodCompletion =>
	  BEGIN
	  IF buf.ois.transCntlAndPktTp.packetType = echo THEN
	    BEGIN
	    firstWord: LONG POINTER TO WORD ← @buf.ois.oisWords[0];
	    -- examine this packet
	    IF firstWord↑ = Echo.echoRequest THEN
	      BEGIN
	      -- good packet, echo it
	      buf.ois.destination ← buf.ois.source;
	      firstWord↑ ← Echo.echoResponse;
	      IF CommFlags.doStats THEN StatsDefs.StatIncr[packetsEchoed];
	      IF CommFlags.doStats THEN
		StatsDefs.StatBump[bytesEchoed, buf.ois.pktLength];
	      Socket.PutPacket[
		cH, buf !
		Socket.ChannelAborted =>
		  BEGIN OISCP.ReturnFreeOisBuffer[buf]; EXIT; END];
	      LOOP;
	      END
	    ELSE IF CommFlags.doStats THEN StatsDefs.StatIncr[packetsBadEchoed]
	    END
	  ELSE IF CommFlags.doStats THEN StatsDefs.StatIncr[packetsBadEchoed];
	  END; -- end goodCompletion

	aborted => EXIT;
	ENDCASE => NULL;
      OISCP.ReturnFreeOisBuffer[buf];
      ENDLOOP;

    -- we got here via an abort or error; now clean up this world.
    Socket.Abort[cH];
    END; -- EchoServer

  END.