-- File: CourierImplN.mesa - last edit:
-- AOF                  9-Nov-87 18:38:29
-- Copyright (C) 1984, 1985, 1986, 1987 by Xerox Corporation. All rights reserved. 

DIRECTORY
  CommSwitches USING [sppNegotiation],
  Courier USING [Error, ErrorCode, LocalSystemElement, Object],
  CourierInternal USING [
    activeTimeout, AugmentedObj, AugmentedStream, CreateNewStream,
    createTimeout, Creator, doStats, hopWeight, Listener, longZone,
    NewStreamFailed, Receiver, stats, UserConnection],
  CourierProtocol USING [Protocol, ProtocolRange, pvHigh],
  Environment USING [bytesPerWord, nullBlock],
  NetworkStream USING [
    ApproveConnection, Close, CloseReply, closeSST, ConnectionFailed,
    ConnectionSuspended, Create, CreateListener, FailureReason, Listen,
    ListenerHandle, SuspendReason, WaitTime],
  NewNetworkStream,
  NSConstants USING [courierSocket],
  Process USING [Detach],
  Router USING [GetDelayToNet, NoTableEntryForNet],
  Stream USING [
    defaultObject, DeleteProcedure, GetProcedure, Handle, Object,
    PutProcedure, SendAttentionProcedure, SendNowProcedure, SetSSTProcedure,
    SubSequenceType, WaitAttentionProcedure],
  System USING [NetworkAddress, NetworkNumber, switches];
  
