432 lines
10 KiB
Plaintext
432 lines
10 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.4 6/11/2004 8:40:12 AM DSiders
|
||
|
Added "Do not Localize" comments.
|
||
|
|
||
|
Rev 1.3 2004.05.06 1:47:28 PM czhower
|
||
|
Now uses IndexOf
|
||
|
|
||
|
Rev 1.2 2004.04.22 11:45:18 PM czhower
|
||
|
Bug fixes
|
||
|
|
||
|
Rev 1.1 2004.02.09 9:16:58 PM czhower
|
||
|
Updated to compile and match lib changes.
|
||
|
|
||
|
Rev 1.0 2004.02.03 12:39:10 AM czhower
|
||
|
Move
|
||
|
|
||
|
Rev 1.14 2003.10.19 2:50:42 PM czhower
|
||
|
Fiber cleanup
|
||
|
|
||
|
Rev 1.13 2003.10.11 5:44:20 PM czhower
|
||
|
Chained servers now functional.
|
||
|
|
||
|
Rev 1.12 2003.07.17 4:42:08 PM czhower
|
||
|
More IOCP improvements.
|
||
|
|
||
|
Rev 1.11 2003.07.17 3:55:18 PM czhower
|
||
|
Removed IdIOChainEngineIOCP and merged it into TIdChaingEngine in
|
||
|
IdIOHandlerChain.pas.
|
||
|
|
||
|
Rev 1.7 2003.07.14 11:00:52 PM czhower
|
||
|
More IOCP fixes.
|
||
|
|
||
|
Rev 1.6 2003.07.14 12:54:34 AM czhower
|
||
|
Fixed graceful close detection if it occurs after connect.
|
||
|
|
||
|
Rev 1.5 7/7/2003 1:25:26 PM BGooijen
|
||
|
Added BytesSent property to TIdWorkOpUnitWriteFile
|
||
|
|
||
|
Rev 1.4 7/5/2003 11:47:14 PM BGooijen
|
||
|
Added TIdWorkOpUnitCheckForDisconnect and TIdWorkOpUnitWriteFile
|
||
|
|
||
|
Rev 1.3 3/27/2003 2:43:06 PM BGooijen
|
||
|
Added woWriteStream and woWriteBuffer
|
||
|
|
||
|
Rev 1.2 3/22/2003 09:45:30 PM JPMugaas
|
||
|
Now should compile under D4.
|
||
|
|
||
|
Rev 1.1 3/2/2003 12:36:26 AM BGooijen
|
||
|
Added woReadBuffer and TIdWorkOpUnitReadBuffer to read a buffer. Now
|
||
|
ReadBuffer doesn't use ReadStream any more.
|
||
|
TIdIOHandlerChain.ReadLn now supports MaxLineLength (splitting, and
|
||
|
exceptions).
|
||
|
woReadLn doesn't check the intire buffer any more, but continued where it
|
||
|
stopped the last time.
|
||
|
Added basic support for timeouts (probably only on read operations, and maybe
|
||
|
connect), accuratie of timeout is currently 500msec.
|
||
|
|
||
|
Rev 1.0 2/27/2003 10:11:50 PM BGooijen
|
||
|
WorkOpUnits combined in one file
|
||
|
}
|
||
|
|
||
|
unit IdWorkOpUnits;
|
||
|
|
||
|
interface
|
||
|
|
||
|
uses
|
||
|
Classes,
|
||
|
IdWorkOpUnit, IdGlobal,
|
||
|
SysUtils;
|
||
|
|
||
|
type
|
||
|
TIdWorkOpUnitStreamBaseRead = class(TIdWorkOpUnitRead)
|
||
|
protected
|
||
|
FStream: TStream;
|
||
|
public
|
||
|
constructor Create(AStream: TStream); reintroduce; virtual;
|
||
|
end;
|
||
|
|
||
|
TIdWorkOpUnitStreamBaseWrite = class(TIdWorkOpUnitWrite)
|
||
|
protected
|
||
|
FFreeStream: Boolean;
|
||
|
FStream: TStream;
|
||
|
public
|
||
|
constructor Create(
|
||
|
AStream: TStream;
|
||
|
AFreeStream: Boolean = True
|
||
|
); reintroduce; virtual;
|
||
|
destructor Destroy; override;
|
||
|
end;
|
||
|
|
||
|
TIdWorkOpUnitWriteBuffer = class(TIdWorkOpUnitWrite)
|
||
|
protected
|
||
|
FBuffer: Pointer;
|
||
|
FFreeBuffer: Boolean;
|
||
|
FSize: Integer;
|
||
|
//
|
||
|
procedure Processing(ABytes: Integer); override;
|
||
|
procedure Starting; override;
|
||
|
public
|
||
|
constructor Create(ABuffer: Pointer; ASize: Integer;
|
||
|
AFreeBuffer: Boolean = True); reintroduce; virtual;
|
||
|
destructor Destroy; override;
|
||
|
end;
|
||
|
|
||
|
TIdWorkOpUnitWriteFile = class(TIdWorkOpUnitWrite)
|
||
|
protected
|
||
|
FFilename: String;
|
||
|
FBytesSent: Integer;
|
||
|
//
|
||
|
procedure Processing(ABytes: Integer); override;
|
||
|
procedure Starting; override;
|
||
|
public
|
||
|
constructor Create(AFileName: string); reintroduce;
|
||
|
end;
|
||
|
|
||
|
TIdWorkOpUnitWriteStream = class(TIdWorkOpUnitStreamBaseWrite)
|
||
|
protected
|
||
|
FCount: Integer;
|
||
|
FStartPos: Integer;
|
||
|
//
|
||
|
procedure Processing(ABytes: Integer); override;
|
||
|
procedure Starting; override;
|
||
|
public
|
||
|
constructor Create(AStream: TStream; AStartPos, ACount: Integer;
|
||
|
AFreeStream: Boolean); reintroduce; virtual;
|
||
|
end;
|
||
|
|
||
|
TIdWorkOpUnitWaitConnected = class(TIdWorkOpUnit)
|
||
|
protected
|
||
|
procedure Starting; override;
|
||
|
public
|
||
|
procedure Process(
|
||
|
AOverlapped: PIdOverlapped;
|
||
|
AByteCount: Integer
|
||
|
); override;
|
||
|
end;
|
||
|
|
||
|
TIdWorkOpUnitReadSized = class(TIdWorkOpUnitRead)
|
||
|
protected
|
||
|
FSize: Integer;
|
||
|
//
|
||
|
procedure Processing(
|
||
|
ABuffer: TIdBytes
|
||
|
); override;
|
||
|
public
|
||
|
constructor Create(ASize: Integer); reintroduce;
|
||
|
end;
|
||
|
|
||
|
TIdWorkOpUnitReadSizedStream = class(TIdWorkOpUnitStreamBaseRead)
|
||
|
protected
|
||
|
FSize: Integer;
|
||
|
//
|
||
|
procedure Processing(
|
||
|
ABuffer: TIdBytes
|
||
|
); override;
|
||
|
public
|
||
|
constructor Create(AStream: TStream; ASize: Integer);
|
||
|
reintroduce;
|
||
|
end;
|
||
|
|
||
|
TIdWorkOpUnitReadLn = class(TIdWorkOpUnitRead)
|
||
|
protected
|
||
|
FLastPos: Integer;
|
||
|
FMaxLength: Integer;
|
||
|
FTerminator: string;
|
||
|
//
|
||
|
procedure Processing(
|
||
|
ABuffer: TIdBytes
|
||
|
); override;
|
||
|
public
|
||
|
constructor Create(
|
||
|
ATerminator: string;
|
||
|
AMaxLength: Integer
|
||
|
); reintroduce;
|
||
|
end;
|
||
|
|
||
|
TIdWorkOpUnitReadUntilDisconnect = class(TIdWorkOpUnitStreamBaseRead)
|
||
|
protected
|
||
|
procedure Processing(
|
||
|
ABuffer: TIdBytes
|
||
|
); override;
|
||
|
end;
|
||
|
|
||
|
TIdWorkOpUnitReadAvailable = class(TIdWorkOpUnitRead)
|
||
|
protected
|
||
|
procedure Processing(
|
||
|
ABuffer: TIdBytes
|
||
|
); override;
|
||
|
end;
|
||
|
|
||
|
implementation
|
||
|
|
||
|
{ TIdWorkOpUnitWriteStream }
|
||
|
|
||
|
constructor TIdWorkOpUnitWriteStream.Create(AStream: TStream; AStartPos,ACount:integer; AFreeStream: Boolean);
|
||
|
begin
|
||
|
inherited Create(AStream, AFreeStream);
|
||
|
FStream.Position := AStartPos;
|
||
|
FCount := ACount;
|
||
|
end;
|
||
|
|
||
|
procedure TIdWorkOpUnitWriteStream.Processing(ABytes: Integer);
|
||
|
//TODO: This used to use pages from IdBuffer, which because of .Net do not exist
|
||
|
// anymore. We need to maybe keep a local persistent buffer instead then for
|
||
|
// storage reasons.
|
||
|
var
|
||
|
LBuffer: TIdBytes;
|
||
|
LSize: Integer;
|
||
|
begin
|
||
|
FCount := FCount - ABytes;
|
||
|
if FCount = 0 then begin
|
||
|
Complete;
|
||
|
end else begin
|
||
|
FStream.Position := ABytes;
|
||
|
//
|
||
|
//TODO: Dont hard code this value. Also find an optimal size for IOCP
|
||
|
LSize := Min(FCount, WOPageSize);
|
||
|
SetLength(LBuffer, LSize);
|
||
|
//
|
||
|
FStream.ReadBuffer(LBuffer[0], LSize);
|
||
|
Write(@LBuffer[0], LSize);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdWorkOpUnitWriteStream.Starting;
|
||
|
begin
|
||
|
Processing(0);
|
||
|
end;
|
||
|
|
||
|
{ TIdWorkOpUnitWriteBuffer }
|
||
|
|
||
|
constructor TIdWorkOpUnitWriteBuffer.Create(ABuffer: pointer; ASize: integer; AFreeBuffer: Boolean = True);
|
||
|
begin
|
||
|
inherited Create;
|
||
|
FSize := ASize;
|
||
|
FBuffer := ABuffer;
|
||
|
FFreeBuffer := AFreeBuffer;
|
||
|
end;
|
||
|
|
||
|
destructor TIdWorkOpUnitWriteBuffer.Destroy;
|
||
|
begin
|
||
|
if FFreeBuffer then begin
|
||
|
FreeMem(FBuffer);
|
||
|
FBuffer := nil;
|
||
|
end;
|
||
|
inherited;
|
||
|
end;
|
||
|
|
||
|
procedure TIdWorkOpUnitWriteBuffer.Processing(ABytes: Integer);
|
||
|
begin
|
||
|
//TODO: Change the pointer to a type that points to bytes
|
||
|
FBuffer := Pointer(Cardinal(FBuffer) + Cardinal(ABytes));
|
||
|
FSize := FSize - ABytes;
|
||
|
if FSize = 0 then begin
|
||
|
Complete;
|
||
|
end else begin
|
||
|
//TODO: Reduce this down so it never sends more than a page
|
||
|
Write(FBuffer, Min(FSize, WOPageSize));
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdWorkOpUnitWriteBuffer.Starting;
|
||
|
begin
|
||
|
Processing(0);
|
||
|
end;
|
||
|
|
||
|
{ TIdWorkOpUnitWriteFile }
|
||
|
|
||
|
constructor TIdWorkOpUnitWriteFile.Create(AFileName:string);
|
||
|
begin
|
||
|
inherited Create;
|
||
|
FFilename := AFileName;
|
||
|
end;
|
||
|
|
||
|
procedure TIdWorkOpUnitWriteFile.Processing(ABytes: Integer);
|
||
|
begin
|
||
|
Assert(False, 'Need to implement WriteFile, also add to a bubble'); {do not localize}
|
||
|
end;
|
||
|
|
||
|
procedure TIdWorkOpUnitWriteFile.Starting;
|
||
|
begin
|
||
|
end;
|
||
|
|
||
|
{ TIdWorkOpUnitSizedStream }
|
||
|
|
||
|
constructor TIdWorkOpUnitReadSizedStream.Create(AStream: TStream; ASize:integer);
|
||
|
begin
|
||
|
inherited Create(AStream);
|
||
|
FSize := ASize;
|
||
|
end;
|
||
|
|
||
|
procedure TIdWorkOpUnitWaitConnected.Process(
|
||
|
AOverlapped: PIdOverlapped;
|
||
|
AByteCount: Integer
|
||
|
);
|
||
|
begin
|
||
|
end;
|
||
|
|
||
|
procedure TIdWorkOpUnitWaitConnected.Starting;
|
||
|
begin
|
||
|
end;
|
||
|
|
||
|
{ TIdWorkOpUnitReadLn }
|
||
|
|
||
|
constructor TIdWorkOpUnitReadLn.Create(
|
||
|
ATerminator: string;
|
||
|
AMaxLength: Integer);
|
||
|
begin
|
||
|
inherited Create;
|
||
|
FLastPos := 1;
|
||
|
FTerminator := ATerminator;
|
||
|
FMaxLength := AMaxLength;
|
||
|
end;
|
||
|
|
||
|
procedure TIdWorkOpUnitReadLn.Processing(
|
||
|
ABuffer: TIdBytes
|
||
|
);
|
||
|
begin
|
||
|
//TODO: ReadLn is very common. Need to optimize this class and maybe
|
||
|
// even pass pack the result directly so we dont search twice.
|
||
|
//Also allow for hinting from the user.
|
||
|
IOHandler.InputBuffer.Write(ABuffer);
|
||
|
if not IOHandler.Connected then begin
|
||
|
Complete;
|
||
|
end else if IOHandler.InputBuffer.IndexOf(FTerminator, FLastPos) = -1 then begin
|
||
|
Read;
|
||
|
end else begin
|
||
|
Complete;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdWorkOpUnitReadUntilDisconnect.Processing(
|
||
|
ABuffer: TIdBytes
|
||
|
);
|
||
|
begin
|
||
|
// 0 is disconnected, so keep requesting til 0
|
||
|
if Length(ABuffer) = 0 then begin
|
||
|
Complete;
|
||
|
end else begin
|
||
|
FStream.WriteBuffer(ABuffer[0], Length(ABuffer));
|
||
|
Read;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
{ TIdWorkOpUnitReadAvailable }
|
||
|
|
||
|
procedure TIdWorkOpUnitReadAvailable.Processing(
|
||
|
ABuffer: TIdBytes
|
||
|
);
|
||
|
begin
|
||
|
Complete;
|
||
|
end;
|
||
|
|
||
|
{ TIdWorkOpUnitReadSized }
|
||
|
|
||
|
constructor TIdWorkOpUnitReadSized.Create(ASize: Integer);
|
||
|
begin
|
||
|
inherited Create;
|
||
|
FSize := ASize;
|
||
|
end;
|
||
|
|
||
|
procedure TIdWorkOpUnitReadSized.Processing(
|
||
|
ABuffer: TIdBytes
|
||
|
);
|
||
|
begin
|
||
|
IOHandler.InputBuffer.Write(ABuffer);
|
||
|
FSize := FSize - Length(ABuffer);
|
||
|
if FSize = 0 then begin
|
||
|
Complete;
|
||
|
end else begin
|
||
|
Read;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
{ TIdWorkOpUnitStreamBaseRead }
|
||
|
|
||
|
constructor TIdWorkOpUnitStreamBaseRead.Create(AStream: TStream);
|
||
|
begin
|
||
|
inherited Create;
|
||
|
FStream := AStream;
|
||
|
end;
|
||
|
|
||
|
{ TIdWorkOpUnitStreamBaseWrite }
|
||
|
|
||
|
constructor TIdWorkOpUnitStreamBaseWrite.Create(AStream: TStream;
|
||
|
AFreeStream: Boolean);
|
||
|
begin
|
||
|
inherited Create;
|
||
|
FStream := AStream;
|
||
|
FFreeStream := AFreeStream;
|
||
|
end;
|
||
|
|
||
|
destructor TIdWorkOpUnitStreamBaseWrite.Destroy;
|
||
|
begin
|
||
|
if FFreeStream then begin
|
||
|
FreeAndNil(FStream);
|
||
|
end;
|
||
|
inherited;
|
||
|
end;
|
||
|
|
||
|
procedure TIdWorkOpUnitReadSizedStream.Processing(
|
||
|
ABuffer: TIdBytes
|
||
|
);
|
||
|
begin
|
||
|
FStream.WriteBuffer(ABuffer[0], Length(ABuffer));
|
||
|
FSize := FSize - Length(ABuffer);
|
||
|
if FSize = 0 then begin
|
||
|
Complete;
|
||
|
end else begin
|
||
|
Read;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
end.
|