source upload

This commit is contained in:
Razor12911
2022-01-17 22:16:47 +02:00
parent 12936d065b
commit 098e8c48de
1778 changed files with 1206749 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
{$INCLUDE ../../flcInclude.inc}
{$INCLUDE ../flcTCP.inc}
{$IFDEF DEBUG}
{$IFDEF TEST}
{$DEFINE TCP_TEST}
{$DEFINE TCP_TEST_LOG_TO_CONSOLE}
{$ENDIF}
{$ENDIF}
{$IFDEF TCP_TEST}
{$DEFINE TCPCLIENT_TEST}
{$DEFINE TCPSERVER_TEST}
{$DEFINE TCPCLIENTSERVER_TEST}
{$IFDEF TCPCLIENT_TLS}
{$DEFINE TCPCLIENT_TEST_TLS}
{$ENDIF}
{$IFDEF TCPSERVER_TLS}
{$DEFINE TCPSERVER_TEST_TLS}
{$ENDIF}
{$IFDEF TCPCLIENT_TLS}
{$IFDEF TCPSERVER_TLS}
{$DEFINE TCPCLIENTSERVER_TEST_TLS}
{$ENDIF}
{$ENDIF}
{$ENDIF}

View File

@@ -0,0 +1,94 @@
{ 2020/05/11 5.01 Move tests from unit flcTests into seperate units. }
{$INCLUDE flcTCPTest.inc}
unit flcTCPTest_Buffer;
interface
{ }
{ Test }
{ }
{$IFDEF TCP_TEST}
procedure Test;
{$ENDIF}
implementation
{$IFDEF TCP_TEST}
uses
flcStdTypes,
flcTCPBuffer;
{$ENDIF}
{ }
{ Test }
{ }
{$IFDEF TCP_TEST}
{$ASSERTIONS ON}
procedure Test_Buffer;
var A : TTCPBuffer;
B : Byte;
S : RawByteString;
I, L : Integer;
begin
TCPBufferInitialise(A, 1500, 1000);
Assert(TCPBufferUsed(A) = 0);
Assert(TCPBufferAvailable(A) = 1500);
TCPBufferSetMaxSize(A, 2000);
Assert(TCPBufferAvailable(A) = 2000);
S := 'Fundamentals';
L := Length(S);
TCPBufferAddBuf(A, S[1], L);
Assert(TCPBufferUsed(A) = L);
Assert(not TCPBufferEmpty(A));
S := '';
for I := 1 to L do
S := S + 'X';
Assert(S = 'XXXXXXXXXXXX');
TCPBufferPeek(A, S[1], 3);
Assert(S = 'FunXXXXXXXXX');
Assert(TCPBufferPeekByte(A, B));
Assert(B = Ord('F'));
S := '';
for I := 1 to L do
S := S + #0;
TCPBufferRemove(A, S[1], L);
Assert(S = 'Fundamentals');
Assert(TCPBufferUsed(A) = 0);
S := 'X';
for I := 1 to 2001 do
begin
S[1] := ByteChar(I mod 256);
TCPBufferAddBuf(A, S[1], 1);
Assert(TCPBufferUsed(A) = I);
Assert(TCPBufferAvailable(A) = 2000 - I);
end;
for I := 1 to 2001 do
begin
S[1] := 'X';
TCPBufferRemove(A, S[1], 1);
Assert(S[1] = ByteChar(I mod 256));
Assert(TCPBufferUsed(A) = 2001 - I);
end;
Assert(TCPBufferEmpty(A));
TCPBufferShrink(A);
Assert(TCPBufferEmpty(A));
TCPBufferFinalise(A);
end;
procedure Test;
begin
Test_Buffer;
end;
{$ENDIF}
end.

View File