CourierImplN: PROGRAM
  IMPORTS
    Courier, CourierInternal, NetworkStream, NewNetworkStream,
    Process, Router, Stream, System
  EXPORTS CourierInternal =
  
  BEGIN

  pvLow: CourierProtocol.Protocol = CourierProtocol.pvHigh;
  pvHigh: CourierProtocol.Protocol = CourierProtocol.pvHigh;
  
  z: UNCOUNTED ZONE = CourierInternal.longZone;
  bpw: CARDINAL = Environment.bytesPerWord;
  negotiating: BOOLEAN = System.switches[CommSwitches.sppNegotiation] = down;
  
  xlateFailure: ARRAY NetworkStream.FailureReason OF Courier.ErrorCode = [
    timeout: remoteSystemElementNotResponding,
    noRouteToDestination: noRouteToSystemElement,
    noServiceAtDestination: noCourierAtRemoteSite,
    noAnswerOrBusy: noAnswerOrBusy,
    noTranslationForDestination: noRouteToSystemElement,
    circuitInUse: transmissionMediumUnavailable,
    circuitNotReady: transmissionMediumNotReady,
    noDialingHardware: transmissionMediumUnavailable,
    dialerHardwareProblem: transmissionMediumHardwareProblem,
    remoteReject: tooManyConnections,
    tooManyConnections: tooManyConnections];
    
  xlateSuspend: ARRAY NetworkStream.SuspendReason OF Courier.ErrorCode = [
    notSuspended: noError,
    transmissionTimeout: transportTimeout,
    remoteServiceDisappeared: transportTimeout,
    noRouteToDestination: noRouteToSystemElement];

  BuildCourierTransport: PROC[sH: Stream.Handle]
    RETURNS[aH: CourierInternal.AugmentedStream] =
    BEGIN

    IF CourierInternal.doStats THEN CourierInternal.stats[streamsCreated] ←
      CourierInternal.stats[streamsCreated] + 1;

    --Build the stream for Courier to use with our own filters.
    aH ← z.NEW[CourierInternal.AugmentedObj[SIZE[Stream.Handle]]];
    aH.object ← sH↑;            --Copy for the filtered stream
    aH.object.get ← Get;            --and put in the filters.
    aH.object.put ← Put;
    aH.object.setSST ← SetSST;
    aH.object.sendNow ← SendNow;
    aH.object.delete ← DeleteStream;
    aH.object.waitAttention ← WaitAttn;
    aH.object.sendAttention ← SendAttn;
    aH.object.getByte ← Stream.defaultObject.getByte;
    aH.object.putByte ← Stream.defaultObject.putByte;
    aH.object.getWord ← Stream.defaultObject.getWord;
    aH.object.putWord ← Stream.defaultObject.putWord;
    LOOPHOLE[@aH.context, LONG POINTER TO Stream.Handle]↑ ← sH;
    END;  --BuildCourierTransport  
    
  DeleteStream: Stream.DeleteProcedure =
    BEGIN  --The delete proc in the stream object visable to courier.
    --Access to the direct network stream.
    aH: CourierInternal.AugmentedStream ← LOOPHOLE[sH];
    ch: CourierInternal.UserConnection ← LOOPHOLE[aH.back];
    sH ← LOOPHOLE[@aH.context, LONG POINTER TO Stream.Handle]↑; 
    --Is the other side smart enough for the close protocol?
    IF ch.protocolRange.high > protocol2 THEN
      BEGIN
      sst: Stream.SubSequenceType;
      sst ← sH.get[sH, Environment.nullBlock, sH.options].sst;
      sH.setTimeout[sH, WaitTime[ch.object.remote.net, 10000]];
      IF sst # NetworkStream.closeSST THEN [] ← NetworkStream.Close[sH]
      ELSE [] ← NetworkStream.CloseReply[sH];
      END;
    sH.delete[sH];
    z.FREE[@aH];
    END;  --DeleteStream

  CreateDefaultStream: PUBLIC CourierInternal.Creator =
    BEGIN
    <<
    CourierInternal.Creator: TYPE = PROC[ch: CourierInternal.ConnectionHandle]
      RETURNS[
        aH: CourierInternal.AugmentedStream,
	protocolRange: CourierProtocol.ProtocolRange,
	connectionless: BOOLEAN];
    Creates a user side transport (NetworkStream), arbitrates protocol
    versions.
    >>
    sH: Stream.Handle ← NIL;
    error: Courier.ErrorCode;

    BEGIN
    ENABLE NetworkStream.ConnectionFailed =>
      BEGIN
      CourierInternal.NewStreamFailed[];  --Decrements stream count.
      error ← xlateFailure[why]; GOTO error;  --translate reason and quit
      END;
    wt: NetworkStream.WaitTime = WaitTime[
      ch.object.remote.net, CourierInternal.createTimeout];
    IF ~CourierInternal.CreateNewStream[] THEN  --are we permitted another?
      {error ← tooManyConnections; GOTO error};  --evidently not
    sH ← IF ~negotiating THEN NetworkStream.Create[
      remote: ch.object.remote, classOfService: bulk, timeout: wt]
    ELSE NewNetworkStream.NewCreate[remote: ch.object.remote, timeout: wt];
    EXITS error => {IF sH # NIL THEN sH.delete[sH]; ERROR Courier.Error[error]};
    END;

    --build the returned paramters
    aH ← BuildCourierTransport[sH]; aH.back ← ch;
    RETURN[aH, [pvLow, pvHigh], FALSE];

    END;  --CreateDefaultStream
    
  
  DefaultListener: PUBLIC CourierInternal.Listener =
    BEGIN
    <<
    SERVER
    Listens on a particular socket for requests.  Also arbitrates
    protocol versions.  When request arrives, creates the stream and
    hands it to Courier.
    Set here in this detached process waiting for somebody (hopefully the
    system element wanting to Call) to initiate a connection.  When that 
    happens, spawn another process to service the request.
    >>
    
    sH: Stream.Handle;
    remote: System.NetworkAddress;
    aH: CourierInternal.AugmentedStream;
    listenerHandle: NetworkStream.ListenerHandle;
    listenerHandle ← NetworkStream.CreateListener[Courier.LocalSystemElement[]];
    DO
      remote ← IF ~negotiating THEN
        NetworkStream.Listen[listenerH: listenerHandle].remote
      ELSE NewNetworkStream.NewListen[listenerH: listenerHandle].remote;
      --May fail due to too many connections
      SELECT TRUE FROM
        (CourierInternal.CreateNewStream[]) =>  --are we allowed?
	  BEGIN
	  ENABLE NetworkStream.ConnectionFailed =>
	    {CourierInternal.NewStreamFailed[]; CONTINUE}; 
	  sH ← IF ~negotiating THEN
	    NetworkStream.ApproveConnection[
	      listenerH: listenerHandle,
	      streamTimeout: CourierInternal.activeTimeout,
	      classOfService: bulk]
	  ELSE NewNetworkStream.NewApprove[
	    listenerH: listenerHandle,
	      streamTimeout: CourierInternal.activeTimeout];
	  remote.socket ← NSConstants.courierSocket;
	  aH ← BuildCourierTransport[sH];  --get things set up
	  Process.Detach[FORK CourierInternal.Receiver[
	    aH, remote, [pvLow, pvHigh], FALSE]];
	  END;
	(negotiating) =>
	  NewNetworkStream.NewReject[listenerH: listenerHandle];
	ENDCASE;
      ENDLOOP;
    END;  --DefaultListener
    
     
  Get: Stream.GetProcedure =
    BEGIN
    <<
    The Get procedure we give to Courier.  Calls the direct network
    stream's get procedure, catches transport-specific	errors, and 
    translates them into the appropriate Courier.Error.
    >>
    error: Courier.ErrorCode ← noError;
    sH ← TransportFromFilter[sH];
    {[bytesTransferred, why, sst] ← sH.get[sH, block, options !
      NetworkStream.ConnectionSuspended =>
        {error ← xlateSuspend[why]; GOTO transportError}];
    EXITS transportError => Courier.Error[error]};
    END;  --Get
    
    
  Put: Stream.PutProcedure =
    BEGIN
    <<
    The Put procedure we give to Courier.  Calls the direct network
    stream's put procedure, catches transport-specific errors, and 
    translates them into the appropriate Courier.Error.
    >>
    error: Courier.ErrorCode ← noError;
    sH ← TransportFromFilter[sH];  --get to transport's stream
    {sH.put[sH, block, endRecord !
      NetworkStream.ConnectionSuspended =>
        {error ← xlateSuspend[why]; GOTO transportError}];
    EXITS transportError => Courier.Error[error]};
    END;  --Put
    

  SendNow: Stream.SendNowProcedure =
    BEGIN
    <<
    The sendNow procedure we give to Courier.  Calls the direct network
    stream's sendNow procedure, catches transport-specific errors, and
    translates them into the appropriate Courier.Error.
    >>
    error: Courier.ErrorCode ← noError;
    sH ← TransportFromFilter[sH];  --get to transport's stream
    {sH.sendNow[sH, endRecord !
      NetworkStream.ConnectionSuspended =>
        {error ← xlateSuspend[why]; GOTO transportError}];
    EXITS transportError => Courier.Error[error]};
    END;  --SendNow

  SetSST: Stream.SetSSTProcedure =
    BEGIN
    error: Courier.ErrorCode ← noError;
    sH ← TransportFromFilter[sH];  --get to transport's stream
    {sH.setSST[sH, sst !
      NetworkStream.ConnectionSuspended =>
        {error ← xlateSuspend[why]; GOTO transportError}];
    EXITS transportError => Courier.Error[error]};
    END;  --SetSST    
    
  WaitAttn: Stream.WaitAttentionProcedure =
    BEGIN
    <<
    The waitForAttention procedure we give to Courier.  Calls the direct
    network stream's waitForAttention procedure, catches
    transport-specific errors, and translates them into the appropriate
    Courier.Error.
    >>
    error: Courier.ErrorCode ← noError;
    sH ← TransportFromFilter[sH];  --get to transport's stream
    {RETURN[sH.waitAttention[sH !
      NetworkStream.ConnectionSuspended =>
        {error ← xlateSuspend[why]; GOTO transportError}]];
    EXITS transportError => Courier.Error[error]};
    END;  --WaitAttn
    
    
  SendAttn: Stream.SendAttentionProcedure =
    BEGIN
    <<
    The sendAttention procedure we give to Courier.  Calls the direct
    network stream's sendAttention procedure, catches transport-specific
    errors and translates them into the appropriate Courier.Error.
    >>
    error: Courier.ErrorCode ← noError;
    sH ← TransportFromFilter[sH];  --get to transport's stream
    {sH.sendAttention[sH, byte !
      NetworkStream.ConnectionSuspended =>
        {error ← xlateSuspend[why]; GOTO transportError}];
    EXITS transportError => Courier.Error[error]};
    END;  --SendAttn

  TransportFromFilter: PROC[sH: Stream.Handle] RETURNS[Stream.Handle] = INLINE
    {RETURN[LOOPHOLE[@LOOPHOLE[sH, CourierInternal.AugmentedStream].context,
      LONG POINTER TO Stream.Handle]↑]};

  WaitTime: PROC[net: System.NetworkNumber, base: NetworkStream.WaitTime]
    RETURNS[tmo: NetworkStream.WaitTime] =
    BEGIN
    ENABLE Router.NoTableEntryForNet =>
      {tmo ← LONG[CourierInternal.hopWeight * 10000]; CONTINUE};
    tmo ← base + (CourierInternal.hopWeight * Router.GetDelayToNet[net] * 1000);
    END;  --WaitTime    

    
  END...
  
LOG

25-Jun-84 12:31:44  SMA  Created file.  
20-Jul-84 10:41:07  SMA  Catch SpecialCourier.Closed.
21-Dec-84 13:07:24  SMA  AugmentedStreams and new interfaces.
27-Dec-84 11:46:19  AOF  FORK Receiver with AugmentedStream, not nsH.
 7-Jan-85  8:54:39  AOF  Moved Closed back to CourierImplC.
 7-Jan-85 10:17:07  SMA  Take out TransportHandle.
 7-Jan-85 17:05:38  SMA  No translations to callerAborted.
 5-Mar-85 19:18:59  AOF  Don't stomp on CourierInternal.createTimeout.
 9-Apr-85 15:41:59  AOF  DeleteStream looking for closeSST already received.
24-May-85 14:32:01  AOF  Resetting stream timeout before close.
16-Jan-86 13:17:04  AOF  cleanup LOOPHOLEs.
 3-Mar-86 15:43:50  AOF  Correct getting input SST in Delete code.
15-Apr-86 10:44:31  AOF  Computation of wait time (missing * 1000).
16-Jan-87  9:47:06  AOF  Removal of Courier Version 2.0
14-Apr-87 12:34:46  AOF  Catch ConnectionFailed in listener
 9-Nov-87 18:27:35  AOF  Add access to Spp Negotiation