restemplate/indy/Core/IdIOHandlerSocket.pas

576 lines
17 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.38 11/10/2004 8:25:54 AM JPMugaas
Fix for AV caused by short-circut boolean evaluation.
Rev 1.37 27.08.2004 21:58:20 Andreas Hausladen
Speed optimization ("const" for string parameters)
Rev 1.36 8/2/04 5:44:40 PM RLebeau
Moved ConnectTimeout over from TIdIOHandlerStack
Rev 1.35 7/21/2004 12:22:32 PM BGooijen
Fix to .connected
Rev 1.34 6/30/2004 12:31:34 PM BGooijen
Added OnSocketAllocated
Rev 1.33 4/24/04 12:52:52 PM RLebeau
Added setter method to UseNagle property
Rev 1.32 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.31 2004.02.03 4:16:46 PM czhower
For unit name changes.
Rev 1.30 2/2/2004 11:46:46 AM BGooijen
Dotnet and TransparentProxy
Rev 1.29 2/1/2004 9:44:00 PM JPMugaas
Start on reenabling Transparant proxy.
Rev 1.28 2004.01.20 10:03:28 PM czhower
InitComponent
Rev 1.27 1/2/2004 12:02:16 AM BGooijen
added OnBeforeBind/OnAfterBind
Rev 1.26 12/31/2003 9:51:56 PM BGooijen
Added IPv6 support
Rev 1.25 11/4/2003 10:37:40 PM BGooijen
JP's patch to fix the bound port
Rev 1.24 10/19/2003 5:21:26 PM BGooijen
SetSocketOption
Rev 1.23 10/18/2003 1:44:06 PM BGooijen
Added include
Rev 1.22 2003.10.14 1:26:54 PM czhower
Uupdates + Intercept support
Rev 1.21 10/9/2003 8:09:06 PM SPerry
bug fixes
Rev 1.20 8/10/2003 2:05:50 PM SGrobety
Dotnet
Rev 1.19 2003.10.07 10:18:26 PM czhower
Uncommneted todo code that is now non dotnet.
Rev 1.18 2003.10.02 8:23:42 PM czhower
DotNet Excludes
Rev 1.17 2003.10.01 9:11:18 PM czhower
.Net
Rev 1.16 2003.10.01 5:05:12 PM czhower
.Net
Rev 1.15 2003.10.01 2:46:38 PM czhower
.Net
Rev 1.14 2003.10.01 11:16:32 AM czhower
.Net
Rev 1.13 2003.09.30 1:22:58 PM czhower
Stack split for DotNet
Rev 1.12 7/4/2003 08:26:44 AM JPMugaas
Optimizations.
Rev 1.11 7/1/2003 03:39:44 PM JPMugaas
Started numeric IP function API calls for more efficiency.
Rev 1.10 2003.06.30 5:41:56 PM czhower
-Fixed AV that occurred sometimes when sockets were closed with chains
-Consolidated code that was marked by a todo for merging as it no longer
needed to be separate
-Removed some older code that was no longer necessary
Passes bubble tests.
Rev 1.9 6/3/2003 11:45:58 PM BGooijen
Added .Connected
Rev 1.8 2003.04.22 7:45:34 PM czhower
Rev 1.7 4/2/2003 3:24:56 PM BGooijen
Moved transparantproxy from ..stack to ..socket
Rev 1.6 2/28/2003 9:51:56 PM BGooijen
removed the field: FReadTimeout: Integer, it hided the one in TIdIOHandler
Rev 1.5 2/26/2003 1:15:38 PM BGooijen
FBinding is now freed in IdIOHandlerSocket, instead of in IdIOHandlerStack
Rev 1.4 2003.02.25 1:36:08 AM czhower
Rev 1.3 2002.12.07 12:26:26 AM czhower
Rev 1.2 12-6-2002 20:09:14 BGooijen
Changed SetDestination to search for the last ':', instead of the first
Rev 1.1 12-6-2002 18:54:14 BGooijen
Added IPv6-support
Rev 1.0 11/13/2002 08:45:08 AM JPMugaas
}
unit IdIOHandlerSocket;
interface
{$I IdCompilerDefines.inc}
uses
Classes,
IdCustomTransparentProxy,
IdBaseComponent,
IdGlobal,
IdIOHandler,
IdSocketHandle;
const
IdDefTimeout = 0;
IdBoundPortDefault = 0;
type
{
TIdIOHandlerSocket is the base class for socket IOHandlers that implement a
binding.
Descendants
-TIdIOHandlerStack
-TIdIOHandlerChain
}
TIdIOHandlerSocket = class(TIdIOHandler)
protected
FBinding: TIdSocketHandle;
FBoundIP: string;
FBoundPort: TIdPort;
FBoundPortMax: TIdPort;
FBoundPortMin: TIdPort;
FDefaultPort: TIdPort;
FOnBeforeBind: TNotifyEvent;
FOnAfterBind: TNotifyEvent;
FOnSocketAllocated: TNotifyEvent;
{$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FTransparentProxy: TIdCustomTransparentProxy;
FImplicitTransparentProxy: Boolean;
FUseNagle: Boolean;
FReuseSocket: TIdReuseSocket;
FIPVersion: TIdIPVersion;
//
procedure ConnectClient; virtual;
procedure DoBeforeBind; virtual;
procedure DoAfterBind; virtual;
procedure DoSocketAllocated; virtual;
procedure InitComponent; override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
function GetDestination: string; override;
procedure SetDestination(const AValue: string); override;
function GetReuseSocket: TIdReuseSocket;
procedure SetReuseSocket(AValue: TIdReuseSocket);
function GetTransparentProxy: TIdCustomTransparentProxy; virtual;
procedure SetTransparentProxy(AProxy: TIdCustomTransparentProxy); virtual;
function GetUseNagle: Boolean;
procedure SetUseNagle(AValue: Boolean);
//
function SourceIsAvailable: Boolean; override;
function CheckForError(ALastResult: Integer): Integer; override;
procedure RaiseError(AError: Integer); override;
public
procedure AfterAccept; override;
destructor Destroy; override;
function BindingAllocated: Boolean;
procedure Close; override;
function Connected: Boolean; override;
procedure Open; override;
function WriteFile(const AFile: String; AEnableTransferFile: Boolean = False): Int64; override;
//
property Binding: TIdSocketHandle read FBinding;
property BoundPortMax: TIdPort read FBoundPortMax write FBoundPortMax;
property BoundPortMin: TIdPort read FBoundPortMin write FBoundPortMin;
// events
property OnBeforeBind: TNotifyEvent read FOnBeforeBind write FOnBeforeBind;
property OnAfterBind: TNotifyEvent read FOnAfterBind write FOnAfterBind;
property OnSocketAllocated: TNotifyEvent read FOnSocketAllocated write FOnSocketAllocated;
published
property BoundIP: string read FBoundIP write FBoundIP;
property BoundPort: TIdPort read FBoundPort write FBoundPort default IdBoundPortDefault;
property DefaultPort: TIdPort read FDefaultPort write FDefaultPort;
property IPVersion: TIdIPVersion read FIPVersion write FIPVersion default ID_DEFAULT_IP_VERSION;
property ReuseSocket: TIdReuseSocket read GetReuseSocket write SetReuseSocket default rsOSDependent;
property TransparentProxy: TIdCustomTransparentProxy read GetTransparentProxy write SetTransparentProxy;
property UseNagle: boolean read GetUseNagle write SetUseNagle default True;
end;
implementation
uses
//facilitate inlining only.
{$IFDEF DOTNET}
{$IFDEF USE_INLINE}
System.IO,
{$ENDIF}
{$ENDIF}
{$IFDEF WIN32_OR_WIN64 }
Windows,
{$ENDIF}
SysUtils,
IdStack,
IdStackConsts,
IdSocks;
{ TIdIOHandlerSocket }
procedure TIdIOHandlerSocket.AfterAccept;
begin
inherited AfterAccept;
FIPVersion := FBinding.IPVersion;
end;
procedure TIdIOHandlerSocket.Close;
begin
if FBinding <> nil then begin
FBinding.CloseSocket;
end;
inherited Close;
end;
procedure TIdIOHandlerSocket.ConnectClient;
var
LBinding: TIdSocketHandle;
begin
LBinding := Binding;
DoBeforeBind;
// Allocate the socket
LBinding.IPVersion := FIPVersion;
LBinding.AllocateSocket;
DoSocketAllocated;
// Bind the socket
if BoundIP <> '' then begin
LBinding.IP := BoundIP;
end;
LBinding.Port := BoundPort;
LBinding.ClientPortMin := BoundPortMin;
LBinding.ClientPortMax := BoundPortMax;
LBinding.ReuseSocket := FReuseSocket;
// RLebeau 11/15/2014: Using the socket bind() function in a Mac OSX sandbox
// causes the Apple store to reject an app with the following error if it
// uses Indy client(s) and no Indy server(s):
//
// "This app uses one or more entitlements which do not have matching
// functionality within the app. Apps should have only the minimum set of
// entitlements necessary for the app to function properly. Please remove
// all entitlements that are not needed by your app and submit an updated
// binary for review, including the following:
//
// com.apple.security.network.server"
//
// Ideally, TIdSocketHandle.Bind() should not call TryBind() if the IP is
// blank and the Port, ClientPortMin, and ClientPortMax are all 0. However,
// TIdSocketHandle.Bind() is used for both clients and servers, and sometimes
// a server needs to bind to port 0 to get a random ephemeral port, which it
// can then report to clients. So lets do the check here instead, as this
// method is only used for clients...
{$IFDEF DARWIN}
// TODO: remove the DARWIN check and just skip the Bind() on all platforms?
if (LBinding.IP <> '') or (LBinding.Port <> 0) or
((LBinding.ClientPortMin <> 0) and (LBinding.ClientPortMax <> 0)) then
begin
LBinding.Bind;
end;
{$ELSE}
LBinding.Bind;
{$ENDIF}
// Turn off Nagle if specified
LBinding.UseNagle := FUseNagle;
DoAfterBind;
end;
function TIdIOHandlerSocket.Connected: Boolean;
begin
Result := (BindingAllocated and inherited Connected) or (not InputBufferIsEmpty);
end;
destructor TIdIOHandlerSocket.Destroy;
begin
SetTransparentProxy(nil);
FreeAndNil(FBinding);
inherited Destroy;
end;
procedure TIdIOHandlerSocket.DoBeforeBind;
begin
if Assigned(FOnBeforeBind) then begin
FOnBeforeBind(self);
end;
end;
procedure TIdIOHandlerSocket.DoAfterBind;
begin
if Assigned(FOnAfterBind) then begin
FOnAfterBind(self);
end;
end;
procedure TIdIOHandlerSocket.DoSocketAllocated;
begin
if Assigned(FOnSocketAllocated) then begin
FOnSocketAllocated(self);
end;
end;
function TIdIOHandlerSocket.GetDestination: string;
begin
Result := Host;
if (Port <> DefaultPort) and (Port > 0) then begin
Result := Host + ':' + IntToStr(Port);
end;
end;
procedure TIdIOHandlerSocket.Open;
begin
inherited Open;
if not Assigned(FBinding) then begin
FBinding := TIdSocketHandle.Create(nil);
end else begin
FBinding.Reset(True);
end;
FBinding.ClientPortMin := BoundPortMin;
FBinding.ClientPortMax := BoundPortMax;
//if the IOHandler is used to accept connections then port+host will be empty
if (Host <> '') and (Port > 0) then begin
ConnectClient;
end;
end;
procedure TIdIOHandlerSocket.SetDestination(const AValue: string);
var
LPortStart: integer;
begin
// Bas Gooijen 06-Dec-2002: Changed to search the last ':', instead of the first:
LPortStart := LastDelimiter(':', AValue);
if LPortStart > 0 then begin
Host := Copy(AValue, 1, LPortStart-1);
Port := IndyStrToInt(Trim(Copy(AValue, LPortStart + 1, $FF)), DefaultPort);
end;
end;
function TIdIOHandlerSocket.BindingAllocated: Boolean;
begin
Result := FBinding <> nil;
if Result then begin
Result := FBinding.HandleAllocated;
end;
end;
function TIdIOHandlerSocket.WriteFile(const AFile: String;
AEnableTransferFile: Boolean): Int64;
{$IFDEF WIN32_OR_WIN64}
var
LOldErrorMode : Integer;
{$ENDIF}
begin
Result := 0;
{$IFDEF WIN32_OR_WIN64}
LOldErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);
try
{$ENDIF}
if FileExists(AFile) then begin
if Assigned(GServeFileProc) and (not WriteBufferingActive)
{and (Intercept = nil)} and AEnableTransferFile
then begin
Result := GServeFileProc(Binding.Handle, AFile);
Exit;
end
else
begin
Result := inherited WriteFile(AFile, AEnableTransferFile);
end;
end;
{$IFDEF WIN32_OR_WIN64}
finally
SetErrorMode(LOldErrorMode)
end;
{$ENDIF}
end;
function TIdIOHandlerSocket.GetReuseSocket: TIdReuseSocket;
begin
if FBinding <> nil then begin
Result := FBinding.ReuseSocket;
end else begin
Result := FReuseSocket;
end;
end;
procedure TIdIOHandlerSocket.SetReuseSocket(AValue: TIdReuseSocket);
begin
FReuseSocket := AValue;
if FBinding <> nil then begin
FBinding.ReuseSocket := AValue;
end;
end;
procedure TIdIOHandlerSocket.SetTransparentProxy(AProxy : TIdCustomTransparentProxy);
var
LClass: TIdCustomTransparentProxyClass;
// under ARC, convert a weak reference to a strong reference before working with it
LTransparentProxy: TIdCustomTransparentProxy;
begin
LTransparentProxy := FTransparentProxy;
if LTransparentProxy <> AProxy then
begin
// All this is to preserve the compatibility with old version
// In the case when we have SocksInfo as object created in runtime without owner form it is treated as temporary object
// In the case when the ASocks points to an object with owner it is treated as component on form.
// under ARC, all weak references to a freed object get nil'ed automatically
if Assigned(AProxy) then begin
if not Assigned(AProxy.Owner) then begin
if Assigned(LTransparentProxy) and (not FImplicitTransparentProxy) then begin
FTransparentProxy := nil;
{$IFNDEF USE_OBJECT_ARC}
LTransparentProxy.RemoveFreeNotification(Self);
{$ENDIF}
end;
LClass := TIdCustomTransparentProxyClass(AProxy.ClassType);
if Assigned(LTransparentProxy) and (LTransparentProxy.ClassType <> LClass) then begin
FTransparentProxy := nil;
FImplicitTransparentProxy := False;
IdDisposeAndNil(LTransparentProxy);
end;
if not Assigned(LTransparentProxy) then begin
LTransparentProxy := LClass.Create(Self);
FTransparentProxy := LTransparentProxy;
FImplicitTransparentProxy := True;
end;
LTransparentProxy.Assign(AProxy);
end else begin
if Assigned(LTransparentProxy) then begin
if FImplicitTransparentProxy then begin
FTransparentProxy := nil;
FImplicitTransparentProxy := False;
IdDisposeAndNil(LTransparentProxy);
end else begin
{$IFNDEF USE_OBJECT_ARC}
LTransparentProxy.RemoveFreeNotification(Self);
{$ENDIF}
end;
end;
FTransparentProxy := AProxy;
{$IFNDEF USE_OBJECT_ARC}
AProxy.FreeNotification(Self);
{$ENDIF}
end;
end
else if Assigned(LTransparentProxy) then begin
if FImplicitTransparentProxy then begin
FTransparentProxy := nil;
FImplicitTransparentProxy := False;
IdDisposeAndNil(LTransparentProxy);
end else begin
FTransparentProxy := nil;
{$IFNDEF USE_OBJECT_ARC}
LTransparentProxy.RemoveFreeNotification(Self);
{$ENDIF}
end;
end;
end;
end;
function TIdIOHandlerSocket.GetTransparentProxy: TIdCustomTransparentProxy;
var
// under ARC, convert a weak reference to a strong reference before working with it
LTransparentProxy: TIdCustomTransparentProxy;
begin
LTransparentProxy := FTransparentProxy;
// Necessary at design time for Borland SOAP support
if LTransparentProxy = nil then begin
LTransparentProxy := TIdSocksInfo.Create(Self); //default
FTransparentProxy := LTransparentProxy;
FImplicitTransparentProxy := True;
end;
Result := LTransparentProxy;
end;
function TIdIOHandlerSocket.GetUseNagle: Boolean;
begin
if FBinding <> nil then begin
Result := FBinding.UseNagle;
end else begin
Result := FUseNagle;
end;
end;
procedure TIdIOHandlerSocket.SetUseNagle(AValue: Boolean);
begin
FUseNagle := AValue;
if FBinding <> nil then begin
FBinding.UseNagle := AValue;
end;
end;
// under ARC, all weak references to a freed object get nil'ed automatically
// so this is mostly redundant
procedure TIdIOHandlerSocket.Notification(AComponent: TComponent; Operation: TOperation);
begin
if (Operation = opRemove) and (AComponent = FTransparentProxy) then begin
FTransparentProxy := nil;
FImplicitTransparentProxy := False;
end;
inherited Notification(AComponent, Operation);
end;
procedure TIdIOHandlerSocket.InitComponent;
begin
inherited InitComponent;
FUseNagle := True;
FIPVersion := ID_DEFAULT_IP_VERSION;
end;
function TIdIOHandlerSocket.SourceIsAvailable: Boolean;
begin
Result := BindingAllocated;
end;
function TIdIOHandlerSocket.CheckForError(ALastResult: Integer): Integer;
begin
Result := GStack.CheckForSocketError(ALastResult, [Id_WSAESHUTDOWN, Id_WSAECONNABORTED, Id_WSAECONNRESET, Id_WSAETIMEDOUT]);
end;
procedure TIdIOHandlerSocket.RaiseError(AError: Integer);
begin
GStack.RaiseSocketError(AError);
end;
end.