-- NetworkStreamMgr.mesa (last edited by: Garlick on: January 26, 1981  11:02 AM)
-- Function: The implementation module for the manager of Pilot Network Streams.

DIRECTORY
  BufferDefs USING [OisBuffer],
  CommunicationInternal USING [],
  NetworkStream USING [
    ConnectionID, ConnectionFailed, ConnectionSuspended, unknownConnID, WaitTime,
    ClassOfService, CloseStatus, closeSST, closeReplySST, defaultWaitTime,
    ListenTimeout, infiniteWaitTime],
  NetworkStreamInstance USING [],
  NetworkStreamInternal USING [CloseState, ControlHandle],
  OISCP USING [ReturnFreeOisBuffer],
  OISCPTypes USING [ConnectionID],
  PacketStream USING [
    ConnectionAlreadyThere, FindAddresses, Handle, Make, SetWaitTime],
  Router USING [XmitStatus],
  Socket USING [
    Abort, AssignNetworkAddress, ChannelAborted, ChannelError, ChannelHandle,
    Create, Delete, GetPacket, SetWaitTime, TimeOut],
  SocketInternal USING [
    SocketHandle, SocketHandleToChannelHandle, ChannelHandleToSocketHandle],
  SpecialSystem USING [NetworkAddress, nullNetworkAddress],
  Stream USING [
    Byte, Handle, PutByte, GetByte, SendNow, SetSST, SSTChange, TimeOut],
  System USING [];