@@ -0,0 +1,182 @@
{ 2020/05/11 5.01 Move tests from unit flcTests into seperate units. }
{$INCLUDE flcTCPTest.inc}
{$IFDEF TCPCLIENT_TEST}
{$DEFINE TCPCLIENT_TEST_WEB}
{$ENDIF}
unit flcTCPTest_Client;
interface
{$IFDEF TCPCLIENT_TEST}
uses
SysUtils,
SyncObjs,
flcStdTypes,
flcTCPClient;
{$ENDIF}
{$IFDEF TCPCLIENT_TEST}
{ }
{ TCP Client Test Object }
{ }
type
TTCPClientTestObj = class
States : array[TTCPClientState] of Boolean;
Connect : Boolean;
LogMsg : String;
Lock : TCriticalSection;
constructor Create;
destructor Destroy; override;
procedure ClientLog(Sender: TF5TCPClient; LogType: TTCPClientLogType; LogMsg: String; LogLevel: Integer);
procedure ClientConnect(Client: TF5TCPClient);
procedure ClientStateChanged(Client: TF5TCPClient; State: TTCPClientState);
end;
{ }
{ Test }
{ }
procedure Test;
{$ENDIF}
implementation
{$IFDEF TCPCLIENT_TEST}
uses
flcTCPConnection;
{$ENDIF}
{$IFDEF TCPCLIENT_TEST}
{$ASSERTIONS ON}
{ }
{ TCP Client Test Object }
{ }
constructor TTCPClientTestObj.Create;
begin
inherited Create;
Lock := TCriticalSection.Create;
end;
destructor TTCPClientTestObj.Destroy;
begin
FreeAndNil(Lock);
inherited Destroy;
end;
procedure TTCPClientTestObj.ClientLog(Sender: TF5TCPClient; LogType: TTCPClientLogType; LogMsg: String; LogLevel: Integer);
begin
{$IFDEF TCP_TEST_LOG_TO_CONSOLE}
Lock.Acquire;
try
Writeln(LogLevel:2, ' ', LogMsg);
finally
Lock.Release;
end;
{$ENDIF}
end;
procedure TTCPClientTestObj.ClientConnect(Client: TF5TCPClient);
begin
Connect := True;
end;
procedure TTCPClientTestObj.ClientStateChanged(Client: TF5TCPClient; State: TTCPClientState);
begin
States[State] := True;
end;
{ }
{ Test }
{ }
{$IFDEF TCPCLIENT_TEST_WEB}
procedure Test_Client_Web;
var
C : TF5TCPClient;
S : RawByteString;
A : TTCPClientTestObj;
begin
A := TTCPClientTestObj.Create;
C := TF5TCPClient.Create(nil);
try
// init
C.OnLog := A.ClientLog;
C.LocalHost := '0.0.0.0';
C.Host := 'www.google.com';
C.Port := '80';
C.OnStateChanged := A.ClientStateChanged;
C.OnConnected := A.ClientConnect;
C.WaitForStartup := True;
Assert(not C.Active);
Assert(C.State = csInit);
Assert(C.IsConnectionClosed);
Assert(not A.Connect);
// start
C.Active := True;
Assert(C.Active);
Assert(C.State <> csInit);
Assert(A.States[csStarting]);
Assert(C.IsConnectingOrConnected);
Assert(not C.IsConnectionClosed);
// wait connect
C.WaitForConnect(8000);
Assert(C.IsConnected);
Assert(C.State = csReady);
Assert(C.Connection.State = cnsConnected);
Assert(A.Connect);
Assert(A.States[csConnecting]);
Assert(A.States[csConnected]);
Assert(A.States[csReady]);
// send request
C.Connection.WriteByteString(
'GET / HTTP/1.1'#13#10 +
'Host: www.google.com'#13#10 +
'Date: 7 Nov 2013 12:34:56 GMT'#13#10 +
#13#10);
// wait response
C.BlockingConnection.WaitForReceiveData(1, 5000);
// read response
S := C.Connection.ReadByteString(C.Connection.ReadBufferUsed);
Assert(S <> '');
// close
C.Connection.Close;
C.WaitForClose(2000);
Assert(not C.IsConnected);
Assert(C.IsConnectionClosed);
Assert(C.Connection.State = cnsClosed);
// stop
C.Active := False;
Assert(not C.Active);
Assert(C.IsConnectionClosed);
finally
C.Finalise;
FreeAndNil(C);
A.Free;
end;
end;
{$ENDIF}
procedure Test;
begin
{$IFDEF TCPCLIENT_TEST_WEB}
Test_Client_Web;
{$ENDIF}
end;
{$ENDIF}
end.

View File

@@ -0,0 +1,863 @@
{ 2020/05/11 5.01 Move tests from unit flcTests into seperate units. }
{$INCLUDE flcTCPTest.inc}
unit flcTCPTest_ClientServer;
interface
{$IFDEF TCPCLIENTSERVER_TEST}
uses
{$IFDEF OS_MSWIN}
Windows,
{$ENDIF}
SysUtils,
SyncObjs,
Classes,
flcStdTypes,
flcTCPConnection,
flcTCPClient,
flcTCPServer;
{$ENDIF}
{$IFDEF TCPCLIENTSERVER_TEST}
{ }
{ TCP ClientServer Test Object }
{ }
type
TTCPClientServerTestObj = class
Lock : TCriticalSection;
constructor Create;
destructor Destroy; override;
procedure Log(Msg: String);
procedure ClientLog(Client: TF5TCPClient; LogType: TTCPClientLogType; Msg: String; LogLevel: Integer);
procedure ServerLog(Sender: TF5TCPServer; LogType: TTCPLogType; Msg: String; LogLevel: Integer);
end;
TTCPClientServerBlockTestObj = class
FinC, FinS : Boolean;
procedure ClientExec(Client: TF5TCPClient; Connection: TTCPBlockingConnection; var CloseOnExit: Boolean);
procedure ServerExec(Sender: TTCPServerClient; Connection: TTCPBlockingConnection; var CloseOnExit: Boolean);
end;
{ }
{ Test functions }
{ }
type
TF5TCPClientArray = array of TF5TCPClient;
TTCPServerClientArray = array of TTCPServerClient;
procedure TestClientServer_ReadWrite_Blocks(
const TestClientCount: Integer;
const TestLargeBlock: Boolean;
const DebugObj : TTCPClientServerTestObj;
const C: TF5TCPClientArray;
const T: TTCPServerClientArray);
{ }
{ Test }
{ }
procedure Test;
{$ENDIF}
implementation
{$IFDEF TCPCLIENTSERVER_TEST}
uses
flcSocketLib,
flcTimers;
{$ENDIF}
{$IFDEF TCPCLIENTSERVER_TEST}
{$ASSERTIONS ON}
{ }
{ TCP ClientServer Test Object }
{ }
{ TTCPClientServerTestObj }
constructor TTCPClientServerTestObj.Create;
begin
inherited Create;
Lock := TCriticalSection.Create;
end;
destructor TTCPClientServerTestObj.Destroy;
begin
FreeAndNil(Lock);
inherited Destroy;
end;
{$IFDEF TCP_TEST_LOG_TO_CONSOLE}
procedure TTCPClientServerTestObj.Log(Msg: String);
var S : String;
begin
S := FormatDateTime('hh:nn:ss.zzz', Now) + ' ' + Msg;
Lock.Acquire;
try
Writeln(S);
finally
Lock.Release;
end;
end;
{$ELSE}
procedure TTCPClientServerTestObj.Log(Msg: String);
begin
end;
{$ENDIF}
procedure TTCPClientServerTestObj.ClientLog(Client: TF5TCPClient; LogType: TTCPClientLogType; Msg: String; LogLevel: Integer);
begin
Log('C[' + IntToStr(Client.Tag) + ']:' + Msg);
end;
procedure TTCPClientServerTestObj.ServerLog(Sender: TF5TCPServer; LogType: TTCPLogType; Msg: String; LogLevel: Integer);
begin
Log('S:' + Msg);
end;
{ TTCPClientServerBlockTestObj }
procedure TTCPClientServerBlockTestObj.ClientExec(Client: TF5TCPClient;Connection: TTCPBlockingConnection; var CloseOnExit: Boolean);
var
B1 : LongWord;
begin
Sleep(200);
B1 := 1234;
Connection.Write(B1, 4, 10000);
B1 := 0;
Connection.Read(B1, 4, 10000);
Assert(B1 = 22222);
FinC := True;
end;
procedure TTCPClientServerBlockTestObj.ServerExec(Sender: TTCPServerClient; Connection: TTCPBlockingConnection; var CloseOnExit: Boolean);
var
B1 : LongWord;
begin
B1 := 0;
Connection.Read(B1, 4, 10000);
Assert(B1 = 1234);
B1 := 22222;
Connection.Write(B1, 4, 10000);
FinS := True;
end;
{ }
{ Test }
{ }
procedure TestClientServer_ReadWrite_Blocks(
const TestClientCount: Integer;
const TestLargeBlock: Boolean;
const DebugObj : TTCPClientServerTestObj;
const C: TF5TCPClientArray;
const T: TTCPServerClientArray);
const
LargeBlockSize = 256 * 1024;
var
K, I, J : Integer;
F : RawByteString;
B : Byte;
begin
// read & write (small block): client to server
for K := 0 to TestClientCount - 1 do
C[K].Connection.WriteByteString('Fundamentals');
for K := 0 to TestClientCount - 1 do
begin
DebugObj.Log('{RWS:' + IntToStr(K + 1) + ':A}');
I := 0;
repeat
Inc(I);
Sleep(1);
until (T[K].Connection.ReadBufferUsed >= 12) or (I >= 5000);
DebugObj.Log('{RWS:' + IntToStr(K + 1) + ':B}');
Assert(T[K].Connection.ReadBufferUsed = 12);
F := T[K].Connection.PeekByteString(3);
Assert(F = 'Fun');
Assert(T[K].Connection.PeekByte(B));
Assert(B = Ord('F'));
F := T[K].Connection.ReadByteString(12);
Assert(F = 'Fundamentals');
DebugObj.Log('{RWS:' + IntToStr(K + 1) + ':Z}');
end;
// read & write (small block): server to client
for K := 0 to TestClientCount - 1 do
T[K].Connection.WriteByteString('123');
for K := 0 to TestClientCount - 1 do
begin
C[K].BlockingConnection.WaitForReceiveData(3, 5000);
F := C[K].Connection.ReadByteString(3);
Assert(F = '123');
end;
if TestLargeBlock then
begin
// read & write (large block): client to server
F := '';
for I := 1 to LargeBlockSize do
F := F + #1;
for K := 0 to TestClientCount - 1 do
C[K].Connection.WriteByteString(F);
for K := 0 to TestClientCount - 1 do
begin
J := LargeBlockSize;
repeat
I := 0;
repeat
Inc(I);
Sleep(1);
Assert(C[K].State = csReady);
Assert(T[K].State = scsReady);
until (T[K].Connection.ReadBufferUsed > 0) or (I >= 5000);
Assert(T[K].Connection.ReadBufferUsed > 0);
F := T[K].Connection.ReadByteString(T[K].Connection.ReadBufferUsed);
Assert(Length(F) > 0);
for I := 1 to Length(F) do
Assert(F[I] = #1);
Dec(J, Length(F));
until J <= 0;
Assert(J = 0);
Sleep(2);
Assert(T[K].Connection.ReadBufferUsed = 0);
end;
// read & write (large block): server to client
F := '';
for I := 1 to LargeBlockSize do
F := F + #1;
for K := 0 to TestClientCount - 1 do
T[K].Connection.WriteByteString(F);
for K := 0 to TestClientCount - 1 do
begin
J := LargeBlockSize;
repeat
C[K].BlockingConnection.WaitForReceiveData(1, 5000);
Assert(C[K].State = csReady);
Assert(T[K].State = scsReady);
Assert(C[K].Connection.ReadBufferUsed > 0);
F := C[K].Connection.ReadByteString(C[K].Connection.ReadBufferUsed);
Assert(Length(F) > 0);
for I := 1 to Length(F) do
Assert(F[I] = #1);
Dec(J, Length(F));
until J <= 0;
Assert(J = 0);
Sleep(2);
Assert(C[K].Connection.ReadBufferUsed = 0);
end;
end;
end;
procedure TestClientServer_ReadWrite(
const TestClientCount: Integer;
const TestLargeBlock: Boolean
);
procedure WaitClientConnected(const Client: TF5TCPClient);
var I : Integer;
begin
I := 0;
repeat
Inc(I);
Sleep(1);
until (I >= 8000) or
(Client.State in [csReady, csClosed]);
Assert(Client.State = csReady);
Assert(Client.Connection.State = cnsConnected);
end;
var C : TF5TCPClientArray;
S : TF5TCPServer;
T : TTCPServerClientArray;
TSC : TTCPServerClient;
I, K : Integer;
DebugObj : TTCPClientServerTestObj;
begin
DebugObj := TTCPClientServerTestObj.Create;
S := TF5TCPServer.Create(nil);
SetLength(C, TestClientCount);
SetLength(T, TestClientCount);
for K := 0 to TestClientCount - 1 do
begin
C[K] := TF5TCPClient.Create(nil);
// init client
C[K].Tag := K + 1;
C[K].OnLog := DebugObj.ClientLog;
end;
try
// init server
S.OnLog := DebugObj.ServerLog;
S.AddressFamily := iaIP4;
S.BindAddress := '127.0.0.1';
S.ServerPort := 12545;
S.MaxClients := -1;
Assert(S.State = ssInit);
Assert(not S.Active);
// start server
S.Start;
Assert(S.Active);
I := 0;
repeat
Inc(I);
Sleep(1);
until (S.State <> ssStarting) or (I >= 5000);
Sleep(100);
Assert(S.State = ssReady);
Assert(S.ClientCount = 0);
for K := 0 to TestClientCount - 1 do
begin
// init client
C[K].AddressFamily := cafIP4;
C[K].Host := '127.0.0.1';
C[K].Port := '12545';
Assert(C[K].State = csInit);
Assert(not C[K].Active);
// start client
C[K].WaitForStartup := True;
C[K].Start;
Assert(Assigned(C[K].Connection));
Assert(C[K].Active);
end;
for K := 0 to TestClientCount - 1 do
// wait for client to connect
WaitClientConnected(C[K]);
// wait for server connections
I := 0;
repeat
Inc(I);
Sleep(1);
until (S.ClientCount >= TestClientCount) or (I >= 5000);
Assert(S.ClientCount = TestClientCount);
// wait for server clients
TSC := S.ClientIterateFirst;
for K := 0 to TestClientCount - 1 do
begin
T[K] := TSC;
Assert(Assigned(T[K]));
Assert(T[K].State in [scsStarting, scsNegotiating, scsReady]);
Assert(T[K].Connection.State in [cnsProxyNegotiation, cnsConnected]);
TSC.ReleaseReference;
TSC := S.ClientIterateNext(TSC);
end;
// test read/write
TestClientServer_ReadWrite_Blocks(
TestClientCount,
TestLargeBlock,
DebugObj,
C, T);
// release reference
for K := 0 to TestClientCount - 1 do
T[K].ReleaseReference;
// stop clients
for K := TestClientCount - 1 downto 0 do
begin
C[K].Stop;
Assert(C[K].State = csStopped);
end;
I := 0;
repeat
Inc(I);
Sleep(1);
until (S.ClientCount = 0) or (I >= 5000);
Assert(S.ClientCount = 0);
// stop server
S.Stop;
Assert(not S.Active);
finally
for K := TestClientCount - 1 downto 0 do
begin
C[K].Finalise;
FreeAndNil(C[K]);
end;
S.Finalise;
FreeAndNil(S);
DebugObj.Free;
end;
end;
procedure Test_ClientServer_StopStart;
const
TestClientCount = 10;
TestRepeatCount = 3;
var C : array of TF5TCPClient;
S : TF5TCPServer;
I, K : Integer;
J : Integer;
DebugObj : TTCPClientServerTestObj;
begin
DebugObj := TTCPClientServerTestObj.Create;
S := TF5TCPServer.Create(nil);
SetLength(C, TestClientCount);
for K := 0 to TestClientCount - 1 do
begin
C[K] := TF5TCPClient.Create(nil);
// init client
C[K].Tag := K + 1;
C[K].OnLog := DebugObj.ClientLog;
end;
try
// init server
S.OnLog := DebugObj.ServerLog;
S.AddressFamily := iaIP4;
S.BindAddress := '127.0.0.1';
S.ServerPort := 12645;
S.MaxClients := -1;
Assert(S.State = ssInit);
Assert(not S.Active);
// start server
S.Start;
Assert(S.Active);
I := 0;
repeat
Inc(I);
Sleep(1);
until (S.State <> ssStarting) or (I >= 5000);
Assert(S.State = ssReady);
Assert(S.ClientCount = 0);
// init clients
for K := 0 to TestClientCount - 1 do
begin
C[K].AddressFamily := cafIP4;
C[K].Host := '127.0.0.1';
C[K].Port := '12645';
C[K].WaitForStartup := True;
Assert(C[K].State = csInit);
Assert(not C[K].Active);
end;
// test quickly starting and stopping clients
for J := 0 to TestRepeatCount - 1 do
begin
// start client
for K := 0 to TestClientCount - 1 do
begin
C[K].Start;
// connection must exist when Start exits
Assert(Assigned(C[K].Connection));
Assert(C[K].Active);
end;
// delay
if J > 0 then
Sleep(J - 1);
// stop clients
for K := TestClientCount - 1 downto 0 do
begin
C[K].Stop;
Assert(C[K].State = csStopped);
Assert(not Assigned(C[K].Connection));
end;
// delay
if J > 0 then
Sleep(J - 1);
// re-start client
for K := 0 to TestClientCount - 1 do
begin
C[K].Start;
// connection must exist when Start exits
Assert(Assigned(C[K].Connection));
Assert(C[K].Active);
end;
// delay
if J > 0 then
Sleep(J - 1);
// re-stop clients
for K := TestClientCount - 1 downto 0 do
begin
C[K].Stop;
Assert(C[K].State = csStopped);
Assert(not Assigned(C[K].Connection));
end;
end;
// stop server
S.Stop;
Assert(not S.Active);
finally
for K := TestClientCount - 1 downto 0 do
begin
C[K].Finalise;
FreeAndNil(C[K]);
end;
S.Finalise;
FreeAndNil(S);
DebugObj.Free;
end;
end;
procedure Test_ClientServer_ReadWrite;
begin
TestClientServer_ReadWrite(1, True);
TestClientServer_ReadWrite(2, True);
TestClientServer_ReadWrite(5, True);
{$IFNDEF LINUX} // FAILS
TestClientServer_ReadWrite(30, False);
{$ENDIF}
end;
procedure Test_ClientServer_Shutdown;
var
S : TF5TCPServer;
C : TF5TCPClient;
T : TTCPServerClient;
B, X : RawByteString;
I, L, N, M : Integer;
begin
S := TF5TCPServer.Create(nil);
S.AddressFamily := iaIP4;
S.BindAddress := '127.0.0.1';
S.ServerPort := 12213;
S.MaxClients := -1;
S.Active := True;
Sleep(100);
C := TF5TCPClient.Create(nil);
C.LocalHost := '0.0.0.0';
C.Host := '127.0.0.1';
C.Port := '12213';
C.WaitForStartup := True;
C.UseWorkerThread := False;
C.Active := True;
Sleep(100);
Assert(C.State = csReady);
SetLength(B, 1024 * 1024);
for I := 1 to Length(B) do
B[I] := #1;
L := C.Connection.WriteByteString(B);
Assert(L > 1024);
Sleep(1);
Assert(not C.IsShutdownComplete);
C.Shutdown;
for I := 1 to 10 do
begin
if C.IsShutdownComplete then
break;
Sleep(50);
end;
Assert(C.IsShutdownComplete);
T := S.ClientIterateFirst;
Assert(Assigned(T));
SetLength(X, L);
M := 0;
repeat
N := T.Connection.Read(X[1], L);
Inc(M, N);
Sleep(10);
until N <= 0;
Assert(M = L);
Sleep(100);
Assert(C.State = csClosed);
Assert(T.State = scsClosed);
T.ReleaseReference;
C.Close;
C.Finalise;
C.Free;
S.Active := False;
S.Finalise;
S.Free;
end;
procedure Test_ClientServer_Block;
var
S : TF5TCPServer;
C : TF5TCPClient;
DebugObj : TTCPClientServerTestObj;
TestObj : TTCPClientServerBlockTestObj;
begin
DebugObj := TTCPClientServerTestObj.Create;
TestObj := TTCPClientServerBlockTestObj.Create;
S := TF5TCPServer.Create(nil);
S.OnLog := DebugObj.ServerLog;
S.AddressFamily := iaIP4;
S.BindAddress := '127.0.0.1';
S.ServerPort := 12145;
S.MaxClients := -1;
S.UseWorkerThread := True;
S.OnClientWorkerExecute := TestObj.ServerExec;
S.Active := True;
Sleep(50);
C := TF5TCPClient.Create(nil);
C.OnLog := DebugObj.ClientLog;
C.LocalHost := '0.0.0.0';
C.Host := '127.0.0.1';
C.Port := '12145';
C.WaitForStartup := True;
C.UseWorkerThread := True;
C.OnWorkerExecute := TestObj.ClientExec;
C.Active := True;
repeat
Sleep(1);
until TestObj.FinC and TestObj.FinS;
C.Active := False;
S.Active := False;
C.Finalise;
FreeAndNil(C);
S.Finalise;
FreeAndNil(S);
TestObj.Free;
DebugObj.Free;
end;
procedure Test_ClientServer_RetryConnect;
var
S : TF5TCPServer;
C : TF5TCPClient;
DebugObj : TTCPClientServerTestObj;
TestObj : TTCPClientServerBlockTestObj;
I : Integer;
begin
DebugObj := TTCPClientServerTestObj.Create;
TestObj := TTCPClientServerBlockTestObj.Create;
S := TF5TCPServer.Create(nil);
S.OnLog := DebugObj.ServerLog;
S.AddressFamily := iaIP4;
S.BindAddress := '127.0.0.1';
S.ServerPort := 12045;
S.MaxClients := -1;
S.UseWorkerThread := True;
S.OnClientWorkerExecute := TestObj.ServerExec;
C := TF5TCPClient.Create(nil);
C.OnLog := DebugObj.ClientLog;
C.LocalHost := '0.0.0.0';
C.Host := '127.0.0.1';
C.Port := '12045';
C.WaitForStartup := True;
C.UseWorkerThread := True;
C.OnWorkerExecute := TestObj.ClientExec;
C.RetryFailedConnect := True;
C.RetryFailedConnectDelaySec := 2;
C.RetryFailedConnectMaxAttempts := 3;
C.ReconnectOnDisconnect := False;
C.Active := True;
Sleep(2000);
S.Active := True;
I := 0;
repeat
Sleep(1);
Inc(I);
until (TestObj.FinC and TestObj.FinS) or (I > 4000);
Assert(TestObj.FinC and TestObj.FinS);
//S.Active := False;
//Sleep(100000);
C.Active := False;
S.Active := False;
C.Finalise;
FreeAndNil(C);
S.Finalise;
FreeAndNil(S);
TestObj.Free;
DebugObj.Free;
end;
procedure Test_ClientServer_Latency;
procedure DoYield;
begin
{$IFDEF DELPHI}
{$IFDEF OS_MSWIN}
Yield;
{$ELSE}
TThread.Yield;
{$ENDIF}
{$ELSE}
TThread.Yield;
{$ENDIF}
end;
procedure WaitClientConnected(const Client: TF5TCPClient);
var I : Integer;
begin
// wait for client to connect
I := 0;
repeat
Inc(I);
Sleep(1);
until (I >= 8000) or
(Client.State in [csReady, csClosed]);
Assert(Client.State = csReady);
Assert(Client.Connection.State = cnsConnected);
end;
var C : TF5TCPClient;
S : TF5TCPServer;
TSC : TTCPServerClient;
I, Nr : Integer;
F : RawByteString;
DebugObj : TTCPClientServerTestObj;
T : Word64;
Buf : array[0..15] of Byte;
const
Test1N = 5;
begin
DebugObj := TTCPClientServerTestObj.Create;
DebugObj.Log('');
DebugObj.Log('Test_ClientServer_Latency:');
S := TF5TCPServer.Create(nil);
C := TF5TCPClient.Create(nil);
// init client
C.Tag := 1;
C.OnLog := DebugObj.ClientLog;
try
// init server
S.OnLog := DebugObj.ServerLog;
S.AddressFamily := iaIP4;
S.BindAddress := '127.0.0.1';
Randomize;
S.ServerPort := 12513 + Random(1000);
S.MaxClients := -1;
Assert(S.State = ssInit);
Assert(not S.Active);
// start server
S.Start;
Assert(S.Active);
I := 0;
repeat
Inc(I);
Sleep(10);
until (S.State <> ssStarting) or (I >= 400);
Sleep(100);
Assert(S.State = ssReady);
Assert(S.ClientCount = 0);
// init client
C.AddressFamily := cafIP4;
C.Host := '127.0.0.1';
C.Port := IntToStr(S.ServerPort);
Assert(C.State = csInit);
Assert(not C.Active);
// start client
C.WaitForStartup := True;
C.Start;
Assert(Assigned(C.Connection));
Assert(C.Active);
// wait for client to connect
WaitClientConnected(C);
// wait for server connections
I := 0;
repeat
Inc(I);
Sleep(10);
until (S.ClientCount >= 1) or (I >= 10000);
Assert(S.ClientCount = 1);
// wait for server clients
TSC := S.ClientIterateFirst;
Assert(Assigned(TSC));
Assert(TSC.State in [scsStarting, scsNegotiating, scsReady]);
Assert(TSC.Connection.State in [cnsProxyNegotiation, cnsConnected]);
for Nr := 1 to Test1N do
begin
// read & write (small block): client to server
// read directly from connection before buffered
T := GetMicroTick;
C.Connection.WriteByteString('Fundamentals');
repeat
DoYield;
F := TSC.Connection.ReadByteString(12);
if F = 'Fundamentals' then
break;
until MicroTickDelta(T, GetMicroTick) >= 5000000; // 5s
T := MicroTickDelta(T, GetMicroTick);
DebugObj.Log(' Latency1_ClientToServer:' + IntToStr(T));
Assert(F = 'Fundamentals');
// read & write (small block): server to client
// read directly from connection before buffered
FillChar(Buf, SizeOf(Buf), 0);
T := GetMicroTick;
TSC.Connection.WriteByteString('123');
repeat
DoYield;
if C.Connection.Read(Buf[0], 3) = 3 then
if (Buf[0] = Ord('1')) and (Buf[1] = Ord('2')) and (Buf[2] = Ord('3')) then
break;
until MicroTickDelta(T, GetMicroTick) >= 5000000; // 5s
T := MicroTickDelta(T, GetMicroTick);
DebugObj.Log(' Latency2_ServerToClient:' + IntToStr(T));
Assert((Buf[0] = Ord('1')) and (Buf[1] = Ord('2')) and (Buf[2] = Ord('3')));
// read & write (small block): client to server
T := GetMicroTick;
C.Connection.WriteByteString('Fundamentals');
repeat
DoYield;
until (TSC.Connection.ReadBufferUsed >= 12) or (MicroTickDelta(T, GetMicroTick) >= 5000000);
T := MicroTickDelta(T, GetMicroTick);
DebugObj.Log(' Latency3_ClientToServer:' + IntToStr(T));
Assert(TSC.Connection.ReadBufferUsed = 12);
F := TSC.Connection.ReadByteString(12);
Assert(F = 'Fundamentals');
// read & write (small block): server to client
T := GetMicroTick;
TSC.Connection.WriteByteString('123');
repeat
DoYield;
until (C.Connection.ReadBufferUsed >= 3) or (MicroTickDelta(T, GetMicroTick) >= 5000000);
T := MicroTickDelta(T, GetMicroTick);
DebugObj.Log(' Latency4_ServerToClient:' + IntToStr(T));
F := C.Connection.ReadByteString(3);
Assert(F = '123');
end;
TSC.ReleaseReference;
finally
C.Free;
S.Free;
end;
FreeAndNil(DebugObj);
end;
procedure Test;
begin
Test_ClientServer_ReadWrite;
Test_ClientServer_StopStart;
Test_ClientServer_Shutdown;
Test_ClientServer_Block;
Test_ClientServer_RetryConnect;
Test_ClientServer_Block;
Test_ClientServer_ReadWrite;
Test_ClientServer_Latency;
end;
{$ENDIF}
end.

View File

@@ -0,0 +1,303 @@
{ 2020/05/11 5.01 Move tests from unit flcTests into seperate units. }
{$INCLUDE flcTCPTest.inc}
unit flcTCPTest_ClientServerTLS;
interface
{ }
{ Test }
{ }
{$IFDEF TCPCLIENTSERVER_TEST_TLS}
procedure Test;
{$ENDIF}
implementation
{$IFDEF TCPCLIENTSERVER_TEST_TLS}
uses
SysUtils,
flcTLSTransportTypes,
flcTLSTransportClient,
flcSocketLib,
flcTCPConnection,
flcTCPClient,
flcTCPServer,
flcTCPTest_ClientServer;
{$ENDIF}
{ }
{ Test }
{ }
{$IFDEF TCPCLIENTSERVER_TEST_TLS}
{$ASSERTIONS ON}
procedure TestClientServerTLS_ReadWrite(
const TestClientCount: Integer;
const TestLargeBlock: Boolean;
const TLSOptions: TTCPClientTLSOptions = DefaultTCPClientTLSOptions;
const TLSClientOptions: TTCPClientTLSClientOptions = DefaultTLSClientOptions;
const TLSVersionOptions: TTCPClientTLSVersionOptions = DefaultTLSClientVersionOptions;
const TLSKeyExchangeOptions: TTCPClientTLSKeyExchangeOptions = DefaultTLSClientKeyExchangeOptions;
const TLSCipherOptions: TTCPClientTLSCipherOptions = DefaultTLSClientCipherOptions;
const TLSHashOptions: TTCPClientTLSHashOptions = DefaultTLSClientHashOptions
);
procedure WaitClientConnected(const Client: TF5TCPClient);
var I : Integer;
begin
// wait for client to connect and finish TLS
I := 0;
repeat
Inc(I);
Sleep(1);
until (I >= 8000) or
(
(Client.State in [csReady, csClosed])
and
(
not Client.TLSEnabled or
(Client.TLSClient.IsFinishedState or Client.TLSClient.IsReadyState)
)
);
Assert(Client.State = csReady);
Assert(Client.Connection.State = cnsConnected);
Assert(not Client.TLSEnabled or Client.TLSClient.IsReadyState);
end;
var C : TF5TCPClientArray;
S : TF5TCPServer;
T : TTCPServerClientArray;
TSC : TTCPServerClient;
I, K : Integer;
// CtL : TTLSCertificateList;
DebugObj : TTCPClientServerTestObj;
begin
DebugObj := TTCPClientServerTestObj.Create;
S := TF5TCPServer.Create(nil);
SetLength(C, TestClientCount);
SetLength(T, TestClientCount);
for K := 0 to TestClientCount - 1 do
begin
C[K] := TF5TCPClient.Create(nil);
// init client
C[K].Tag := K + 1;
C[K].OnLog := DebugObj.ClientLog;
C[K].TLSOptions := TLSOptions;
C[K].TLSClientOptions := TLSClientOptions;
C[K].TLSVersionOptions := TLSVersionOptions;
C[K].TLSKeyExchangeOptions := TLSKeyExchangeOptions;
C[K].TLSCipherOptions := TLSCipherOptions;
C[K].TLSHashOptions := TLSHashOptions;
end;
try
// init server
S.OnLog := DebugObj.ServerLog;
S.AddressFamily := iaIP4;
S.BindAddress := '127.0.0.1';
S.ServerPort := 12545;
S.MaxClients := -1;
S.TLSEnabled := True;
(*
S.TLSServer.PrivateKeyRSAPEM := // from stunnel pem file
'MIICXAIBAAKBgQCxUFMuqJJbI9KnB8VtwSbcvwNOltWBtWyaSmp7yEnqwWel5TFf' +
'cOObCuLZ69sFi1ELi5C91qRaDMow7k5Gj05DZtLDFfICD0W1S+n2Kql2o8f2RSvZ' +
'qD2W9l8i59XbCz1oS4l9S09L+3RTZV9oer/Unby/QmicFLNM0WgrVNiKywIDAQAB' +
'AoGAKX4KeRipZvpzCPMgmBZi6bUpKPLS849o4pIXaO/tnCm1/3QqoZLhMB7UBvrS' +
'PfHj/Tejn0jjHM9xYRHi71AJmAgzI+gcN1XQpHiW6kATNDz1r3yftpjwvLhuOcp9' +
'tAOblojtImV8KrAlVH/21rTYQI+Q0m9qnWKKCoUsX9Yu8UECQQDlbHL38rqBvIMk' +
'zK2wWJAbRvVf4Fs47qUSef9pOo+p7jrrtaTqd99irNbVRe8EWKbSnAod/B04d+cQ' +
'ci8W+nVtAkEAxdqPOnCISW4MeS+qHSVtaGv2kwvfxqfsQw+zkwwHYqa+ueg4wHtG' +
'/9+UgxcXyCXrj0ciYCqURkYhQoPbWP82FwJAWWkjgTgqsYcLQRs3kaNiPg8wb7Yb' +
'NxviX0oGXTdCaAJ9GgGHjQ08lNMxQprnpLT8BtZjJv5rUOeBuKoXagggHQJAaUAF' +
'91GLvnwzWHg5p32UgPsF1V14siX8MgR1Q6EfgKQxS5Y0Mnih4VXfnAi51vgNIk/2' +
'AnBEJkoCQW8BTYueCwJBALvz2JkaUfCJc18E7jCP7qLY4+6qqsq+wr0t18+ogOM9' +
'JIY9r6e1qwNxQ/j1Mud6gn6cRrObpRtEad5z2FtcnwY=';
TLSCertificateListAppend(CtL,
MIMEBase64Decode( // from stunnel pem file
'MIICDzCCAXigAwIBAgIBADANBgkqhkiG9w0BAQQFADBCMQswCQYDVQQGEwJQTDEf' +
'MB0GA1UEChMWU3R1bm5lbCBEZXZlbG9wZXJzIEx0ZDESMBAGA1UEAxMJbG9jYWxo' +
'b3N0MB4XDTk5MDQwODE1MDkwOFoXDTAwMDQwNzE1MDkwOFowQjELMAkGA1UEBhMC' +
'UEwxHzAdBgNVBAoTFlN0dW5uZWwgRGV2ZWxvcGVycyBMdGQxEjAQBgNVBAMTCWxv' +
'Y2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsVBTLqiSWyPSpwfF' +
'bcEm3L8DTpbVgbVsmkpqe8hJ6sFnpeUxX3Djmwri2evbBYtRC4uQvdakWgzKMO5O' +
'Ro9OQ2bSwxXyAg9FtUvp9iqpdqPH9kUr2ag9lvZfIufV2ws9aEuJfUtPS/t0U2Vf' +
'aHq/1J28v0JonBSzTNFoK1TYissCAwEAAaMVMBMwEQYJYIZIAYb4QgEBBAQDAgZA' +
'MA0GCSqGSIb3DQEBBAUAA4GBAAhYFTngWc3tuMjVFhS4HbfFF/vlOgTu44/rv2F+' +
'ya1mEB93htfNxx3ofRxcjCdorqONZFwEba6xZ8/UujYfVmIGCBy4X8+aXd83TJ9A' +
'eSjTzV9UayOoGtmg8Dv2aj/5iabNeK1Qf35ouvlcTezVZt2ZeJRhqUHcGaE+apCN' +
'TC9Y'));
S.TLSServer.CertificateList := CtL;
*)
S.TLSServer.PEMText :=
'-----BEGIN CERTIFICATE-----' +
'MIIDQjCCAiqgAwIBAgIJAKDslQh3d8kdMA0GCSqGSIb3DQEBBQUAMB8xHTAbBgNV' +
'BAMTFHd3dy5ldGVybmFsbGluZXMuY29tMB4XDTExMTAxODEwMzYwOVoXDTIxMTAx' +
'NTEwMzYwOVowHzEdMBsGA1UEAxMUd3d3LmV0ZXJuYWxsaW5lcy5jb20wggEiMA0G' +
'CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCw/7d6zyehR69DaJCGbk3oMP7pSWya' +
'U1tDMG+CdqikLqHoo3SBshbvquOVFcy9yY8fECTbNXfOjhV0M6SJgGQ/SP/nfZgx' +
'MHAK9sWc5G6V5sqPqrTRgkv0Wu25mdO6FRh8DIxOMY0Ppqno5hHZ0emSj1amvtWX' +
'zBD6pXNGgrFln6HL2eyCwqlL0wTXWO/YrvblF/83Ln9i6luVQ9NtACQBiPcYqoNM' +
'1OG142xYNpRNp7zrHkNCQeXVxmC6goCgj0BmcSqrUPayLdgkgv8hniUwLYQIt91r' +
'cxJwGNWxlbLgqQqTdhecKp01JVgO8jy3yFpMEoqCj9+BuuxVqDfvHK1tAgMBAAGj' +
'gYAwfjAdBgNVHQ4EFgQUbLgD+S3ZSNlU1nxTsjTmAQIfpCQwTwYDVR0jBEgwRoAU' +
'bLgD+S3ZSNlU1nxTsjTmAQIfpCShI6QhMB8xHTAbBgNVBAMTFHd3dy5ldGVybmFs' +
'bGluZXMuY29tggkAoOyVCHd3yR0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUF' +
'AAOCAQEACSQTcPC8ga5C/PysnoTNAk4OB+hdgMoS3Fv7ROUV9GqgYED6rJo0+CxD' +
'g19GLlKt/aBlglh4Ddc7X84dWtftS4JIjjVVkWevt8/sDoZ+ISd/tC9aDX3gOAlW' +
'RORhfp3Qtyy0AjZcIOAGNkzkotuMG/uOVifPFhTNXwa8hHOGN60riGXEj5sNFFop' +
'EaxplTfakVq8TxlQivnIETjrEbVX8XkOl4nlsHevC2suXE1ZkQIbQoaAy0WzGGUR' +
'54GBIzXf32t80S71w5rs/mzVaGOeTZYcHtv5Epd9CNVrEle6w0NW9R7Ov4gXI9n8' +
'GV9jITGfsOdqu7j9Iaf7MVj+JRE7Dw==' +
'-----END CERTIFICATE-----' +
'-----BEGIN RSA PRIVATE KEY-----' +
'MIIEpQIBAAKCAQEAsP+3es8noUevQ2iQhm5N6DD+6UlsmlNbQzBvgnaopC6h6KN0' +
'gbIW76rjlRXMvcmPHxAk2zV3zo4VdDOkiYBkP0j/532YMTBwCvbFnORulebKj6q0' +
'0YJL9FrtuZnTuhUYfAyMTjGND6ap6OYR2dHpko9Wpr7Vl8wQ+qVzRoKxZZ+hy9ns' +
'gsKpS9ME11jv2K725Rf/Ny5/YupblUPTbQAkAYj3GKqDTNThteNsWDaUTae86x5D' +
'QkHl1cZguoKAoI9AZnEqq1D2si3YJIL/IZ4lMC2ECLfda3MScBjVsZWy4KkKk3YX' +
'nCqdNSVYDvI8t8haTBKKgo/fgbrsVag37xytbQIDAQABAoIBAQCdnZnOCtrHjAZO' +
'iLbqfx9xPPBC3deQNdp3IpKqIvBaBAy6FZSSSfySwCKZiCgieXKxvraTXjGqBmyk' +
'ZbiHmYWrtV3szrLQWsnreYTQCbtQUYzgEquiRd1NZAt907XvZwm+rY3js8xhu5Bi' +
'jT4oMf1FPc9z/UxHOLmF+f+FMqy2SM2Fxh3jAsxJBaMVEJXpqdQDI86CATgYrqVY' +
'mlAWQcQ8pL0wwRctZ+XgjQH52V3sk4cIzqIBTO+MN6emmxDl9JdrGZKRei9YEIhG' +
'mFeXH7rsGg+TZtfvu1M9Kfy2fdgNwTUoTTn93v8gcrwCbyvl5JCzKy07Om/aOXFr' +
'I8bSWXIhAoGBANu07hegU99zIhvTWmh2Fuml0Lr+cHcZTObh+oeZg1xaDUrlnFOY' +
'3fyA5x5Jxib3V7OOAeIz/AsmcYq/649nR8NfeiizY5or84Fy1mazRR8diGDV3nUG' +
'ZATv6yaOY/z31FOLaxT95tDvqWK+Qr5cykq4e6XDDp9P8odCIjJmUdt7AoGBAM48' +
'vCjtGQ99BVwkcFIj0IacRj3YKzsp06W6V2Z+czlKctJAMAQN8hu9IcXMEIUsi9GD' +
'MkyzzxjvGRdmIuS58IFqRbr/fIAQLVpY9SPAL771ZCFHmIrKrCYiLYAcg/BSoR29' +
'me6aFaEcLBFvzHPFNymdyMsaOHSRMZYUlq6VUbI3AoGBAINJeMURf00VRZqPD4VA' +
'm6x+813qUVY5/iQxgT2qVD7JaQwKbQHfZTdP58vHlesO/o9DGokLO1+GV27sBF0r' +
'AE0VLrBHkgs8nEQMVWYFVhaj1SzYYBhZ+0af/0qI5+LwTSanNxPSLS1JKVTiEIwk' +
'cpV37Bs/letJIMoGkNzBG8UlAoGBAKrSfZt8f3RnvmfKusoeZhsJF9kj0vMHOwob' +
'ZUc8152Nf7uMdPj2wCGfr3iRBOH5urnH7ILBsHjbmjHaZG6FYKMg7i7sbSf5vkcG' +
'Rc3d4u5NfSlfjwbuxlYzmvJxLAuDtXXX1MdgEyhGGG485uDBamZrDaTEzBwpIyRH' +
'W2OxxGBTAoGAZHJQKTajcqQQoRSgPPWWU3X8zdlu5hCgNU54bXaPAfJ6IBWvicMZ' +
'QLw+9mtshtz+Xy0aBbkxUeUlwwzexb9rg1KZppTq/yRqkOlEkI3ZdqiclTK13BCh' +
'6r6dC2qqq+DVm9Nlm/S9Gab9YSIA0g5MFg5WLwu1KNwuOODE4Le/91c=' +
'-----END RSA PRIVATE KEY-----';
Assert(S.State = ssInit);
Assert(not S.Active);
// start server
S.Start;
Assert(S.Active);
I := 0;
repeat
Inc(I);
Sleep(1);
until (S.State <> ssStarting) or (I >= 5000);
Sleep(100);
Assert(S.State = ssReady);
Assert(S.ClientCount = 0);
for K := 0 to TestClientCount - 1 do
begin
// init client
C[K].AddressFamily := cafIP4;
C[K].Host := '127.0.0.1';
C[K].Port := '12545';
C[K].TLSEnabled := True;
Assert(C[K].State = csInit);
Assert(not C[K].Active);
// start client
C[K].WaitForStartup := True;
C[K].Start;
Assert(Assigned(C[K].Connection));
Assert(C[K].Active);
end;
for K := 0 to TestClientCount - 1 do
// wait for client to connect
WaitClientConnected(C[K]);
// wait for server connections
I := 0;
repeat
Inc(I);
Sleep(1);
until (S.ClientCount >= TestClientCount) or (I >= 5000);
Assert(S.ClientCount = TestClientCount);
// wait for server clients
TSC := S.ClientIterateFirst;
for K := 0 to TestClientCount - 1 do
begin
T[K] := TSC;
Assert(Assigned(T[K]));
Assert(T[K].State in [scsStarting, scsNegotiating, scsReady]);
Assert(T[K].Connection.State in [cnsProxyNegotiation, cnsConnected]);
I := 0;
repeat
Inc(I);
Sleep(1);
until (T[K].Connection.State = cnsConnected) or (I >= 5000);
Assert(T[K].Connection.State = cnsConnected);
Assert(not C[K].TLSEnabled or T[K].TLSClient.IsReadyState);
TSC.ReleaseReference;
TSC := S.ClientIterateNext(TSC);
end;
// test read/write
TestClientServer_ReadWrite_Blocks(
TestClientCount,
TestLargeBlock,
DebugObj,
C, T);
// release reference
for K := 0 to TestClientCount - 1 do
T[K].ReleaseReference;
// stop clients
for K := TestClientCount - 1 downto 0 do
begin
C[K].Stop;
Assert(C[K].State = csStopped);
end;
I := 0;
repeat
Inc(I);
Sleep(1);
until (S.ClientCount = 0) or (I >= 5000);
Assert(S.ClientCount = 0);
// stop server
S.Stop;
Assert(not S.Active);
finally
for K := TestClientCount - 1 downto 0 do
begin
C[K].Finalise;
FreeAndNil(C[K]);
end;
S.Finalise;
FreeAndNil(S);
DebugObj.Free;
end;
end;
procedure Test_ClientServerTLS_ReadWrite;
begin
// TestClientServerTLS_ReadWrite(tmTLS, 1, True, [ctoDisableTLS10, ctoDisableTLS11, ctoDisableTLS12]); // SSL 3.0
TestClientServerTLS_ReadWrite(1, True, DefaultTCPClientTLSOptions, DefaultTLSClientOptions,
[tlsvoTLS12]); // TLS 1.2
TestClientServerTLS_ReadWrite(1, True, DefaultTCPClientTLSOptions, DefaultTLSClientOptions,
[tlsvoTLS11]); // TLS 1.1
TestClientServerTLS_ReadWrite(1, True, DefaultTCPClientTLSOptions, DefaultTLSClientOptions,
[tlsvoTLS10]); // TLS 1.0
end;
procedure Test;
begin
Test_ClientServerTLS_ReadWrite;
end;
{$ENDIF}
end.

View File

@@ -0,0 +1,172 @@
{ 2020/05/11 5.01 Move tests from unit flcTests into seperate units. }
{$INCLUDE flcTCPTest.inc}
{$IFDEF TCPCLIENT_TEST_TLS}
{$DEFINE TCPCLIENT_TEST_TLS_WEB}
{$ENDIF}
unit flcTCPTest_ClientTLS;
interface
{ }
{ Test }
{ }
{$IFDEF TCPCLIENT_TEST_TLS}
procedure Test;
{$ENDIF}
implementation
{$IFDEF TCPCLIENT_TEST_TLS}
uses
SysUtils,
flcTLSTransportTypes,
flcTLSTransportConnection,
flcTLSTransportClient,
flcTCPConnection,
flcTCPClient,
flcTCPTest_Client;
{$ENDIF}
{ }
{ Test }
{ }
{$IFDEF TCPCLIENT_TEST_TLS}
{$ASSERTIONS ON}
{$IFDEF TCPCLIENT_TEST_TLS_WEB}
const
TLSWebTestHost = 'www.rfc.org'; // DHE_RSA_WITH_AES_256_CBC_SHA
procedure Test_Client_TLS_Web(
const TLSOptions: TTCPClientTLSOptions = [];
const TLSClientOptions: TTCPClientTLSClientOptions = DefaultTLSClientOptions;
const TLSVersionOptions: TTCPClientTLSVersionOptions = DefaultTLSClientVersionOptions;
const TLSKeyExchangeOptions: TTCPClientTLSKeyExchangeOptions = DefaultTLSClientKeyExchangeOptions;
const TLSCipherOptions: TTCPClientTLSCipherOptions = DefaultTLSClientCipherOptions;
const TLSHashOptions: TTCPClientTLSHashOptions = DefaultTLSClientHashOptions
);
var
C : TF5TCPClient;
I, L : Integer;
S : RawByteString;
A : TTCPClientTestObj;
begin
A := TTCPClientTestObj.Create;
C := TF5TCPClient.Create(nil);
try
// init
C.OnLog := A.ClientLog;
C.TLSEnabled := True;
C.TLSOptions := TLSOptions;
C.TLSClientOptions := TLSClientOptions;
C.TLSVersionOptions := TLSVersionOptions;
C.TLSKeyExchangeOptions := TLSKeyExchangeOptions;
C.TLSCipherOptions := TLSCipherOptions;
C.TLSHashOptions := TLSHashOptions;
C.LocalHost := '0.0.0.0';
C.Host := TLSWebTestHost;
C.Port := '443';
// start
C.Active := True;
Assert(C.Active);
// wait connect
I := 0;
repeat
Sleep(1);
Inc(I);
until (
(C.State in [csReady, csClosed]) and
(C.TLSClient.ConnectionState in [tlscoApplicationData, tlscoErrorBadProtocol, tlscoCancelled, tlscoClosed]) and
(C.Connection.State = cnsConnected)
) or
(I = 5000);
Assert(C.State = csReady);
Assert(C.Connection.State = cnsConnected);
Assert(C.TLSClient.ConnectionState = tlscoApplicationData);
// send
S :=
'GET / HTTP/1.1'#13#10 +
'Host: ' + TLSWebTestHost + #13#10 +
'Date: 11 Oct 2011 12:34:56 GMT'#13#10 +
#13#10;
C.Connection.Write(S[1], Length(S));
C.BlockingConnection.WaitForTransmitFin(5000);
// read
C.BlockingConnection.WaitForReceiveData(1, 5000);
L := C.Connection.ReadBufferUsed;
Assert(L > 0);
SetLength(S, L);
Assert(C.Connection.Read(S[1], L) = L);
Assert(Copy(S, 1, 6) = 'HTTP/1');
// close
C.BlockingConnection.Shutdown(2000, 2000, 5000);
Assert(C.Connection.State = cnsClosed);
// stop
C.Active := False;
Assert(not C.Active);
finally
C.Finalise;
FreeAndNil(C);
A.Free;
end;
end;
procedure TestClientTLSWeb;
begin
//Test_Client_TLS_Web([ctoDisableTLS10, ctoDisableTLS11, ctoDisableTLS12]); // SSL 3
//Test_Client_TLS_Web([ctoDisableSSL3, ctoDisableTLS11, ctoDisableTLS12]); // TLS 1.0
//Test_Client_TLS_Web([ctoDisableSSL3, ctoDisableTLS10, ctoDisableTLS12]); // TLS 1.1
// TLS 1.2
{Test_Client_TLS_Web(
DefaultTCPClientTLSOptions,
DefaultTLSClientOptions,
[tlsvoTLS12],
DefaultTLSClientKeyExchangeOptions,
DefaultTLSClientCipherOptions,
DefaultTLSClientHashOptions);}
// TLS 1.2
Test_Client_TLS_Web(
DefaultTCPClientTLSOptions,
DefaultTLSClientOptions,
[tlsvoTLS12],
[tlskeoDHE_RSA],
DefaultTLSClientCipherOptions,
DefaultTLSClientHashOptions);
{ Test_Client_TLS_Web(
DefaultTCPClientTLSOptions,
DefaultTLSClientOptions,
[tlsvoTLS12],
[tlskeoRSA],
[tlsco3DES],
DefaultTLSClientHashOptions); }
//Test_Client_TLS_Web([]);
end;
{$ENDIF}
procedure Test;
begin
{$IFDEF TCPCLIENT_TEST_TLS_WEB}
TestClientTLSWeb;
{$ENDIF}
end;
{$ENDIF}
end.

View File

@@ -0,0 +1,223 @@
{ 2020/05/11 5.01 Move tests from unit flcTests into seperate units. }
{$INCLUDE flcTCPTest.inc}
unit flcTCPTest_Server;
interface
{ }
{ Test }
{ }
{$IFDEF TCPSERVER_TEST}
procedure Test;
{$ENDIF}
implementation
{$IFDEF TCPSERVER_TEST}
uses
SysUtils,
flcStdTypes,
flcSocketLib,
flcSocket,
flcTCPUtils,
flcTCPConnection,
flcTCPServer;
{$ENDIF}
{ }
{ Test }
{ }
{$IFDEF TCPSERVER_TEST}
{$ASSERTIONS ON}
procedure Test_Server_Simple;
var S : TF5TCPServer;
I : Integer;
begin
S := TF5TCPServer.Create(nil);
try
// init
S.AddressFamily := iaIP4;
S.ServerPort := 12745;
S.MaxClients := -1;
Assert(S.State = ssInit);
Assert(not S.Active);
// activate
S.Active := True;
Assert(S.Active);
I := 0;
repeat
Inc(I);
Sleep(1);
until (S.State <> ssStarting) or (I >= 5000);
Assert(S.State = ssReady);
Assert(S.ClientCount = 0);
// shut down
S.Active := False;
Assert(not S.Active);
Assert(S.State = ssClosed);
finally
S.Finalise;
FreeAndNil(S);
end;
S := TF5TCPServer.Create(nil);
try
// init
S.AddressFamily := iaIP4;
S.ServerPort := 12745;
S.MaxClients := -1;
Assert(S.State = ssInit);
for I := 1 to 10 do
begin
// activate
Assert(not S.Active);
S.Active := True;
Assert(S.Active);
// deactivate
S.Active := False;
Assert(not S.Active);
Assert(S.State = ssClosed);
end;
finally
S.Finalise;
FreeAndNil(S);
end;
end;
procedure Test_Server_Connections;
const
MaxConns = 10;
var
T : Word32;
S : TF5TCPServer;
C : array[1..MaxConns] of TSysSocket;
I : Integer;
begin
S := TF5TCPServer.Create(nil);
S.AddressFamily := iaIP4;
S.BindAddress := '127.0.0.1';
S.ServerPort := 12249;
S.MaxClients := -1;
S.Active := True;
for I := 1 to MaxConns do
begin
C[I] := TSysSocket.Create(iaIP4, ipTCP, False);
C[I].Bind('127.0.0.1', 0);
C[I].SetBlocking(False);
end;
T := TCPGetTick;
for I := 1 to MaxConns do
begin
C[I].Connect('127.0.0.1', '12249');
Sleep(5);
if I mod 100 = 0 then
Writeln(I, ' ', Word32(TCPGetTick - T) / I:0:2);
end;
I := 0;
repeat
Sleep(10);
Inc(I, 10);
until (S.ClientCount = MaxConns) or (I > 4000);
Assert(S.ClientCount = MaxConns);
T := Word32(TCPGetTick - T);
Writeln(T / MaxConns:0:2);
for I := 1 to MaxConns do
C[I].Close;
for I := 1 to MaxConns do
FreeAndNil(C[I]);
S.Active := False;
Sleep(100);
S.Finalise;
S.Free;
end;
procedure Test_Server_MultiServers_Connections;
const
MaxSrvrs = 20;
MaxConns = 10;
var
T : Word32;
S : array[1..MaxSrvrs] of TF5TCPServer;
C : array[1..MaxSrvrs] of array[1..MaxConns] of TSysSocket;
SySo : TSysSocket;
I, J : Integer;
begin
for I := 1 to MaxSrvrs do
begin
S[I] := TF5TCPServer.Create(nil);
S[I].AddressFamily := iaIP4;
S[I].BindAddress := '127.0.0.1';
S[I].ServerPort := 12300 + I;
S[I].MaxClients := -1;
S[I].Active := True;
end;
for J := 1 to MaxSrvrs do
for I := 1 to MaxConns do
begin
SySo := TSysSocket.Create(iaIP4, ipTCP, False);
C[J][I] := SySo;
SySo.Bind('127.0.0.1', 0);
SySo.SetBlocking(False);
end;
T := TCPGetTick;
for J := 1 to MaxSrvrs do
for I := 1 to MaxConns do
begin
C[J][I].Connect('127.0.0.1', RawByteString(IntToStr(12300 + J)));
if I mod 2 = 0 then
Sleep(1);
if I mod 100 = 0 then
Writeln(J, ' ', I, ' ', Word32(TCPGetTick - T) / (I + (J - 1) * MaxConns):0:2);
end;
I := 0;
Sleep(1000);
repeat
Sleep(1);
Inc(I);
until (S[MaxSrvrs].ClientCount = MaxConns) or (I > 10000);
Assert(S[MaxSrvrs].ClientCount = MaxConns);
T := Word32(TCPGetTick - T);
Writeln(T / (MaxConns * MaxSrvrs):0:2);
Sleep(1000);
for J := 1 to MaxSrvrs do
for I := 1 to MaxConns do
begin
C[J][I].Close;
C[J][I].Free;
end;
for I := 1 to MaxSrvrs do
S[I].Active := False;
for I := 1 to MaxSrvrs do
begin
S[I].Finalise;
S[I].Free;
end;
end;
procedure Test;
begin
Test_Server_Simple;
Test_Server_Connections;
Test_Server_MultiServers_Connections;
end;
{$ENDIF}
end.

View File

@@ -0,0 +1,63 @@
{ 2020/05/11 5.01 Move tests from unit flcTests into seperate units. }
{$INCLUDE flcTCPTest.inc}
unit flcTCPTest_ServerTLS;
interface
{$IFDEF TCPSERVER_TEST_TLS}
{ }
{ Test }
{ }
procedure Test;
{$ENDIF}
implementation
{$IFDEF TCPSERVER_TEST_TLS}
uses
SysUtils,
flcSocketLib,
flcTLSTestCertificates,
flcTCPServer;
{$ENDIF}
{ }
{ Test }
{ }
{$IFDEF TCPSERVER_TEST_TLS}
{$ASSERTIONS ON}
procedure Test_Server_TLS;
var
S : TF5TCPServer;
begin
S := TF5TCPServer.Create(nil);
try
// init
S.AddressFamily := iaIP4;
S.ServerPort := 12845;
S.TLSEnabled := True;
S.TLSServer.PrivateKeyRSAPEM := RSA_STunnel_PrivateKeyRSAPEM;
S.Active := True;
finally
S.Finalise;
FreeAndNil(S);
end;
end;
procedure Test;
begin
Test_Server_TLS;
end;
{$ENDIF}
end.

View File

@@ -0,0 +1,162 @@
{******************************************************************************}
{ }
{ Library: Fundamentals 5.00 }
{ File name: flcTCPTests.pas }
{ File version: 5.11 }
{ Description: TCP tests. }
{ }
{ Copyright: Copyright (c) 2007-2020, David J Butler }
{ All rights reserved. }
{ This file is licensed under the BSD License. }
{ See http://www.opensource.org/licenses/bsd-license.php }
{ Redistribution and use in source and binary forms, with }
{ or without modification, are permitted provided that }
{ the following conditions are met: }
{ Redistributions of source code must retain the above }
{ copyright notice, this list of conditions and the }
{ following disclaimer. }
{ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND }
{ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED }
{ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED }
{ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A }
{ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL }
{ THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, }
{ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR }
{ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, }
{ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF }
{ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) }
{ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER }
{ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING }
{ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE }
{ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE }
{ POSSIBILITY OF SUCH DAMAGE. }
{ }
{ Github: https://github.com/fundamentalslib }
{ E-mail: fundamentals.library at gmail.com }
{ }
{ Revision history: }
{ }
{ 2010/12/15 0.01 Test for TLS client/server. }
{ 2011/01/02 0.02 Test for large buffers. }
{ 2011/04/22 0.03 Simple buffer tests. }
{ 2011/04/22 4.04 Test for multiple connections. }
{ 2011/10/13 4.05 SSL3 tests. }
{ 2012/04/19 4.06 Test for stopping and restarting client. }
{ 2015/04/26 4.07 Test for worker thread and blocking interface. }
{ 2016/01/08 5.08 Update for Fundamentals 5. }
{ 2018/09/08 5.09 Server tests with high connection count. }
{ 2019/04/16 5.10 Shutdown test. }
{ 2020/05/11 5.11 Move tests out into seperate units. }
{ }
{ Todo: }
{ - Test case socks proxy }
{ - Test case buffer full/empty events }
{ - Test case deferred shutdown }
{ - Test case throttling }
{ - Test case read/write rate reporting }
{ - Test case multiple proxies }
{ - Test case writing large chunks }
{ - Test case performance }
{ - Test case stress test (throughput and number of connections) }
{ - See SSL3 test case }
{******************************************************************************}
{$INCLUDE flcTCPTest.inc}
unit flcTCPTests;
interface
{ }
{ Test }
{ }
{$IFDEF TCP_TEST}
procedure Test;
{$ENDIF}
implementation
uses
flcTCPTest_Buffer
{$IFDEF TCPCLIENT_TEST},
flcTCPTest_Client
{$ENDIF}
{$IFDEF TCPCLIENT_TEST_TLS},
flcTCPTest_ClientTLS
{$ENDIF}
{$IFDEF TCPSERVER_TEST},
flcTCPTest_Server
{$ENDIF}
{$IFDEF TCPSERVER_TEST_TLS},
flcTCPTest_ServerTLS
{$ENDIF}
{$IFDEF TCPCLIENTSERVER_TEST},
flcTCPTest_ClientServer
{$ENDIF}
{$IFDEF TCPCLIENTSERVER_TEST_TLS},
flcTCPTest_ClientServerTLS
{$ENDIF}
;
{$IFDEF TCP_TEST}
{$ASSERTIONS ON}
procedure Test_Buffer;
begin
flcTCPTest_Buffer.Test;
end;
{$IFDEF TCPCLIENT_TEST}
procedure Test_Client;
begin
flcTCPTest_Client.Test;
{$IFDEF TCPCLIENT_TEST_TLS}
flcTCPTest_ClientTLS.Test;
{$ENDIF}
end;
{$ENDIF}
{$IFDEF TCPSERVER_TEST}
procedure Test_Server;
begin
flcTCPTest_Server.Test;
{$IFDEF TCPSERVER_TEST_TLS}
flcTCPTest_ServerTLS.Test;
{$ENDIF}
end;
{$ENDIF}
{$IFDEF TCPCLIENTSERVER_TEST}
procedure Test_ClientServer;
begin
flcTCPTest_ClientServer.Test;
{$IFDEF TCPCLIENTSERVER_TEST_TLS}
flcTCPTest_ClientServerTLS.Test;
{$ENDIF}
end;
{$ENDIF}
procedure Test;
begin
Test_Buffer;
{$IFDEF TCPSERVER_TEST}
Test_Server;
{$ENDIF}
{$IFDEF TCPCLIENT_TEST}
Test_Client;
{$ENDIF}
{$IFDEF TCPCLIENTSERVER_TEST}
Test_ClientServer;
{$ENDIF}
end;
{$ENDIF}
end.

View File

@@ -0,0 +1,40 @@
{******************************************************************************}
{ }
{ Library: Fundamentals 5.00 }
{ File name: flcTCP.inc }
{ Description: TCP library conditional defines. }
{ }
{******************************************************************************}
{.DEFINE TLS}
{.DEFINE TCPCLIENT_SOCKS}
{.DEFINE TCPCLIENT_WEBSOCKET}
{.DEFINE DEBUG_TCP}
{$IFDEF DEBUG}
{$IFDEF TEST}
{$DEFINE DEBUG_TCP}
{$ENDIF}
{$ENDIF}
{$IFDEF DEBUG_TCP}
{.DEFINE TCP_DEBUG}
{.DEFINE TCP_DEBUG_DATA}
{$DEFINE TCP_DEBUG_PROXY}
{$DEFINE TCP_DEBUG_TLS}
{.DEFINE TCP_DEBUG_SOCKET}
{$DEFINE TCP_DEBUG_CONNECTION}
{.DEFINE TCP_DEBUG_THREAD}
{.DEFINE TCP_DEBUG_WEBSOCKET}
{.DEFINE TCP_LOG_PARAMETERS}
{.DEFINE TCP_LOG_SERVERSTATE}
{$ENDIF}
{$IFDEF TLS}
{$DEFINE TCP_TLS}
{$DEFINE TCPCLIENT_TLS}
{$DEFINE TCPSERVER_TLS}
{$ENDIF}

View File

@@ -0,0 +1,694 @@
{******************************************************************************}
{ }
{ Library: Fundamentals 5.00 }
{ File name: flcTCPBuffer.pas }
{ File version: 5.08 }
{ Description: TCP buffer. }
{ }
{ Copyright: Copyright (c) 2007-2020, David J Butler }
{ All rights reserved. }
{ This file is licensed under the BSD License. }
{ See http://www.opensource.org/licenses/bsd-license.php }
{ Redistribution and use in source and binary forms, with }
{ or without modification, are permitted provided that }
{ the following conditions are met: }
{ Redistributions of source code must retain the above }
{ copyright notice, this list of conditions and the }
{ following disclaimer. }
{ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND }
{ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED }
{ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED }
{ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A }
{ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL }
{ THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, }
{ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR }
{ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, }
{ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF }
{ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) }
{ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER }
{ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING }
{ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE }
{ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE }
{ POSSIBILITY OF SUCH DAMAGE. }
{ }
{ Github: https://github.com/fundamentalslib }
{ E-mail: fundamentals.library at gmail.com }
{ }
{ Revision history: }
{ }
{ 2008/12/23 0.01 Initial development. }
{ 2010/12/02 0.02 Revision. }
{ 2011/04/22 0.03 Simple test cases. }
{ 2011/06/16 0.04 Minor change in PeekPtr routine. }
{ 2011/09/03 4.05 Revised for Fundamentals 4. }
{ 2016/01/09 5.06 Revised for Fundamentals 5. }
{ 2019/04/10 5.07 Change default buffer size. }
{ 2019/12/29 5.08 Minimum buffer size. }
{ }
{******************************************************************************}
{$INCLUDE ../flcInclude.inc}
{$INCLUDE flcTCP.inc}
unit flcTCPBuffer;
interface
uses
{ System }
SysUtils,
{ Utils }
flcStdTypes;
{ }
{ TCP Buffer }
{ }
type
ETCPBuffer = class(Exception);
TTCPBuffer = record
Ptr : Pointer;
Size : Integer;
Min : Int32;
Max : Int32;
Head : Int32;
Used : Int32;
end;
const
ETHERNET_MTU = 1500;
ETHERNET_MTU_JUMBO = 9000;
TCP_BUFFER_DEFAULTMAXSIZE = ETHERNET_MTU_JUMBO * 8; // 72,000 bytes
TCP_BUFFER_DEFAULTMINSIZE = ETHERNET_MTU * 6; // 9,000 bytes
procedure TCPBufferInitialise(
var TCPBuf: TTCPBuffer;
const TCPBufMaxSize: Int32 = TCP_BUFFER_DEFAULTMAXSIZE;
const TCPBufMinSize: Int32 = TCP_BUFFER_DEFAULTMINSIZE);
procedure TCPBufferFinalise(var TCPBuf: TTCPBuffer);
function TCPBufferGetMaxSize(const TCPBuf: TTCPBuffer): Int32; {$IFDEF UseInline}inline;{$ENDIF}
procedure TCPBufferSetMaxSize(
var TCPBuf: TTCPBuffer;
const MaxSize: Int32);
function TCPBufferGetMinSize(const TCPBuf: TTCPBuffer): Int32; {$IFDEF UseInline}inline;{$ENDIF}
procedure TCPBufferSetMinSize(
var TCPBuf: TTCPBuffer;
const MinSize: Int32);
procedure TCPBufferPack(var TCPBuf: TTCPBuffer);
procedure TCPBufferResize(
var TCPBuf: TTCPBuffer;
const TCPBufSize: Int32);
procedure TCPBufferExpand(
var TCPBuf: TTCPBuffer;
const Size: Int32);
procedure TCPBufferShrink(var TCPBuf: TTCPBuffer);
procedure TCPBufferMinimize(var TCPBuf: TTCPBuffer);
procedure TCPBufferClear(var TCPBuf: TTCPBuffer);
function TCPBufferAddPtr(
var TCPBuf: TTCPBuffer;
const Size: Int32): Pointer;
procedure TCPBufferAdded(
var TCPBuf: TTCPBuffer;
const Size: Int32);
procedure TCPBufferAddBuf(
var TCPBuf: TTCPBuffer;
const Buf; const Size: Int32);
function TCPBufferPeekPtr(
const TCPBuf: TTCPBuffer;
var BufPtr: Pointer): Int32; {$IFDEF UseInline}inline;{$ENDIF}
function TCPBufferPeek(
var TCPBuf: TTCPBuffer;
var Buf; const Size: Int32): Int32;
function TCPBufferPeekByte(
var TCPBuf: TTCPBuffer;
out B: Byte): Boolean;
function TCPBufferRemove(
var TCPBuf: TTCPBuffer;
var Buf; const Size: Int32): Int32;
function TCPBufferRemoveBuf(
var TCPBuf: TTCPBuffer;
var Buf; const Size: Int32): Boolean;
function TCPBufferDiscard(
var TCPBuf: TTCPBuffer;
const Size: Int32): Int32;
function TCPBufferUsed(const TCPBuf: TTCPBuffer): Int32; {$IFDEF UseInline}inline;{$ENDIF}
function TCPBufferEmpty(const TCPBuf: TTCPBuffer): Boolean; {$IFDEF UseInline}inline;{$ENDIF}
function TCPBufferAvailable(const TCPBuf: TTCPBuffer): Int32; {$IFDEF UseInline}inline;{$ENDIF}
function TCPBufferPtr(const TCPBuf: TTCPBuffer): Pointer; {$IFDEF UseInline}inline;{$ENDIF}
function TCPBufferLocateByteChar(const TCPBuf: TTCPBuffer;
const Delimiter: ByteCharSet; const MaxSize: Integer): Int32;
implementation
{ }
{ Resource strings }
{ }
const
SBufferOverflow = 'Buffer overflow';
{ }
{ TCP Buffer }
{ }
// Initialise a TCP buffer
procedure TCPBufferInitialise(
var TCPBuf: TTCPBuffer;
const TCPBufMaxSize: Int32;
const TCPBufMinSize: Int32);
var
L, M : Int32;
begin
TCPBuf.Ptr := nil;
TCPBuf.Size := 0;
TCPBuf.Head := 0;
TCPBuf.Used := 0;
L := TCPBufMinSize;
if L < 0 then
L := TCP_BUFFER_DEFAULTMINSIZE;
M := TCPBufMaxSize;
if M < 0 then
M := TCP_BUFFER_DEFAULTMAXSIZE;
if L > M then
L := M;
TCPBuf.Min := L;
TCPBuf.Max := M;
if L > 0 then
GetMem(TCPBuf.Ptr, L);
TCPBuf.Size := L;
end;
// Finalise a TCP buffer
procedure TCPBufferFinalise(var TCPBuf: TTCPBuffer);
var
P : Pointer;
begin
P := TCPBuf.Ptr;
if Assigned(P) then
begin
TCPBuf.Ptr := nil;
FreeMem(P);
end;
TCPBuf.Size := 0;
end;
// Gets maximum buffer size
function TCPBufferGetMaxSize(const TCPBuf: TTCPBuffer): Int32;
begin
Result := TCPBuf.Max;
end;
// Sets maximum buffer size
// Note: This limit is not enforced. It is used by TCPBufferAvailable.
procedure TCPBufferSetMaxSize(
var TCPBuf: TTCPBuffer;
const MaxSize: Int32);
var
L : Int32;
begin
L := MaxSize;
if L < 0 then
L := TCP_BUFFER_DEFAULTMAXSIZE;
TCPBuf.Max := L;
end;
// Gets minimum buffer size
function TCPBufferGetMinSize(const TCPBuf: TTCPBuffer): Int32;
begin
Result := TCPBuf.Min;
end;
procedure TCPBufferSetMinSize(
var TCPBuf: TTCPBuffer;
const MinSize: Int32);
var
L : Int32;
begin
L := MinSize;
if L < 0 then
L := TCP_BUFFER_DEFAULTMINSIZE;
TCPBuf.Min := L;
end;
// Pack a TCP buffer
// Moves data to front of buffer
// Post: TCPBuf.Head = 0
procedure TCPBufferPack(var TCPBuf: TTCPBuffer);
var
P, Q : PByte;
U, H : Int32;
begin
H := TCPBuf.Head;
if H <= 0 then
exit;
U := TCPBuf.Used;
if U <= 0 then
begin
TCPBuf.Head := 0;
exit;
end;
Assert(Assigned(TCPBuf.Ptr));
P := TCPBuf.Ptr;
Q := P;
Inc(P, H);
Move(P^, Q^, U);
TCPBuf.Head := 0;
end;
// Resize a TCP buffer
// New buffer size must be large enough to hold existing data
// Post: TCPBuf.Size = TCPBufSize
procedure TCPBufferResize(
var TCPBuf: TTCPBuffer;
const TCPBufSize: Int32);
var
U, L : Int32;
begin
L := TCPBufSize;
U := TCPBuf.Used;
// treat negative TCPBufSize parameter as zero
if L < 0 then
L := 0;
// check if shrinking buffer to less than used size
if U > L then
raise ETCPBuffer.Create(SBufferOverflow);
// check if packing required to fit buffer
if U + TCPBuf.Head > L then
TCPBufferPack(TCPBuf);
Assert(U + TCPBuf.Head <= L);
// resize
ReallocMem(TCPBuf.Ptr, L);
TCPBuf.Size := L;
end;
// Expand a TCP buffer
// Expands the size of the TCP buffer to at least Size
procedure TCPBufferExpand(
var TCPBuf: TTCPBuffer;
const Size: Int32);
var
S : Int32;
N : Int64;
I : Int64;
begin
S := TCPBuf.Size;
N := Size;
// check if expansion not required
if N <= S then
exit;
// scale up new size proportional to current size
// increase by at least quarter of current size
// this reduces the number of resizes in growing buffers
I := S + (S div 4);
if N < I then
N := I;
// ensure new size is multiple of MTU size
I := N mod ETHERNET_MTU;
if I > 0 then
Inc(N, ETHERNET_MTU - I);
// resize buffer
Assert(N >= Size);
TCPBufferResize(TCPBuf, N);
end;
// Shrink the size of a TCP buffer to release all unused memory
// Post: TCPBuf.Used = TCPBuf.Size and TCPBuf.Head = 0
procedure TCPBufferShrink(var TCPBuf: TTCPBuffer);
var
S, U : Int32;
begin
S := TCPBuf.Size;
if S <= 0 then
exit;
U := TCPBuf.Used;
if U = 0 then
begin
TCPBufferResize(TCPBuf, 0);
TCPBuf.Head := 0;
exit;
end;
if U = S then
exit;
TCPBufferPack(TCPBuf); // move data to front of buffer
TCPBufferResize(TCPBuf, U); // set size equal to used bytes
Assert(TCPBuf.Used = TCPBuf.Size);
end;
// Applies Min parameter to allocated memory
procedure TCPBufferMinimize(var TCPBuf: TTCPBuffer); {$IFDEF UseInline}inline;{$ENDIF}
var
Mi : Int32;
begin
Mi := TCPBuf.Min;
if Mi >= 0 then
if TCPBuf.Used <= Mi then
if TCPBuf.Size > Mi then
TCPBufferResize(TCPBuf, Mi);
end;
// Clear the data from a TCP buffer
procedure TCPBufferClear(var TCPBuf: TTCPBuffer); {$IFDEF UseInline}inline;{$ENDIF}
begin
TCPBuf.Used := 0;
TCPBuf.Head := 0;
TCPBufferMinimize(TCPBuf);
end;
// Returns a pointer to position in buffer to add new data of Size
// Handles resizing and packing of buffer to fit new data
function TCPBufferAddPtr(
var TCPBuf: TTCPBuffer;
const Size: Int32): Pointer; {$IFDEF UseInline}inline;{$ENDIF}
var
P : PByte;
U : Int32;
L : Int64;
H : Int32;
begin
// return nil if nothing to add
if Size <= 0 then
begin
Result := nil;
exit;
end;
U := TCPBuf.Used;
L := U + Size;
// resize if necessary
if L > TCPBuf.Size then
TCPBufferExpand(TCPBuf, L);
// pack if necessary
if TCPBuf.Head + L > TCPBuf.Size then
TCPBufferPack(TCPBuf);
// buffer should now be large enough for new data
H := TCPBuf.Head;
Assert(TCPBuf.Size > 0);
Assert(H + L <= TCPBuf.Size);
// get buffer pointer
Assert(Assigned(TCPBuf.Ptr));
P := TCPBuf.Ptr;
Inc(P, H);
Inc(P, U);
Result := P;
end;
// Increases data used in buffer by Size.
// TCPBufferAdded should only be called in conjuction with TCPBufferAddPtr.
procedure TCPBufferAdded(
var TCPBuf: TTCPBuffer;
const Size: Int32);
begin
if Size <= 0 then
exit;
Assert(TCPBuf.Head + TCPBuf.Used + Size <= TCPBuf.Size);
Inc(TCPBuf.Used, Size);
end;
// Adds new data from a buffer to a TCP buffer
procedure TCPBufferAddBuf(
var TCPBuf: TTCPBuffer;
const Buf; const Size: Int32); {$IFDEF UseInline}inline;{$ENDIF}
var
P : PByte;
begin
if Size <= 0 then
exit;
// get TCP buffer pointer
P := TCPBufferAddPtr(TCPBuf, Size);
// move user buffer to TCP buffer
Assert(Assigned(P));
Move(Buf, P^, Size);
Inc(TCPBuf.Used, Size);
Assert(TCPBuf.Head + TCPBuf.Used <= TCPBuf.Size);
end;
// Peek TCP buffer
// Returns the number of bytes available to peek
function TCPBufferPeekPtr(
const TCPBuf: TTCPBuffer;
var BufPtr: Pointer): Int32; {$IFDEF UseInline}inline;{$ENDIF}
var
P : PByte;
L : Int32;
begin
// handle empty TCP buffer
L := TCPBuf.Used;
if L <= 0 then
begin
BufPtr := nil;
Result := 0;
exit;
end;
// get buffer pointer
Assert(TCPBuf.Head + L <= TCPBuf.Size);
Assert(Assigned(TCPBuf.Ptr));
P := TCPBuf.Ptr;
Inc(P, TCPBuf.Head);
BufPtr := P;
// return size
Result := L;
end;
// Peek data from a TCP buffer
// Returns the number of bytes actually available and copied into the buffer
function TCPBufferPeek(
var TCPBuf: TTCPBuffer;
var Buf; const Size: Int32): Int32; {$IFDEF UseInline}inline;{$ENDIF}
var
P : Pointer;
L : Int32;
begin
// handle peeking zero bytes
if Size <= 0 then
begin
Result := 0;
exit;
end;
L := TCPBufferPeekPtr(TCPBuf, P);
// peek from TCP buffer
if L > Size then
L := Size;
Move(P^, Buf, L);
Result := L;
end;
// Peek byte from a TCP buffer
// Returns True if a byte is available
function TCPBufferPeekByte(
var TCPBuf: TTCPBuffer;
out B: Byte): Boolean;
var
P : Pointer;
L : Int32;
begin
L := TCPBufferPeekPtr(TCPBuf, P);
// peek from TCP buffer
if L = 0 then
Result := False
else
begin
B := PByte(P)^;
Result := True;
end;
end;
// Remove data from a TCP buffer
// Returns the number of bytes actually available and copied into the user buffer
function TCPBufferRemove(
var TCPBuf: TTCPBuffer;
var Buf; const Size: Int32): Int32; {$IFDEF UseInline}inline;{$ENDIF}
var
L, H, U : Int32;
begin
// peek data from buffer
L := TCPBufferPeek(TCPBuf, Buf, Size);
if L = 0 then
begin
Result := 0;
exit;
end;
// remove from TCP buffer
H := TCPBuf.Head;
U := TCPBuf.Used;
Dec(U, L);
if U = 0 then
H := 0
else
Inc(H, L);
TCPBuf.Head := H;
TCPBuf.Used := U;
TCPBufferMinimize(TCPBuf);
Result := L;
end;
// Remove data from a TCP buffer
// Returns True if Size bytes were available and copied into the user buffer
// Returns False if Size bytes were not available
function TCPBufferRemoveBuf(
var TCPBuf: TTCPBuffer;
var Buf; const Size: Int32): Boolean; {$IFDEF UseInline}inline;{$ENDIF}
var
H, U : Int32;
P : PByte;
begin
// handle invalid size
if Size <= 0 then
begin
Result := False;
exit;
end;
// check if enough data available
U := TCPBuf.Used;
if U < Size then
begin
Result := False;
exit;
end;
// get buffer
H := TCPBuf.Head;
Assert(H + Size <= TCPBuf.Size);
P := TCPBuf.Ptr;
Assert(Assigned(P));
Inc(P, H);
Move(P^, Buf, Size);
// remove from TCP buffer
Dec(U, Size);
if U = 0 then
H := 0
else
Inc(H, Size);
TCPBuf.Head := H;
TCPBuf.Used := U;
TCPBufferMinimize(TCPBuf);
Result := True;
end;
// Discard a number of bytes from the TCP buffer
// Returns the number of bytes actually discarded from buffer
function TCPBufferDiscard(
var TCPBuf: TTCPBuffer;
const Size: Int32): Int32; {$IFDEF UseInline}inline;{$ENDIF}
var
L, U : Int32;
begin
// handle discarding zero bytes from buffer
L := Size;
if L <= 0 then
begin
Result := 0;
exit;
end;
// handle discarding the complete buffer
U := TCPBuf.Used;
if L >= U then
begin
TCPBuf.Used := 0;
TCPBuf.Head := 0;
TCPBufferMinimize(TCPBuf);
Result := U;
exit;
end;
// discard partial buffer
Inc(TCPBuf.Head, L);
Dec(U, L);
TCPBuf.Used := U;
TCPBufferMinimize(TCPBuf);
Result := L;
end;
// Returns number of bytes used in TCP buffer
function TCPBufferUsed(const TCPBuf: TTCPBuffer): Int32; {$IFDEF UseInline}inline;{$ENDIF}
begin
Result := TCPBuf.Used;
end;
function TCPBufferEmpty(const TCPBuf: TTCPBuffer): Boolean; {$IFDEF UseInline}inline;{$ENDIF}
begin
Result := TCPBuf.Used = 0;
end;
// Returns number of bytes available in TCP buffer
// Note: this function can return a negative number if the TCP buffer uses more bytes than set in Max
function TCPBufferAvailable(const TCPBuf: TTCPBuffer): Int32; {$IFDEF UseInline}inline;{$ENDIF}
begin
Result := TCPBuf.Max - TCPBuf.Used;
end;
// Returns pointer to TCP buffer head
function TCPBufferPtr(const TCPBuf: TTCPBuffer): Pointer; {$IFDEF UseInline}inline;{$ENDIF}
var
P : PByte;
begin
Assert(Assigned(TCPBuf.Ptr));
P := TCPBuf.Ptr;
Inc(P, TCPBuf.Head);
Result := P;
end;
// LocateByteChar
// Returns position of Delimiter in buffer
// Returns >= 0 if found in buffer
// Returns -1 if not found in buffer
// MaxSize specifies maximum bytes before delimiter, of -1 for no limit
function TCPBufferLocateByteChar(const TCPBuf: TTCPBuffer;
const Delimiter: ByteCharSet; const MaxSize: Integer): Int32;
var
BufSize : Int32;
LocLen : Int32;
BufPtr : PByteChar;
I : Int32;
begin
if MaxSize = 0 then
begin
Result := -1;
exit;
end;
BufSize := TCPBuf.Used;
if BufSize <= 0 then
begin
Result := -1;
exit;
end;
if MaxSize < 0 then
LocLen := BufSize
else
if BufSize < MaxSize then
LocLen := BufSize
else
LocLen := MaxSize;
BufPtr := TCPBufferPtr(TCPBuf);
for I := 0 to LocLen - 1 do
if BufPtr^ in Delimiter then
begin
Result := I;
exit;
end
else
Inc(BufPtr);
Result := -1;
end;
end.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,175 @@
{******************************************************************************}
{ }
{ Library: Fundamentals 5.00 }
{ File name: flcTCPUtils.pas }
{ File version: 5.01 }
{ Description: TCP utilities. }
{ }
{ Copyright: Copyright (c) 2007-2020, David J Butler }
{ All rights reserved. }
{ This file is licensed under the BSD License. }
{ See http://www.opensource.org/licenses/bsd-license.php }
{ Redistribution and use in source and binary forms, with }
{ or without modification, are permitted provided that }
{ the following conditions are met: }
{ Redistributions of source code must retain the above }
{ copyright notice, this list of conditions and the }
{ following disclaimer. }
{ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND }
{ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED }
{ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED }
{ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A }
{ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL }
{ THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, }
{ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR }
{ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, }
{ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF }
{ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) }
{ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER }
{ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING }
{ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE }
{ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE }
{ POSSIBILITY OF SUCH DAMAGE. }
{ }
{ Github: https://github.com/fundamentalslib }
{ E-mail: fundamentals.library at gmail.com }
{ }
{ Revision history: }
{ }
{ 2020/05/20 0.01 Initial version from unit flcTCPConnection. }
{ TCP timers helpers. TCP CompareMem helper. }
{ }
{******************************************************************************}
{$INCLUDE ../flcInclude.inc}
{$INCLUDE flcTCP.inc}
unit flcTCPUtils;
interface
uses
{ Fundamentals }
flcStdTypes;
{ }
{ TCP timer helpers }
{ }
function TCPGetTick: Word64;
function TCPTickDelta(const D1, D2: Word64): Int64;
function TCPTickDeltaU(const D1, D2: Word64): Word64;
{ }
{ TCP CompareMem helper }
{ }
function TCPCompareMem(const Buf1; const Buf2; const Count: Integer): Boolean;
implementation
uses
{ Fundamentals }
flcTimers;
{ }
{ TCP timer helpers }
{ }
function TCPGetTick: Word64;
begin
Result := GetMilliTick;
end;
function TCPTickDelta(const D1, D2: Word64): Int64;
begin
Result := MilliTickDelta(D1, D2);
end;
function TCPTickDeltaU(const D1, D2: Word64): Word64;
begin
Result := MilliTickDeltaU(D1, D2);
end;
{ }
{ TCP CompareMem helper }
{ }
{$IFDEF ASM386_DELPHI}
function TCPCompareMem(const Buf1; const Buf2; const Count: Integer): Boolean;
asm
// EAX = Buf1, EDX = Buf2, ECX = Count
OR ECX, ECX
JLE @Fin1
CMP EAX, EDX
JE @Fin1
PUSH ESI
PUSH EDI
MOV ESI, EAX
MOV EDI, EDX
MOV EDX, ECX
SHR ECX, 2
XOR EAX, EAX
REPE CMPSD
JNE @Fin0
MOV ECX, EDX
AND ECX, 3
REPE CMPSB
JNE @Fin0
INC EAX
@Fin0:
POP EDI
POP ESI
RET
@Fin1:
MOV AL, 1
end;
{$ELSE}
function TCPCompareMem(const Buf1; const Buf2; const Count: Integer): Boolean;
var P, Q : Pointer;
D, I : Integer;
begin
P := @Buf1;
Q := @Buf2;
if (Count <= 0) or (P = Q) then
begin
Result := True;
exit;
end;
D := Word32(Count) div 4;
for I := 1 to D do
if PWord32(P)^ = PWord32(Q)^ then
begin
Inc(PWord32(P));
Inc(PWord32(Q));
end
else
begin
Result := False;
exit;
end;
D := Word32(Count) and 3;
for I := 1 to D do
if PByte(P)^ = PByte(Q)^ then
begin
Inc(PByte(P));
Inc(PByte(Q));
end
else
begin
Result := False;
exit;
end;
Result := True;
end;
{$ENDIF}
end.