| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- {
- $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.10 2/22/2004 12:04:00 AM JPMugaas
- Updated for file rename.
- Rev 1.9 2/12/2004 11:28:04 PM JPMugaas
- Modified compression intercept to use the ZLibEx unit.
- Rev 1.8 2004.02.09 9:56:00 PM czhower
- Fixed for lib changes.
- Rev 1.7 5/12/2003 12:31:00 AM GGrieve
- Get compiling again with DotNet Changes
- Rev 1.6 10/12/2003 1:49:26 PM BGooijen
- Changed comment of last checkin
- Rev 1.5 10/12/2003 1:43:24 PM BGooijen
- Changed IdCompilerDefines.inc to Core\IdCompilerDefines.inc
- Rev 1.3 6/27/2003 2:38:04 PM BGooijen
- Fixed bug where last part was not compressed/send
- Rev 1.2 4/10/2003 4:12:42 PM BGooijen
- Added TIdServerCompressionIntercept
- Rev 1.1 4/3/2003 2:55:48 PM BGooijen
- Now calls DeinitCompressors on disconnect
- Rev 1.0 11/14/2002 02:15:50 PM JPMugaas
- }
- unit IdCompressionIntercept;
- { This file implements an Indy intercept component that compresses a data
- stream using the open-source zlib compression library. In order for this
- file to compile on Windows, the follow .obj files *must* be provided as
- delivered with this file:
- deflate.obj
- inflate.obj
- inftrees.obj
- trees.obj
- adler32.obj
- infblock.obj
- infcodes.obj
- infutil.obj
- inffast.obj
- On Linux, the shared-object file libz.so.1 *must* be available on the
- system. Most modern Linux distributions include this file.
- Simply set the CompressionLevel property to a value between 1 and 9 to
- enable compressing of the data stream. A setting of 0(zero) disables
- compression and the component is dormant. The sender *and* received must
- have compression enabled in order to properly decompress the data stream.
- They do *not* have to use the same CompressionLevel as long as they are
- both set to a value between 1 and 9.
- Original Author: Allen Bauer
- This source file is submitted to the Indy project on behalf of Borland
- Sofware Corporation. No warranties, express or implied are given with
- this source file.
- }
- interface
- {$I IdCompilerDefines.inc}
- uses
- Classes,
- IdException,
- IdGlobal,
- IdGlobalProtocols,
- IdIntercept,
- IdZLibHeaders;
- type
- EIdCompressionException = class(EIdException);
- EIdCompressorInitFailure = class(EIdCompressionException);
- EIdDecompressorInitFailure = class(EIdCompressionException);
- EIdCompressionError = class(EIdCompressionException);
- EIdDecompressionError = class(EIdCompressionException);
- TIdCompressionLevel = 0..9;
- TIdCompressionIntercept = class(TIdConnectionIntercept)
- protected
- FCompressionLevel: TIdCompressionLevel;
- FCompressRec: TZStreamRec;
- FDecompressRec: TZStreamRec;
- FRecvBuf: TIdBytes;
- FRecvCount, FRecvSize: UInt32;
- FSendBuf: TIdBytes;
- FSendCount, FSendSize: UInt32;
- procedure SetCompressionLevel(Value: TIdCompressionLevel);
- procedure InitCompressors;
- procedure DeinitCompressors;
- public
- destructor Destroy; override;
- procedure Disconnect; override;
- procedure Receive(var VBuffer: TIdBytes); override;
- procedure Send(var VBuffer: TIdBytes); override;
- published
- property CompressionLevel: TIdCompressionLevel read FCompressionLevel write SetCompressionLevel;
- end;
- TIdServerCompressionIntercept = class(TIdServerIntercept)
- protected
- FCompressionLevel: TIdCompressionLevel;
- public
- procedure Init; override;
- function Accept(AConnection: TComponent): TIdConnectionIntercept; override;
- published
- property CompressionLevel: TIdCompressionLevel read FCompressionLevel write FCompressionLevel;
- end;
- implementation
- uses
- IdResourceStringsProtocols, IdExceptionCore;
- { TIdCompressionIntercept }
- procedure TIdCompressionIntercept.DeinitCompressors;
- begin
- if Assigned(FCompressRec.zalloc) then begin
- deflateEnd(FCompressRec);
- FillChar(FCompressRec, SizeOf(FCompressRec), 0);
- end;
- if Assigned(FDecompressRec.zalloc) then
- begin
- inflateEnd(FDecompressRec);
- FillChar(FDecompressRec, SizeOf(FDecompressRec), 0);
- end;
- end;
- destructor TIdCompressionIntercept.Destroy;
- begin
- DeinitCompressors;
- SetLength(FRecvBuf, 0);
- SetLength(FSendBuf, 0);
- inherited Destroy;
- end;
- procedure TIdCompressionIntercept.Disconnect;
- begin
- inherited Disconnect;
- DeinitCompressors;
- end;
- procedure TIdCompressionIntercept.InitCompressors;
- begin
- if not Assigned(FCompressRec.zalloc) then
- begin
- FCompressRec.zalloc := IdZLibHeaders.zlibAllocMem;
- FCompressRec.zfree := IdZLibHeaders.zlibFreeMem;
- if deflateInit_(FCompressRec, FCompressionLevel, zlib_Version, SizeOf(FCompressRec)) <> Z_OK then
- begin
- raise EIdCompressorInitFailure.Create(RSZLCompressorInitializeFailure);
- end;
- end;
- if not Assigned(FDecompressRec.zalloc) then
- begin
- FDecompressRec.zalloc := IdZLibHeaders.zlibAllocMem;
- FDecompressRec.zfree := IdZLibHeaders.zlibFreeMem;
- if inflateInit_(FDecompressRec, zlib_Version, SizeOf(FDecompressRec)) <> Z_OK then
- begin
- raise EIdDecompressorInitFailure.Create(RSZLDecompressorInitializeFailure);
- end;
- end;
- end;
- procedure TIdCompressionIntercept.Receive(var VBuffer: TIdBytes);
- var
- LBuffer: TIdBytes;
- LPos : integer;
- nChars, C : UInt32;
- StreamEnd: Boolean;
- begin
- // let the next Intercept in the chain decode its data first
- inherited Receive(VBuffer);
- SetLength(LBuffer, 2048);
- if FCompressionLevel in [1..9] then
- begin
- InitCompressors;
- StreamEnd := False;
- LPos := 0;
- repeat
- nChars := IndyMin(Length(VBuffer) - LPos, Length(LBuffer));
- if nChars = 0 then begin
- Break;
- end;
- CopyTIdBytes(VBuffer, LPos, LBuffer, 0, nChars);
- Inc(LPos, nChars);
- FDecompressRec.next_in := PIdAnsiChar(@LBuffer[0]);
- FDecompressRec.avail_in := nChars;
- FDecompressRec.total_in := 0;
- while FDecompressRec.avail_in > 0 do
- begin
- if FRecvCount = FRecvSize then begin
- if FRecvSize = 0 then begin
- FRecvSize := 2048;
- end else begin
- Inc(FRecvSize, 1024);
- end;
- SetLength(FRecvBuf, FRecvSize);
- end;
- FDecompressRec.next_out := PIdAnsiChar(@FRecvBuf[FRecvCount]);
- C := FRecvSize - FRecvCount;
- FDecompressRec.avail_out := C;
- FDecompressRec.total_out := 0;
- case inflate(FDecompressRec, Z_NO_FLUSH) of
- Z_STREAM_END:
- StreamEnd := True;
- Z_STREAM_ERROR,
- Z_DATA_ERROR,
- Z_MEM_ERROR:
- raise EIdDecompressionError.Create(RSZLDecompressionError);
- end;
- Inc(FRecvCount, C - FDecompressRec.avail_out);
- end;
- until StreamEnd;
- SetLength(VBuffer, FRecvCount);
- CopyTIdBytes(FRecvBuf, 0, VBuffer, 0, FRecvCount);
- FRecvCount := 0;
- end;
- end;
- procedure TIdCompressionIntercept.Send(var VBuffer: TIdBytes);
- var
- LBuffer: TIdBytes;
- LLen, LSize: UInt32;
- begin
- LBuffer := nil;
- if FCompressionLevel in [1..9] then
- begin
- InitCompressors;
- // Make sure the Send buffer is large enough to hold the input data
- LSize := Length(VBuffer);
- if LSize > FSendSize then
- begin
- if LSize > 2048 then begin
- FSendSize := LSize + (LSize + 1023) mod 1024;
- end else begin
- FSendSize := 2048;
- end;
- SetLength(FSendBuf, FSendSize);
- end;
- // Get the data from the input and save it off
- // TODO: get rid of FSendBuf and use ABuffer directly
- FSendCount := LSize;
- CopyTIdBytes(VBuffer, 0, FSendBuf, 0, FSendCount);
- FCompressRec.next_in := PIdAnsiChar(@FSendBuf[0]);
- FCompressRec.avail_in := FSendCount;
- FCompressRec.avail_out := 0;
- // clear the output stream in preparation for compression
- SetLength(VBuffer, 0);
- SetLength(LBuffer, 1024);
- // As long as data is being outputted, keep compressing
- while FCompressRec.avail_out = 0 do
- begin
- FCompressRec.next_out := PIdAnsiChar(@LBuffer[0]);
- FCompressRec.avail_out := Length(LBuffer);
- case deflate(FCompressRec, Z_SYNC_FLUSH) of
- Z_STREAM_ERROR,
- Z_DATA_ERROR,
- Z_MEM_ERROR: raise EIdCompressionError.Create(RSZLCompressionError);
- end;
- // Place the compressed data into the output stream
- LLen := Length(VBuffer);
- SetLength(VBuffer, LLen + UInt32(Length(LBuffer)) - FCompressRec.avail_out);
- CopyTIdBytes(LBuffer, 0, VBuffer, LLen, UInt32(Length(LBuffer)) - FCompressRec.avail_out);
- end;
- end;
- // let the next Intercept in the chain encode its data next
- inherited Send(VBuffer);
- end;
- procedure TIdCompressionIntercept.SetCompressionLevel(Value: TIdCompressionLevel);
- begin
- if Value < 0 then begin
- Value := 0;
- end else if Value > 9 then begin
- Value := 9;
- end;
- if Value <> FCompressionLevel then begin
- DeinitCompressors;
- FCompressionLevel := Value;
- end;
- end;
- { TIdServerCompressionIntercept }
- procedure TIdServerCompressionIntercept.Init;
- begin
- end;
- function TIdServerCompressionIntercept.Accept(AConnection: TComponent): TIdConnectionIntercept;
- begin
- Result := TIdCompressionIntercept.Create(nil);
- TIdCompressionIntercept(Result).CompressionLevel := CompressionLevel;
- end;
- end.
|