NetworkStreamMgr: PROGRAM
  IMPORTS
    NetworkStream, netStrmInst: NetworkStreamInstance, OISCP, PacketStream,
    Socket, SocketInternal, Stream
  EXPORTS CommunicationInternal, NetworkStream, System
  SHARES BufferDefs, NetworkStreamInstance =
  BEGIN OPEN NetworkStream;

  -- EXPORTED TYPE(S) and READONLY Variables
  ListenerHandle: PUBLIC TYPE = SocketInternal.SocketHandle;
  NetworkAddress: PUBLIC TYPE = SpecialSystem.NetworkAddress;
  uniqueNetworkAddr: PUBLIC NetworkAddress ← SpecialSystem.nullNetworkAddress;

  IllegalAddress: PUBLIC ERROR = CODE;
  ListenTimeout: PUBLIC SIGNAL = CODE;

  -- Cool Procedures

  -- This procedure creates a network stream to the specified remote address.
  Create: PUBLIC PROCEDURE [remote: NetworkAddress, timeout: WaitTime ←
    defaultWaitTime, classOfService: ClassOfService ← bulk]
    RETURNS [Stream.Handle] =
    BEGIN
    RETURN[
      CreateTransducer[
	uniqueNetworkAddr, remote, NetworkStream.unknownConnID,
	NetworkStream.unknownConnID, TRUE, timeout, classOfService]];
    END; -- Create

  AssignNetworkAddress: PUBLIC PROCEDURE RETURNS [NetworkAddress] =
    BEGIN RETURN[Socket.AssignNetworkAddress[]]; END; -- AssignNetworkAddress

  -- This procedure returns the local and remote addresses of the Network Stream.

  FindAddresses: PUBLIC PROCEDURE [sH: Stream.Handle]
    RETURNS [local, remote: NetworkAddress] =
    BEGIN
    [local, remote] ← PacketStream.FindAddresses[
      LOOPHOLE[sH, NetworkStreamInternal.ControlHandle].psH];
    END; -- FindAddresses

  -- This procedure sets the wait time for the Network Stream.

  SetWaitTime: PUBLIC PROCEDURE [sH: Stream.Handle, time: WaitTime] =
    BEGIN
    PacketStream.SetWaitTime[
      LOOPHOLE[sH, NetworkStreamInternal.ControlHandle].psH, time];
    END; -- SetWaitTime

  -- This procedure creates a listener at the specified local Network Address.
  -- The generation of IllegalAddress will become more sophisticated.
  -- We enqueue all the buffers onto the socket channel.

  CreateListener: PUBLIC PROCEDURE [addr: NetworkAddress]
    RETURNS [lH: ListenerHandle] =
    BEGIN
    listenerSendBuffers: CARDINAL = 0;
    listenerReceiveBuffers: CARDINAL = 2;
    lH ← SocketInternal.ChannelHandleToSocketHandle[
      Socket.Create[
      addr, listenerSendBuffers, listenerReceiveBuffers, 0, FALSE !
      Socket.ChannelError => GOTO bad]];
    EXITS bad => RETURN WITH ERROR IllegalAddress;
    END; -- CreateListener

  -- This procedure deletes a listener.

  DeleteListener: PUBLIC PROCEDURE [listenerH: ListenerHandle] =
    BEGIN
    -- We assume that there are no processes doing a Listen.
    -- The Listen code is such that it always does a Socket.Get leaving the buffer with
    -- the socket channel.
    Socket.Abort[SocketInternal.SocketHandleToChannelHandle[listenerH]];
    Socket.Delete[SocketInternal.SocketHandleToChannelHandle[listenerH]];
    END; -- DeleteListener


  -- This procedure creates a sequenced packet transducer with all its parameters.

  CreateTransducer: PUBLIC PROCEDURE [
    local, remote: NetworkAddress,
    localConnID, remoteConnID: NetworkStream.ConnectionID,
    activelyEstablish: BOOLEAN, timeout: NetworkStream.WaitTime ←
    defaultWaitTime, classOfService: ClassOfService ← bulk]
    RETURNS [sH: Stream.Handle] =
    BEGIN
    psH: PacketStream.Handle;
    newNetStrmInst: POINTER TO FRAME[NetworkStreamInstance];
    psH ← PacketStream.Make[
      local, remote, LOOPHOLE[localConnID, OISCPTypes.ConnectionID],
      LOOPHOLE[remoteConnID, OISCPTypes.ConnectionID], activelyEstablish,
      timeout, classOfService];
    newNetStrmInst ← NEW netStrmInst;
    sH ← START newNetStrmInst[psH];
    END; -- CreateTransducer

  -- These procedures define an optional close protocol using the exchange of reserved
  -- subsequence types on the client's behalf.

  -- This procedure closes communication over a network stream at the client's
  -- level of protocol.  The semantics are I want to close the stream, therefore I do
  -- not want to transmit any more data and only want to know that the remote
  -- end is aware of my actions.  Any input data arriving on the stream may be discarded.
  -- The status is good, noReply if the other end just did not respond, and incomplete if it
  -- was a simultaneous close and the third data packet did not make it and the other
  -- end is gone.

  Close: PUBLIC PROCEDURE [sH: Stream.Handle]
    RETURNS [status: NetworkStream.CloseStatus] =
    BEGIN
    state: NetworkStreamInternal.CloseState ← sendClose;
    dataByte: Stream.Byte ← 0;
    status ← good;
    Stream.SetSST[sH, NetworkStream.closeSST];
    Stream.PutByte[sH, dataByte];
    Stream.SendNow[sH];
    state ← waitCloseReply;
    UNTIL state = closed OR state = sendCloseReply DO
      dataByte ← Stream.GetByte[
	sH !
	Stream.SSTChange =>
	  BEGIN
	  IF sst = NetworkStream.closeReplySST THEN
	    BEGIN
	    -- tricky we are diddling the stream from within a catch phrase
	    Stream.SetSST[sH, NetworkStream.closeReplySST];
	    Stream.PutByte[sH, dataByte];
	    Stream.SendNow[sH];
	    state ← closed;
	    END
	  ELSE IF sst = NetworkStream.closeSST THEN state ← sendCloseReply;
	  -- simultaneous close
	  RESUME
	  ;
	  END;
	Stream.TimeOut => BEGIN status ← noReply; state ← closed; EXIT; END;
	NetworkStream.ConnectionSuspended =>
	  BEGIN status ← noReply; state ← closed; EXIT; END];
      ENDLOOP;
    IF state = sendCloseReply THEN status ← CloseReply[sH];
    END; -- Close

  -- This procedure conforms to the close protocol and is invoked by the client of
  -- a network stream when it receives a subsequence change to closeSST.  The
  -- semantics of this call are that the client knows that the other end wants to
  -- close the communication and it will not transmit any more data and will reply
  -- with a closeReplySST.  The status can be good, or incomplete; the latter implying
  -- that an answer to the closeReplySST did not make it back and the other end is
  -- gone, or that the closeReply was never acked.

  CloseReply: PUBLIC PROCEDURE [sH: Stream.Handle]
    RETURNS [status: NetworkStream.CloseStatus] =
    BEGIN
    state: NetworkStreamInternal.CloseState ← sendCloseReply;
    dataByte: Stream.Byte ← 0;
    status ← good;
    Stream.SetSST[sH, NetworkStream.closeReplySST];
    Stream.PutByte[sH, dataByte];
    Stream.SendNow[sH];
    state ← waitCloseReplyReply;
    UNTIL state = closed DO
      dataByte ← Stream.GetByte[
	sH !
	Stream.SSTChange =>
	  BEGIN
	  IF sst = NetworkStream.closeReplySST THEN state ← closed;
	  RESUME
	  ;
	  END;
	Stream.TimeOut => BEGIN status ← incomplete; state ← closed; EXIT; END;
	NetworkStream.ConnectionSuspended =>
	  BEGIN status ← noReply; state ← closed; EXIT; END];
      ENDLOOP;
    END; -- CloseReply

  -- Hot Procedures

  -- This procedure listens on the specified socket channel for a sequenced packet, and
  -- creates a network stream from another unique local socket to the remote end.
  -- This procedure checks to make sure that the arriving packet is not a duplicate.

  Listen: PUBLIC PROCEDURE [
    listenerH: ListenerHandle, listenTimeout: NetworkStream.WaitTime ← infiniteWaitTime,
    streamTimeout: NetworkStream.WaitTime ← infiniteWaitTime, classOfService: 
    ClassOfService ← bulk]
    RETURNS [Stream.Handle] =
    BEGIN OPEN SocketInternal;
    b: BufferDefs.OisBuffer;
    sH: Stream.Handle;
    Socket.SetWaitTime[SocketHandleToChannelHandle[listenerH], listenTimeout];
    DO
      -- until the packet is a sequenced packet and is not a duplicate, or timeout, or aborted socket
      sH ← NIL;
      b ← LOOPHOLE[Socket.GetPacket[
	SocketInternal.SocketHandleToChannelHandle[listenerH] !
	Socket.TimeOut => {SIGNAL NetworkStream.ListenTimeout; RETRY}; Socket.ChannelAborted => EXIT],
	BufferDefs.OisBuffer];
      -- b is NIL if client aborted this process
      -- b.status can be one of the errors or goodCompletion
      IF b # NIL AND LOOPHOLE[b.status, Router.XmitStatus] = goodCompletion THEN
	BEGIN
	IF b.ois.transCntlAndPktTp.packetType = sequencedPacket THEN
	  BEGIN
	  -- examine this packet
	  IF b.ois.destinationConnectionID = unknownConnID AND b.ois.sourceConnectionID #
	    unknownConnID AND b.ois.sequenceNumber = 0 AND b.ois.systemPacket THEN
	    BEGIN
	    -- good packet, but must see if this is an old duplicate
	    IF NOT PacketStream.ConnectionAlreadyThere[
	      b.ois.source, b.ois.sourceConnectionID] THEN
	      BEGIN
	      source: SpecialSystem.NetworkAddress ← b.ois.source;
	      remoteConnID: NetworkStream.ConnectionID ←
		LOOPHOLE[b.ois.sourceConnectionID];
	      OISCP.ReturnFreeOisBuffer[b];
	      sH ← CreateTransducer[
		uniqueNetworkAddr, source, NetworkStream.unknownConnID,
		remoteConnID, TRUE, streamTimeout, classOfService !
		ConnectionFailed => BEGIN sH ← NIL; CONTINUE; END];
	      EXIT;
	      END;
	    END;
	  END;
	END;
      OISCP.ReturnFreeOisBuffer[b];
      ENDLOOP;
    -- NIL sH means client aborted via DeleteListener or Process.Abort
    RETURN[sH];
    END; -- Listen


  -- initialization (Cold)

  END. -- NetworkStreamMgr module

LOG

Time: May 9, 1978  1:12 PM  By: Dalal  Action: created file.
Time: August 31, 1979  1:14 PM  By: Dalal  Action: modified Listener routines.
Time: January 26, 1980  2:51 PM  By: Dalal  Action: moved stuff to PacketStreamMgr.
Time: April 15, 1980  10:22 AM  By: BLyon  Action: uses new Level1 routines.
Time: July 15, 1980  11:11 AM  By: BLyon  Action: Use exported types.
Time: September 29, 1980  3:51 PM  By: BLyon  Action: Listener catches Socket.ChannelAborted.
Time: January 26, 1981  11:02 AM  By: Garlick  Action: 1) Listener generates resumeable signal on timeout and also allows client to use Process.Abort.  2) added class of service to Create, CreateTransducer, Listen.