ArpaTCPTest.mesa
Copyright (C) 1983, 1984 by Xerox Corporation. All rights reserved. The following program was created in 1983 but has not been published within the meaning of the copyright law, is furnished under license, and may not be used, copied and/or disclosed except in accordance with the terms of said license.
Last Edited by: Nichols, September 1, 1983 4:43 pm
Last Edited by: Taft, January 22, 1984 11:28:59 am PST
Last Edited by: HGM, October 6, 1984 10:51:08 pm PDT
Doug Terry, November 20, 1987 12:50:18 pm PST
Hal Murray May 16, 1985 3:16:56 am PDT
John Larson, April 14, 1986 11:34:42 pm PST
DIRECTORY
Arpa USING [Address],
ArpaExtras USING [MyAddress],
Commander USING [CommandProc, Register],
IO USING [Close, EndOf, EndOfStream, Error, Flush, GetChar, GetLineRope, int, PutBlock, PutChar, PutF, PutFR, PutRope, rope, STREAM],
Process USING [Milliseconds, PauseMsec],
Rope USING [Cat, Find, FromChar, Length, ROPE],
UserCredentials USING [Get],
ArpaTCP USING [AbortTCPStream, CreateTCPStream, Error, ErrorFromStream, neverTimeout, Reason, Timeout, WaitForListenerOpen];
ArpaTCPTest:
CEDAR
PROGRAM
IMPORTS ArpaExtras, Commander, IO, Process, Rope, UserCredentials, ArpaTCP =
BEGIN
fingerSocket: INT = 79;
sinkSocket: INT = 531;
me: Arpa.Address ← ArpaExtras.MyAddress[];
cslSun: Arpa.Address ← [13, 1, 100, 206];
parcvax: Arpa.Address ← [13, 2, 16, 8];
remoteAddress: Arpa.Address ← me;
used for tests involving blocks of data being sent:
totalsize: INT ← 100000;
blocksize: INT ← 8192;
running: BOOL ← TRUE;
server: PROCESS;
SetAddress: Commander.CommandProc = {
out: IO.STREAM ← cmd.out;
argv: CommandTool.ArgumentVector = CommandTool.Parse[cmd !
CommandTool.Failed => {out.PutRope[errorMsg]; GO TO GiveUp}];
remoteAddresses: LIST OF Arpa.Address ← IPName.NameToAddress[argv[1]];
IF remoteAddresses=NIL THEN {out.PutRope["Name not found"]; GOTO GiveUp};
remoteAddress ← remoteAddresses.first;
out.PutF["[%g.%g.%g.%g]\n", IO.int[remoteAddress[0]], IO.int[remoteAddress[1]], IO.int[remoteAddress[2]], IO.int[remoteAddress[3]]];
};
Finger: Commander.CommandProc = {
out: IO.STREAM ← cmd.out;
fingerStream: IO.STREAM;
c: CHAR;
cr: ArpaTCP.Reason ← neverOpen;
{
ENABLE {
ArpaTCP.Timeout => {
out.PutRope["[No response.]\n"];
GO TO GiveUp;
};
ArpaTCP.Error => {
cr ← reason;
GO TO GiveUp;
};
IO.Error => {
cr ← ArpaTCP.ErrorFromStream[fingerStream];
GOTO GiveUp;
};
};
fingerStream ← ArpaTCP.CreateTCPStream[[matchLocalPort~FALSE, localPort~0, matchForeignAddr~TRUE, foreignAddress~remoteAddress, matchForeignPort~TRUE, foreignPort~fingerSocket, active~TRUE, timeout~120000]];
ArpaTCP.WaitForListenerOpen[fingerStream, ArpaTCP.neverTimeout];
fingerStream.PutRope[cmd.commandLine];
fingerStream.PutChar['\l]; -- send the LF as well
fingerStream.Flush[];
WHILE
NOT fingerStream.EndOf[]
DO
c ← fingerStream.GetChar[ ! IO.EndOfStream => EXIT];
IF c # '\l THEN out.PutChar[c];
ENDLOOP;
fingerStream.Close[];
EXITS
GiveUp =>
SELECT cr
FROM
remoteAbort => out.PutRope["[Remote host refused to point the finger at its users.]\n"];
ENDCASE => ArpaTCP.AbortTCPStream[fingerStream];
};
};
StartFingerServer: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
running ← TRUE;
server ← FORK FingerServer[];
msg ← "TCP finger server started.";
};
FingerServer:
PROC = {
ENABLE ArpaTCP.Timeout => RESUME;
fingerStream: IO.STREAM;
WHILE running
DO
ENABLE
BEGIN
IO.Error => RETRY;
ArpaTCP.Timeout => RETRY;
ArpaTCP.Error => RETRY;
END;
char: CHAR;
rope: Rope.ROPE ← NIL;
fingerStream ← ArpaTCP.CreateTCPStream[[matchLocalPort: TRUE, localPort: fingerSocket, matchForeignAddr: FALSE, matchForeignPort: FALSE, active: FALSE]];
DO
char ← fingerStream.GetChar[];
IF char = '\l THEN EXIT;
rope ← Rope.Cat[rope, Rope.FromChar[char]];
ENDLOOP;
fingerStream.PutF["User %g logged in on a Dorado.\n\l", IO.rope[UserCredentials.Get[].name]];
IF Rope.Find[rope, "Verbose"] # -1
THEN
Hack for resting reassembly via echoing to GOONHILLY-ECHO. Send big packets.
THROUGH [0..20)
DO
fingerStream.PutF[" 123456789 123456789 123456789 123456789 123456789\n\l"];
ENDLOOP;
IF Rope.Find[rope, "Super"] # -1
THEN
THROUGH [0..200)
DO
fingerStream.PutF[" 123456789 123456789 123456789 123456789 123456789\n\l"];
ENDLOOP;
fingerStream.Flush[];
fingerStream.Close[];
ENDLOOP;
};
Adapted from Mark Weiser:
CharTest: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
IO.PutF[cmd.out, "Starting to send data in character mode.\n"];
msg ← SendData[blockmode: FALSE];
};
BlockTest: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
IO.PutF[cmd.out, "Starting to send data in block mode.\n"];
msg ← SendData[blockmode: TRUE];
};
SendData:
PROC[blockmode:
BOOLEAN ←
TRUE, waitForResult:
BOOLEAN ←
FALSE]
RETURNS [result: Rope.
ROPE ←
NIL] = {
stillToDo: INT;
tcpStream: IO.STREAM;
headerForm: Rope.ROPE = "t%09g ";
header: Rope.ROPE;
block: REF TEXT ← NEW[TEXT[blocksize]];
block.length ← blocksize;
tcpStream ← ArpaTCP.CreateTCPStream[[foreignAddress~remoteAddress, foreignPort~sinkSocket, active~TRUE, timeout~200000]];
header ← IO.PutFR[headerForm, IO.int[0]];
header ← IO.PutFR[headerForm, IO.int[totalsize + Rope.Length[header]]];
{ -- nested block to catch errors
ENABLE {
ArpaTCP.Timeout => {result ← "ArpaTCP.Timeout on TCP stream"; GOTO GiveUp};
ArpaTCP.Error => {result ← "ArpaTCP.Error on TCP stream"; GOTO GiveUp};
IO.Error => {result ← "IO.Error on TCP stream"; GOTO GiveUp};
};
IO.PutF[tcpStream, header];
IO.Flush[tcpStream];
stillToDo ← totalsize;
FOR left:
INT ← totalsize-blocksize, left - blocksize
UNTIL left < 0
DO
IF blockmode
THEN IO.PutBlock[tcpStream, block, 0, blocksize]
ELSE
FOR count:
INT
IN [0..blocksize)
DO
IO.PutChar[tcpStream, 'w];
ENDLOOP;
IO.Flush[tcpStream];
stillToDo ← left;
ENDLOOP;
IF blockmode
THEN IO.PutBlock[tcpStream, block, 0, stillToDo]
ELSE
FOR count:
INT
IN [0..stillToDo)
DO
IO.PutChar[tcpStream, 'w];
ENDLOOP;
IO.Flush[tcpStream];
IF waitForResult
THEN
result ← IO.GetLineRope[tcpStream ! ArpaTCP.Timeout => CONTINUE];
IO.Close[tcpStream];
EXITS
GiveUp => ArpaTCP.AbortTCPStream[tcpStream];
};
};
Sink: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
server ← FORK SinkServer[];
msg ← "TCP sink server started.";
};
the following can be adjusted to slow down the sink, perhaps to simulate a slow receiver or to test what happens when the senders window goes to zero
charsBetweenPauses: INT ← 500;
msToPause: Process.Milliseconds ← 0;
SinkServer:
PROC = {
ENABLE ArpaTCP.Timeout => RESUME;
sinkStream: IO.STREAM;
charsSincePause: INT ← 0;
DO
-- forever
sinkStream ← ArpaTCP.CreateTCPStream[[matchLocalPort: TRUE, localPort: sinkSocket, matchForeignAddr: FALSE, matchForeignPort: FALSE, active: FALSE]];
DO
ENABLE
BEGIN
IO.Error => EXIT;
IO.EndOfStream => EXIT;
ArpaTCP.Timeout => EXIT;
ArpaTCP.Error => EXIT;
END;
char: CHAR ← sinkStream.GetChar[];
charsSincePause ← charsSincePause + 1;
IF charsSincePause > charsBetweenPauses
THEN {
IF msToPause#0 THEN Process.PauseMsec[msToPause];
charsSincePause ← 0;
};
ENDLOOP;
sinkStream.Close[ ! IO.Error => CONTINUE];
ENDLOOP;
};
Commander.Register["SetTCPAddress", SetAddress, "Tell which remote host to use."];
Commander.Register["TCPFinger", Finger, "Who is on host."];
Commander.Register["TCPFingerServer", StartFingerServer, "Start TCP finger server on this host."];
Commander.Register["TCPBlockSend", BlockTest, "Tests the 'ArpaTCP' interface."];
Commander.Register["TCPCharSend", CharTest, "Tests the 'ArpaTCP' interface."];
Commander.Register["TCPSink", Sink, "Start TCP server that eats whatever data is sent to it."];
END.