| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541 |
- {
- $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}
- {$IFDEF WINDOWS}
- Windows,
- {$ENDIF}
- IdAntiFreezeBase, IdResourceStringsCore, IdStackConsts, IdException,
- IdComponent, IdIOHandler, IdCustomTransparentProxy;
- type
- TIdConnectThread = class(TThread)
- protected
- FBinding: TIdSocketHandle;
- {$IFDEF HAS_AcquireExceptionObject}
- FConnectException: TObject;
- {$ELSE}
- FLastSocketError: Integer;
- FExceptionMessage: string;
- {$ENDIF}
- FExceptionOccured: Boolean;
- procedure Execute; override;
- procedure DoTerminate; override;
- public
- constructor Create(ABinding: TIdSocketHandle); reintroduce;
- {$IFDEF HAS_AcquireExceptionObject}
- destructor Destroy; override;
- {$ENDIF}
- procedure CheckForConnectError;
- property Terminated;
- end;
- { TIdIOHandlerStack }
- function TIdIOHandlerStack.Connected: Boolean;
- begin
- try
- ReadFromSource(False, 0, False);
- Result := inherited Connected;
- except
- on E: EIdSocketError do begin
- if not ((E.LastError = Id_WSAESHUTDOWN) or (E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET)) then begin
- raise;
- end;
- Result := False;
- end;
- end;
- end;
- procedure TIdIOHandlerStack.ConnectClient;
- procedure DoConnectTimeout(ATimeout: Integer);
- var
- LSleepTime, LWaitTime: Integer;
- LThread: TIdConnectThread;
- begin
- // IndySleep
- if TIdAntiFreezeBase.ShouldUse then begin
- LSleepTime := IndyMin(GAntiFreeze.IdleTimeOut, 125);
- end else begin
- LSleepTime := 125;
- end;
- LThread := TIdConnectThread.Create(Binding);
- try
- if TIdAntiFreezeBase.ShouldUse then 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...
- {
- Start := Ticks64;
- repeat
- while (GetElapsedTicks(Start) < ATimeout) and (not LThread.Terminated) do begin
- LWaitTime := IndyMin(ATimeout - GetElapsedTicks(Start), LSleepTime);
- if LWaitTime <= 0 then Break;
- ($IFDEF WINDOWS)
- if WaitForSingleObject(LThread.Handle, LWaitTime) <> WAIT_TIMEOUT then begin
- Break;
- end;
- ($ELSE)
- // TODO: figure out what else can be used here...
- IndySleep(LWaitTime);
- ($ENDIF)
- TIdAntiFreezeBase.DoProcess;
- end;
- end;
- }
- while (ATimeout > 0) and (not LThread.Terminated) do begin
- LWaitTime := IndyMin(ATimeout, LSleepTime);
- {$IFDEF WINDOWS}
- if WaitForSingleObject(LThread.Handle, LWaitTime) <> WAIT_TIMEOUT then begin
- Break;
- end;
- {$ELSE}
- // TODO: figure out what else can be used here...
- IndySleep(LWaitTime);
- {$ENDIF}
- TIdAntiFreezeBase.DoProcess;
- Dec(ATimeout, LWaitTime);
- end;
- end else begin
- {$IFDEF WINDOWS}
- WaitForSingleObject(LThread.Handle, ATimeout);
- {$ELSE}
- // TODO: figure out what else can be used here...
- while (ATimeout > 0) and (not LThread.Terminated) do begin
- LWaitTime := IndyMin(ATimeout, LSleepTime);
- IndySleep(LWaitTime);
- Dec(ATimeout, LWaitTime);
- end;
- {$ENDIF}
- end;
- if LThread.Terminated then begin
- LThread.CheckForConnectError;
- end else begin
- LThread.Terminate;
- // TODO: before closing, maybe enable SO_DONTLINGER, or SO_LINGER with a 0 timeout...
- //Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_DONTLINGER, 1);
- {
- var l: linger;
- l.l_onoff := 1;
- l.l_linger := 0;
- Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_LINGER, Integer(@l));
- }
- Close;
- LThread.WaitFor;
- raise EIdConnectTimeout.Create(RSConnectTimeout);
- end;
- finally
- LThread.Free;
- end;
- end;
- var
- LHost: String;
- LPort: Integer;
- LIP: string;
- LIPVersion : TIdIPVersion;
- // under ARC, convert a weak reference to a strong reference before working with it
- LProxy: TIdCustomTransparentProxy;
- LTimeout: Integer;
- begin
- inherited ConnectClient;
- LProxy := FTransparentProxy;
- if Assigned(LProxy) then begin
- if LProxy.Enabled then begin
- LHost := LProxy.Host;
- LPort := LProxy.Port;
- LIPVersion := LProxy.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;
- LTimeout := ConnectTimeout;
- if (LTimeout = IdTimeoutDefault) or (LTimeout = 0) then begin
- LTimeout := IdTimeoutInfinite;
- end;
- if LTimeout = IdTimeoutInfinite then begin
- if TIdAntiFreezeBase.ShouldUse then begin
- DoConnectTimeout(120000); // 2 Min
- end else begin
- Binding.Connect;
- end;
- end else begin
- DoConnectTimeout(LTimeout);
- end;
- if Assigned(LProxy) then begin
- if LProxy.Enabled then begin
- LProxy.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;
- {$IFDEF HAS_AcquireExceptionObject}
- destructor TIdConnectThread.Destroy;
- begin
- FConnectException.Free;
- inherited;
- end;
- {$ENDIF}
- procedure TIdConnectThread.Execute;
- begin
- try
- FBinding.Connect;
- except
- {$IFDEF HAS_AcquireExceptionObject}
- // TThread has a FatalException property, but we can't take ownership of it
- // so we can re-raise it, so using AcquireExceptionObject() instead to take
- // ownership of the exception before it can be assigned to FatalException...
- FExceptionOccured := True;
- FConnectException := AcquireExceptionObject;
- {$ELSE}
- on E: Exception do begin
- 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;
- {$ENDIF}
- end;
- end;
- procedure TIdConnectThread.DoTerminate;
- begin
- // Necessary as caller checks this
- Terminate;
- inherited;
- end;
- procedure TIdConnectThread.CheckForConnectError;
- var
- LException: TObject;
- begin
- if FExceptionOccured then begin
- {$IFDEF HAS_AcquireExceptionObject}
- LException := FConnectException;
- FConnectException := nil;
- if LException = nil then begin
- LException := EIdConnectException.Create(''); // TODO
- end;
- {$ELSE}
- if FLastSocketError <> 0 then begin
- LException := EIdSocketError.CreateError(FLastSocketError, FExceptionMessage);
- end else begin
- LException := EIdConnectException.Create(FExceptionMessage);
- end;
- {$ENDIF}
- raise LException;
- end;
- end;
- initialization
- TIdIOHandlerStack.SetDefaultClass;
- end.
|