443 lines
12 KiB
Plaintext
443 lines
12 KiB
Plaintext
|
{
|
||
|
$Project$
|
||
|
$Workfile$
|
||
|
$Revision$
|
||
|
$DateUTC$
|
||
|
$Id$
|
||
|
|
||
|
This file is part of the Indy (Internet Direct) project, and is offered
|
||
|
under the dual-licensing agreement described on the Indy website.
|
||
|
(http://www.indyproject.org/)
|
||
|
|
||
|
Copyright:
|
||
|
(c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
|
||
|
}
|
||
|
{
|
||
|
$Log$
|
||
|
}
|
||
|
{
|
||
|
Rev 1.53 3/10/05 3:23:16 PM RLebeau
|
||
|
Updated WriteDirect() to access the Intercept property directly.
|
||
|
|
||
|
Rev 1.52 11/12/2004 11:30:16 AM JPMugaas
|
||
|
Expansions for IPv6.
|
||
|
|
||
|
Rev 1.51 11/11/04 12:03:46 PM RLebeau
|
||
|
Updated DoConnectTimeout() to recognize IdTimeoutDefault
|
||
|
|
||
|
Rev 1.50 6/18/04 1:06:58 PM RLebeau
|
||
|
Bug fix for ReadTimeout property
|
||
|
|
||
|
Rev 1.49 5/4/2004 9:57:34 AM JPMugaas
|
||
|
Removed some old uncommented code and reenabled some TransparentProxy code
|
||
|
since it compile in DotNET.
|
||
|
|
||
|
Rev 1.48 2004.04.18 12:52:02 AM czhower
|
||
|
Big bug fix with server disconnect and several other bug fixed that I found
|
||
|
along the way.
|
||
|
|
||
|
Rev 1.47 2004.04.08 3:56:34 PM czhower
|
||
|
Fixed bug with Intercept byte count. Also removed Bytes from Buffer.
|
||
|
|
||
|
Rev 1.46 2004.03.12 8:01:00 PM czhower
|
||
|
Exception update
|
||
|
|
||
|
Rev 1.45 2004.03.07 11:48:42 AM czhower
|
||
|
Flushbuffer fix + other minor ones found
|
||
|
|
||
|
Rev 1.44 2004.03.01 5:12:32 PM czhower
|
||
|
-Bug fix for shutdown of servers when connections still existed (AV)
|
||
|
-Implicit HELP support in CMDserver
|
||
|
-Several command handler bugs
|
||
|
-Additional command handler functionality.
|
||
|
|
||
|
Rev 1.43 2/21/04 9:25:50 PM RLebeau
|
||
|
Fix for BBG #66
|
||
|
|
||
|
Added FLastSocketError member to TIdConnectThread
|
||
|
|
||
|
Rev 1.42 2004.02.03 4:16:48 PM czhower
|
||
|
For unit name changes.
|
||
|
|
||
|
Rev 1.41 12/31/2003 9:51:56 PM BGooijen
|
||
|
Added IPv6 support
|
||
|
|
||
|
Rev 1.40 2003.12.28 1:05:58 PM czhower
|
||
|
.Net changes.
|
||
|
|
||
|
Rev 1.39 11/21/2003 12:05:18 AM BGooijen
|
||
|
Terminated isn't public in TThread any more, made it public here now
|
||
|
|
||
|
Rev 1.38 10/28/2003 9:15:44 PM BGooijen
|
||
|
.net
|
||
|
|
||
|
Rev 1.37 10/18/2003 1:42:46 PM BGooijen
|
||
|
Added include
|
||
|
|
||
|
Rev 1.36 2003.10.14 1:26:56 PM czhower
|
||
|
Uupdates + Intercept support
|
||
|
|
||
|
Rev 1.35 2003.10.11 5:48:36 PM czhower
|
||
|
-VCL fixes for servers
|
||
|
-Chain suport for servers (Super core)
|
||
|
-Scheduler upgrades
|
||
|
-Full yarn support
|
||
|
|
||
|
Rev 1.34 10/9/2003 8:09:10 PM SPerry
|
||
|
bug fixes
|
||
|
|
||
|
Rev 1.33 10/5/2003 11:02:36 PM BGooijen
|
||
|
Write buffering
|
||
|
|
||
|
Rev 1.32 05/10/2003 23:01:02 HHariri
|
||
|
Fix for connect problem when IP address specified as opposed to host
|
||
|
|
||
|
Rev 1.31 2003.10.02 8:23:42 PM czhower
|
||
|
DotNet Excludes
|
||
|
|
||
|
Rev 1.30 2003.10.02 10:16:28 AM czhower
|
||
|
.Net
|
||
|
|
||
|
Rev 1.29 2003.10.01 9:11:18 PM czhower
|
||
|
.Net
|
||
|
|
||
|
Rev 1.28 2003.10.01 5:05:14 PM czhower
|
||
|
.Net
|
||
|
|
||
|
Rev 1.27 2003.10.01 2:46:38 PM czhower
|
||
|
.Net
|
||
|
|
||
|
Rev 1.26 2003.10.01 2:30:38 PM czhower
|
||
|
.Net
|
||
|
|
||
|
Rev 1.22 10/1/2003 12:14:14 AM BGooijen
|
||
|
DotNet: removing CheckForSocketError
|
||
|
|
||
|
Rev 1.21 2003.10.01 1:37:34 AM czhower
|
||
|
.Net
|
||
|
|
||
|
Rev 1.19 2003.09.30 1:22:58 PM czhower
|
||
|
Stack split for DotNet
|
||
|
|
||
|
Rev 1.18 2003.07.14 1:57:22 PM czhower
|
||
|
-First set of IOCP fixes.
|
||
|
-Fixed a threadsafe problem with the stack class.
|
||
|
|
||
|
Rev 1.17 2003.07.14 12:54:32 AM czhower
|
||
|
Fixed graceful close detection if it occurs after connect.
|
||
|
|
||
|
Rev 1.16 2003.07.10 4:34:58 PM czhower
|
||
|
Fixed AV, added some new comments
|
||
|
|
||
|
Rev 1.15 7/4/2003 08:26:46 AM JPMugaas
|
||
|
Optimizations.
|
||
|
|
||
|
Rev 1.14 7/1/2003 03:39:48 PM JPMugaas
|
||
|
Started numeric IP function API calls for more efficiency.
|
||
|
|
||
|
Rev 1.13 6/30/2003 10:25:18 AM BGooijen
|
||
|
removed unnecessary assignment to FRecvBuffer.Size
|
||
|
|
||
|
Rev 1.12 6/29/2003 10:56:28 PM BGooijen
|
||
|
Removed .Memory from the buffer, and added some extra methods
|
||
|
|
||
|
Rev 1.11 2003.06.25 4:28:32 PM czhower
|
||
|
Formatting and fixed a short circuit clause.
|
||
|
|
||
|
Rev 1.10 6/3/2003 11:43:52 PM BGooijen
|
||
|
Elimintated some code
|
||
|
|
||
|
Rev 1.9 4/16/2003 3:31:26 PM BGooijen
|
||
|
Removed InternalCheckForDisconnect, added .Connected
|
||
|
|
||
|
Rev 1.8 4/14/2003 11:44:20 AM BGooijen
|
||
|
CheckForDisconnect calls ReadFromSource now
|
||
|
|
||
|
Rev 1.7 4/2/2003 3:24:56 PM BGooijen
|
||
|
Moved transparantproxy from ..stack to ..socket
|
||
|
|
||
|
Rev 1.6 3/5/2003 11:04:32 PM BGooijen
|
||
|
Fixed Intercept, but the part in WriteBuffer doesn't look really nice yet
|
||
|
|
||
|
Rev 1.5 3/3/2003 11:31:58 PM BGooijen
|
||
|
fixed stack overflow in .CheckForDisconnect
|
||
|
|
||
|
Rev 1.4 2/26/2003 1:15:40 PM BGooijen
|
||
|
FBinding is now freed in IdIOHandlerSocket, instead of in IdIOHandlerStack
|
||
|
|
||
|
Rev 1.3 2003.02.25 1:36:12 AM czhower
|
||
|
|
||
|
Rev 1.2 2002.12.06 11:49:34 PM czhower
|
||
|
|
||
|
Rev 1.1 12-6-2002 20:10:18 BGooijen
|
||
|
Added IPv6-support
|
||
|
|
||
|
Rev 1.0 11/13/2002 08:45:16 AM JPMugaas
|
||
|
}
|
||
|
|
||
|
unit IdIOHandlerStack;
|
||
|
|
||
|
interface
|
||
|
|
||
|
{$i IdCompilerDefines.inc}
|
||
|
|
||
|
uses
|
||
|
Classes,
|
||
|
IdGlobal, IdSocketHandle, IdIOHandlerSocket, IdExceptionCore, IdStack,
|
||
|
SysUtils;
|
||
|
|
||
|
type
|
||
|
TIdIOHandlerStack = class(TIdIOHandlerSocket)
|
||
|
protected
|
||
|
procedure ConnectClient; override;
|
||
|
function ReadDataFromSource(var VBuffer: TIdBytes): Integer; override;
|
||
|
function WriteDataToTarget(const ABuffer: TIdBytes; const AOffset, ALength: Integer): Integer; override;
|
||
|
public
|
||
|
procedure CheckForDisconnect(ARaiseExceptionIfDisconnected: Boolean = True;
|
||
|
AIgnoreBuffer: Boolean = False); override;
|
||
|
function Connected: Boolean; override;
|
||
|
function Readable(AMSec: Integer = IdTimeoutDefault): Boolean; override;
|
||
|
published
|
||
|
property ReadTimeout;
|
||
|
end;
|
||
|
|
||
|
implementation
|
||
|
|
||
|
uses
|
||
|
{$IFDEF USE_VCL_POSIX}
|
||
|
Posix.SysSelect,
|
||
|
Posix.SysTime,
|
||
|
{$ENDIF}
|
||
|
IdAntiFreezeBase, IdResourceStringsCore, IdResourceStrings, IdStackConsts, IdException,
|
||
|
IdTCPConnection, IdComponent, IdIOHandler;
|
||
|
|
||
|
type
|
||
|
TIdConnectThread = class(TThread)
|
||
|
protected
|
||
|
FBinding: TIdSocketHandle;
|
||
|
FLastSocketError: Integer;
|
||
|
FExceptionMessage: string;
|
||
|
FExceptionOccured: Boolean;
|
||
|
procedure Execute; override;
|
||
|
procedure DoTerminate; override;
|
||
|
public
|
||
|
constructor Create(ABinding: TIdSocketHandle); reintroduce;
|
||
|
property Terminated;
|
||
|
end;
|
||
|
|
||
|
{ TIdIOHandlerStack }
|
||
|
|
||
|
function TIdIOHandlerStack.Connected: Boolean;
|
||
|
begin
|
||
|
ReadFromSource(False, 0, False);
|
||
|
Result := inherited Connected;
|
||
|
end;
|
||
|
|
||
|
procedure TIdIOHandlerStack.ConnectClient;
|
||
|
|
||
|
procedure DoConnectTimeout(ATimeout: Integer);
|
||
|
var
|
||
|
LSleepTime: Integer;
|
||
|
LThread: TIdConnectThread;
|
||
|
begin
|
||
|
if ATimeout = IdTimeoutDefault then begin
|
||
|
ATimeout := IdTimeoutInfinite;
|
||
|
end;
|
||
|
LThread := TIdConnectThread.Create(Binding);
|
||
|
try
|
||
|
// IndySleep
|
||
|
if TIdAntiFreezeBase.ShouldUse then begin
|
||
|
LSleepTime := IndyMin(GAntiFreeze.IdleTimeOut, 125);
|
||
|
end else begin
|
||
|
LSleepTime := 125;
|
||
|
end;
|
||
|
|
||
|
if ATimeout = IdTimeoutInfinite then begin
|
||
|
while not LThread.Terminated do begin
|
||
|
IndySleep(LSleepTime);
|
||
|
TIdAntiFreezeBase.DoProcess;
|
||
|
end;
|
||
|
end else
|
||
|
begin
|
||
|
// TODO: we need to take the actual clock into account, not just
|
||
|
// decrement by the sleep interval. If IndySleep() runs longer then
|
||
|
// requested, that would slow down the loop and exceed the original
|
||
|
// timeout that was requested...
|
||
|
while (ATimeout > 0) and (not LThread.Terminated) do begin
|
||
|
IndySleep(IndyMin(ATimeout, LSleepTime));
|
||
|
TIdAntiFreezeBase.DoProcess;
|
||
|
Dec(ATimeout, IndyMin(ATimeout, LSleepTime));
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
if LThread.Terminated then begin
|
||
|
if LThread.FExceptionOccured then begin
|
||
|
// TODO: acquire the actual Exception object from TIdConnectThread and re-raise it here
|
||
|
if LThread.FLastSocketError <> 0 then begin
|
||
|
raise EIdSocketError.CreateError(LThread.FLastSocketError, LThread.FExceptionMessage);
|
||
|
end;
|
||
|
raise EIdConnectException.Create(LThread.FExceptionMessage);
|
||
|
end;
|
||
|
end else begin
|
||
|
LThread.Terminate;
|
||
|
Close;
|
||
|
LThread.WaitFor;
|
||
|
raise EIdConnectTimeout.Create(RSConnectTimeout);
|
||
|
end;
|
||
|
finally
|
||
|
LThread.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
var
|
||
|
LHost: String;
|
||
|
LPort: Integer;
|
||
|
LIP: string;
|
||
|
LIPVersion : TIdIPVersion;
|
||
|
begin
|
||
|
inherited ConnectClient;
|
||
|
if Assigned(FTransparentProxy) then begin
|
||
|
if FTransparentProxy.Enabled then begin
|
||
|
LHost := FTransparentProxy.Host;
|
||
|
LPort := FTransparentProxy.Port;
|
||
|
LIPVersion := FTransparentProxy.IPVersion;
|
||
|
end else begin
|
||
|
LHost := Host;
|
||
|
LPort := Port;
|
||
|
LIPVersion := IPVersion;
|
||
|
end;
|
||
|
end else begin
|
||
|
LHost := Host;
|
||
|
LPort := Port;
|
||
|
LIPVersion := IPVersion;
|
||
|
end;
|
||
|
if LIPVersion = Id_IPv4 then
|
||
|
begin
|
||
|
if not GStack.IsIP(LHost) then begin
|
||
|
if Assigned(OnStatus) then begin
|
||
|
DoStatus(hsResolving, [LHost]);
|
||
|
end;
|
||
|
LIP := GStack.ResolveHost(LHost, LIPVersion);
|
||
|
end else begin
|
||
|
LIP := LHost;
|
||
|
end;
|
||
|
end
|
||
|
else
|
||
|
begin //IPv6
|
||
|
LIP := MakeCanonicalIPv6Address(LHost);
|
||
|
if LIP='' then begin //if MakeCanonicalIPv6Address failed, we have a hostname
|
||
|
if Assigned(OnStatus) then begin
|
||
|
DoStatus(hsResolving, [LHost]);
|
||
|
end;
|
||
|
LIP := GStack.ResolveHost(LHost, LIPVersion);
|
||
|
end else begin
|
||
|
LIP := LHost;
|
||
|
end;
|
||
|
end;
|
||
|
Binding.SetPeer(LIP, LPort, LIPVersion);
|
||
|
// Connect
|
||
|
//note for status events, we check specifically for them here
|
||
|
//so we don't do a string conversion in Binding.PeerIP.
|
||
|
if Assigned(OnStatus) then begin
|
||
|
DoStatus(hsConnecting, [Binding.PeerIP]);
|
||
|
end;
|
||
|
|
||
|
if ConnectTimeout = 0 then begin
|
||
|
if TIdAntiFreezeBase.ShouldUse then begin
|
||
|
DoConnectTimeout(120000); // 2 Min
|
||
|
end else begin
|
||
|
Binding.Connect;
|
||
|
end;
|
||
|
end else begin
|
||
|
DoConnectTimeout(ConnectTimeout);
|
||
|
end;
|
||
|
if Assigned(FTransparentProxy) then begin
|
||
|
if FTransparentProxy.Enabled then begin
|
||
|
FTransparentProxy.Connect(Self, Host, Port, IPVersion);
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIdIOHandlerStack.Readable(AMSec: integer): boolean;
|
||
|
begin
|
||
|
Result := Binding.Readable(AMSec);
|
||
|
end;
|
||
|
|
||
|
function TIdIOHandlerStack.WriteDataToTarget(const ABuffer: TIdBytes; const AOffset, ALength: Integer): Integer;
|
||
|
begin
|
||
|
Assert(Binding<>nil);
|
||
|
Result := Binding.Send(ABuffer, AOffset, ALength);
|
||
|
end;
|
||
|
|
||
|
// Reads any data in tcp/ip buffer and puts it into Indy buffer
|
||
|
// This must be the ONLY raw read from Winsock routine
|
||
|
// This must be the ONLY call to RECV - all data goes thru this method
|
||
|
function TIdIOHandlerStack.ReadDataFromSource(var VBuffer: TIdBytes): Integer;
|
||
|
begin
|
||
|
Assert(Binding<>nil);
|
||
|
Result := Binding.Receive(VBuffer);
|
||
|
end;
|
||
|
|
||
|
procedure TIdIOHandlerStack.CheckForDisconnect(
|
||
|
ARaiseExceptionIfDisconnected: Boolean; AIgnoreBuffer: Boolean);
|
||
|
var
|
||
|
LDisconnected: Boolean;
|
||
|
begin
|
||
|
// ClosedGracefully // Server disconnected
|
||
|
// IOHandler = nil // Client disconnected
|
||
|
if ClosedGracefully then begin
|
||
|
if BindingAllocated then begin
|
||
|
Close;
|
||
|
// Call event handlers to inform the user that we were disconnected
|
||
|
DoStatus(hsDisconnected);
|
||
|
//DoOnDisconnected;
|
||
|
end;
|
||
|
LDisconnected := True;
|
||
|
end else begin
|
||
|
LDisconnected := not BindingAllocated;
|
||
|
end;
|
||
|
// Do not raise unless all data has been read by the user
|
||
|
if LDisconnected then begin
|
||
|
if (InputBufferIsEmpty or AIgnoreBuffer) and ARaiseExceptionIfDisconnected then begin
|
||
|
RaiseConnClosedGracefully;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
{ TIdConnectThread }
|
||
|
|
||
|
constructor TIdConnectThread.Create(ABinding: TIdSocketHandle);
|
||
|
begin
|
||
|
FBinding := ABinding;
|
||
|
inherited Create(False);
|
||
|
end;
|
||
|
|
||
|
procedure TIdConnectThread.Execute;
|
||
|
begin
|
||
|
try
|
||
|
FBinding.Connect;
|
||
|
except
|
||
|
on E: Exception do begin
|
||
|
// TODO: acquire the actual Exception object and re-raise it in TIdIOHandlerStack.ConnectClient()
|
||
|
FExceptionOccured := True;
|
||
|
FExceptionMessage := E.Message;
|
||
|
if E is EIdSocketError then begin
|
||
|
if (EIdSocketError(E).LastError <> Id_WSAEBADF) and (EIdSocketError(E).LastError <> Id_WSAENOTSOCK) then begin
|
||
|
FLastSocketError := EIdSocketError(E).LastError;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdConnectThread.DoTerminate;
|
||
|
begin
|
||
|
// Necessary as caller checks this
|
||
|
Terminate;
|
||
|
inherited;
|
||
|
end;
|
||
|
|
||
|
initialization
|
||
|
TIdIOHandlerStack.SetDefaultClass;
|
||
|
end.
|