-- File: ArpaEchoServerImpl.mesa - last edit:
-- AOF                 11-Mar-87 16:25:45
-- SMA                 11-Dec-85 11:58:39
-- Copyright (C) 1985, 1987 by Xerox Corporation. All rights reserved. 

DIRECTORY
  ArpaConstants USING [echoPort],
  ArpaEchoServer,
  ArpaPort USING [WaitTime],
  CommHeap USING [zone],
  Environment USING [Block],
  Process USING [Abort],
  TcpStream USING [CompletionCode, Failed, Handle, Listen, Suspended],
  TcpStreamInternal USING [minTcpHeaderBytes, maxTcpDataBytes];
  
ArpaEchoServerImpl: PROGRAM
  IMPORTS CommHeap, Process, TcpStream
  EXPORTS ArpaEchoServer =
  BEGIN
  
  echoServerFork: PROCESS;
  --udpEchoH: ArpaPort.Handle;
  echoPackets : LONG CARDINAL ← 0;
  echoBytes: LONG CARDINAL ← 0;
  
  tmo: ArpaPort.WaitTime = LAST[ArpaPort.WaitTime];  --equates to disabled
  
  CreateServer: PUBLIC PROC [buffers: CARDINAL] =
    BEGIN
    <<udpEchoH ← ArpaPort.Create[
      port: ArpaConstants.echoPort, receive: buffers, portType: listen];
    ArpaPort.SetWaitTime[echoCH, tmo];  --really disables timeouts>>
    echoServerFork ← FORK EchoServer[];
    END; -- CreateServer


  DeleteServer: PUBLIC PROC =
    BEGIN
    Process.Abort[echoServerFork];
    JOIN echoServerFork;
    <<ArpaPort.Delete[udpEchoH];>>
    END;  --DeleteServer
    
    
  EchoServer: PROC =
    BEGIN
    block: Environment.Block;
    tsH: TcpStream.Handle ← NIL;
    BEGIN ENABLE ABORTED =>
      BEGIN
      IF tsH # NIL THEN
        {CommHeap.zone.FREE[@block.blockPointer]; tsH.destroy[tsH]};
      GOTO exit;
      END;
    n: CARDINAL ← 0;
    outcome: TcpStream.CompletionCode;
    maxBytes: CARDINAL = TcpStreamInternal.maxTcpDataBytes;
    Block: TYPE = PACKED ARRAY[0..maxBytes) OF CHARACTER;
    DO
    
      <<b ← NIL; b ← ArpaPort.GetPacket[echoCH ! ABORTED => EXIT];
      SELECT b.arpa.protocol FROM
        (udp) =>
	  BEGIN
	  ArpaPort.SwapSourceAndDestination[b];
	  echoPackets ← echoPackets + 1;
	  echoBytes ← echoBytes + b.arpa.length;
	    IF CommFlags.doStats THEN ArpaStats.Incr[udpsEchoed];
	    IF CommFlags.doStats THEN ArpaStats.Bump[bytesEchoed, b.arpa.length];
	    ArpaPort.PutPacket[echoCH, b];
	    END
	  END;
	(tcp) => Process.Detach[FORK TcpEcho[b]];
	ENDCASE => {--**ICMP error packet here?--};>>
	
      <<There are some unspoken conventions here - that the data will be pushed
        by both ends, and the max size of data to be echoed is one full packet.>>
      
      tsH ← TcpStream.Listen[ArpaConstants.echoPort, tmo !
        TcpStream.Failed => LOOP];
      --build the input data block.
      block.blockPointer ← LOOPHOLE[CommHeap.zone.NEW[Block]];
      block.startIndex ← 0;
      
      DO  --until other end gets bored.
        ENABLE TcpStream.Suspended => GOTO finished;  --whoops
	block.stopIndexPlusOne ← maxBytes;
	[n, outcome] ← tsH.get[block];
	echoPackets ← echoPackets + 1;
	IF outcome = timeout THEN GOTO finished;  --give up.
	--'cause SPP echo counts packet headers.  Estimate (ignore options). 
	echoBytes ← echoBytes + n + TcpStreamInternal.minTcpHeaderBytes;
	block.stopIndexPlusOne ← n;
	tsH.put[block, TRUE, FALSE];
	IF outcome = closing THEN {tsH.close[]; GOTO finished};
	REPEAT
	  finished =>
	    BEGIN
	    CommHeap.zone.FREE[@block.blockPointer];
	    tsH.destroy[tsH];
	    tsH ← NIL;
	    END;
	ENDLOOP;  --end of the echo session.
      
      ENDLOOP;
    END;  --enabled scope.
    EXITS exit => NULL;
    END; -- EchoServer
    
    
  GetCounters: PUBLIC PROC RETURNS [packets, bytes: LONG CARDINAL] =
    {RETURN[echoPackets, echoBytes]};
    
  
  END...
  
LOG

14-Oct-85  9:31:18  SMA  Created file.
11-Mar-87 16:20:45  AOF  Use CommHeap.