IdCompressionIntercept.pas 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. $Log$
  13. Rev 1.10 2/22/2004 12:04:00 AM JPMugaas
  14. Updated for file rename.
  15. Rev 1.9 2/12/2004 11:28:04 PM JPMugaas
  16. Modified compression intercept to use the ZLibEx unit.
  17. Rev 1.8 2004.02.09 9:56:00 PM czhower
  18. Fixed for lib changes.
  19. Rev 1.7 5/12/2003 12:31:00 AM GGrieve
  20. Get compiling again with DotNet Changes
  21. Rev 1.6 10/12/2003 1:49:26 PM BGooijen
  22. Changed comment of last checkin
  23. Rev 1.5 10/12/2003 1:43:24 PM BGooijen
  24. Changed IdCompilerDefines.inc to Core\IdCompilerDefines.inc
  25. Rev 1.3 6/27/2003 2:38:04 PM BGooijen
  26. Fixed bug where last part was not compressed/send
  27. Rev 1.2 4/10/2003 4:12:42 PM BGooijen
  28. Added TIdServerCompressionIntercept
  29. Rev 1.1 4/3/2003 2:55:48 PM BGooijen
  30. Now calls DeinitCompressors on disconnect
  31. Rev 1.0 11/14/2002 02:15:50 PM JPMugaas
  32. }
  33. unit IdCompressionIntercept;
  34. { This file implements an Indy intercept component that compresses a data
  35. stream using the open-source zlib compression library. In order for this
  36. file to compile on Windows, the follow .obj files *must* be provided as
  37. delivered with this file:
  38. deflate.obj
  39. inflate.obj
  40. inftrees.obj
  41. trees.obj
  42. adler32.obj
  43. infblock.obj
  44. infcodes.obj
  45. infutil.obj
  46. inffast.obj
  47. On Linux, the shared-object file libz.so.1 *must* be available on the
  48. system. Most modern Linux distributions include this file.
  49. Simply set the CompressionLevel property to a value between 1 and 9 to
  50. enable compressing of the data stream. A setting of 0(zero) disables
  51. compression and the component is dormant. The sender *and* received must
  52. have compression enabled in order to properly decompress the data stream.
  53. They do *not* have to use the same CompressionLevel as long as they are
  54. both set to a value between 1 and 9.
  55. Original Author: Allen Bauer
  56. This source file is submitted to the Indy project on behalf of Borland
  57. Sofware Corporation. No warranties, express or implied are given with
  58. this source file.
  59. }
  60. interface
  61. {$I IdCompilerDefines.inc}
  62. uses
  63. Classes,
  64. IdException,
  65. IdGlobal,
  66. IdGlobalProtocols,
  67. IdIntercept,
  68. IdZLibHeaders;
  69. type
  70. EIdCompressionException = class(EIdException);
  71. EIdCompressorInitFailure = class(EIdCompressionException);
  72. EIdDecompressorInitFailure = class(EIdCompressionException);
  73. EIdCompressionError = class(EIdCompressionException);
  74. EIdDecompressionError = class(EIdCompressionException);
  75. TIdCompressionLevel = 0..9;
  76. TIdCompressionIntercept = class(TIdConnectionIntercept)
  77. protected
  78. FCompressionLevel: TIdCompressionLevel;
  79. FCompressRec: TZStreamRec;
  80. FDecompressRec: TZStreamRec;
  81. FRecvBuf: TIdBytes;
  82. FRecvCount, FRecvSize: UInt32;
  83. FSendBuf: TIdBytes;
  84. FSendCount, FSendSize: UInt32;
  85. procedure SetCompressionLevel(Value: TIdCompressionLevel);
  86. procedure InitCompressors;
  87. procedure DeinitCompressors;
  88. public
  89. destructor Destroy; override;
  90. procedure Disconnect; override;
  91. procedure Receive(var VBuffer: TIdBytes); override;
  92. procedure Send(var VBuffer: TIdBytes); override;
  93. published
  94. property CompressionLevel: TIdCompressionLevel read FCompressionLevel write SetCompressionLevel;
  95. end;
  96. TIdServerCompressionIntercept = class(TIdServerIntercept)
  97. protected
  98. FCompressionLevel: TIdCompressionLevel;
  99. public
  100. procedure Init; override;
  101. function Accept(AConnection: TComponent): TIdConnectionIntercept; override;
  102. published
  103. property CompressionLevel: TIdCompressionLevel read FCompressionLevel write FCompressionLevel;
  104. end;
  105. implementation
  106. uses
  107. IdResourceStringsProtocols, IdExceptionCore;
  108. { TIdCompressionIntercept }
  109. procedure TIdCompressionIntercept.DeinitCompressors;
  110. begin
  111. if Assigned(FCompressRec.zalloc) then begin
  112. deflateEnd(FCompressRec);
  113. FillChar(FCompressRec, SizeOf(FCompressRec), 0);
  114. end;
  115. if Assigned(FDecompressRec.zalloc) then
  116. begin
  117. inflateEnd(FDecompressRec);
  118. FillChar(FDecompressRec, SizeOf(FDecompressRec), 0);
  119. end;
  120. end;
  121. destructor TIdCompressionIntercept.Destroy;
  122. begin
  123. DeinitCompressors;
  124. SetLength(FRecvBuf, 0);
  125. SetLength(FSendBuf, 0);
  126. inherited Destroy;
  127. end;
  128. procedure TIdCompressionIntercept.Disconnect;
  129. begin
  130. inherited Disconnect;
  131. DeinitCompressors;
  132. end;
  133. procedure TIdCompressionIntercept.InitCompressors;
  134. begin
  135. if not Assigned(FCompressRec.zalloc) then
  136. begin
  137. FCompressRec.zalloc := IdZLibHeaders.zlibAllocMem;
  138. FCompressRec.zfree := IdZLibHeaders.zlibFreeMem;
  139. if deflateInit_(FCompressRec, FCompressionLevel, zlib_Version, SizeOf(FCompressRec)) <> Z_OK then
  140. begin
  141. raise EIdCompressorInitFailure.Create(RSZLCompressorInitializeFailure);
  142. end;
  143. end;
  144. if not Assigned(FDecompressRec.zalloc) then
  145. begin
  146. FDecompressRec.zalloc := IdZLibHeaders.zlibAllocMem;
  147. FDecompressRec.zfree := IdZLibHeaders.zlibFreeMem;
  148. if inflateInit_(FDecompressRec, zlib_Version, SizeOf(FDecompressRec)) <> Z_OK then
  149. begin
  150. raise EIdDecompressorInitFailure.Create(RSZLDecompressorInitializeFailure);
  151. end;
  152. end;
  153. end;
  154. procedure TIdCompressionIntercept.Receive(var VBuffer: TIdBytes);
  155. var
  156. LBuffer: TIdBytes;
  157. LPos : integer;
  158. nChars, C : UInt32;
  159. StreamEnd: Boolean;
  160. begin
  161. // let the next Intercept in the chain decode its data first
  162. inherited Receive(VBuffer);
  163. SetLength(LBuffer, 2048);
  164. if FCompressionLevel in [1..9] then
  165. begin
  166. InitCompressors;
  167. StreamEnd := False;
  168. LPos := 0;
  169. repeat
  170. nChars := IndyMin(Length(VBuffer) - LPos, Length(LBuffer));
  171. if nChars = 0 then begin
  172. Break;
  173. end;
  174. CopyTIdBytes(VBuffer, LPos, LBuffer, 0, nChars);
  175. Inc(LPos, nChars);
  176. FDecompressRec.next_in := PIdAnsiChar(@LBuffer[0]);
  177. FDecompressRec.avail_in := nChars;
  178. FDecompressRec.total_in := 0;
  179. while FDecompressRec.avail_in > 0 do
  180. begin
  181. if FRecvCount = FRecvSize then begin
  182. if FRecvSize = 0 then begin
  183. FRecvSize := 2048;
  184. end else begin
  185. Inc(FRecvSize, 1024);
  186. end;
  187. SetLength(FRecvBuf, FRecvSize);
  188. end;
  189. FDecompressRec.next_out := PIdAnsiChar(@FRecvBuf[FRecvCount]);
  190. C := FRecvSize - FRecvCount;
  191. FDecompressRec.avail_out := C;
  192. FDecompressRec.total_out := 0;
  193. case inflate(FDecompressRec, Z_NO_FLUSH) of
  194. Z_STREAM_END:
  195. StreamEnd := True;
  196. Z_STREAM_ERROR,
  197. Z_DATA_ERROR,
  198. Z_MEM_ERROR:
  199. raise EIdDecompressionError.Create(RSZLDecompressionError);
  200. end;
  201. Inc(FRecvCount, C - FDecompressRec.avail_out);
  202. end;
  203. until StreamEnd;
  204. SetLength(VBuffer, FRecvCount);
  205. CopyTIdBytes(FRecvBuf, 0, VBuffer, 0, FRecvCount);
  206. FRecvCount := 0;
  207. end;
  208. end;
  209. procedure TIdCompressionIntercept.Send(var VBuffer: TIdBytes);
  210. var
  211. LBuffer: TIdBytes;
  212. LLen, LSize: UInt32;
  213. begin
  214. LBuffer := nil;
  215. if FCompressionLevel in [1..9] then
  216. begin
  217. InitCompressors;
  218. // Make sure the Send buffer is large enough to hold the input data
  219. LSize := Length(VBuffer);
  220. if LSize > FSendSize then
  221. begin
  222. if LSize > 2048 then begin
  223. FSendSize := LSize + (LSize + 1023) mod 1024;
  224. end else begin
  225. FSendSize := 2048;
  226. end;
  227. SetLength(FSendBuf, FSendSize);
  228. end;
  229. // Get the data from the input and save it off
  230. // TODO: get rid of FSendBuf and use ABuffer directly
  231. FSendCount := LSize;
  232. CopyTIdBytes(VBuffer, 0, FSendBuf, 0, FSendCount);
  233. FCompressRec.next_in := PIdAnsiChar(@FSendBuf[0]);
  234. FCompressRec.avail_in := FSendCount;
  235. FCompressRec.avail_out := 0;
  236. // clear the output stream in preparation for compression
  237. SetLength(VBuffer, 0);
  238. SetLength(LBuffer, 1024);
  239. // As long as data is being outputted, keep compressing
  240. while FCompressRec.avail_out = 0 do
  241. begin
  242. FCompressRec.next_out := PIdAnsiChar(@LBuffer[0]);
  243. FCompressRec.avail_out := Length(LBuffer);
  244. case deflate(FCompressRec, Z_SYNC_FLUSH) of
  245. Z_STREAM_ERROR,
  246. Z_DATA_ERROR,
  247. Z_MEM_ERROR: raise EIdCompressionError.Create(RSZLCompressionError);
  248. end;
  249. // Place the compressed data into the output stream
  250. LLen := Length(VBuffer);
  251. SetLength(VBuffer, LLen + UInt32(Length(LBuffer)) - FCompressRec.avail_out);
  252. CopyTIdBytes(LBuffer, 0, VBuffer, LLen, UInt32(Length(LBuffer)) - FCompressRec.avail_out);
  253. end;
  254. end;
  255. // let the next Intercept in the chain encode its data next
  256. inherited Send(VBuffer);
  257. end;
  258. procedure TIdCompressionIntercept.SetCompressionLevel(Value: TIdCompressionLevel);
  259. begin
  260. if Value < 0 then begin
  261. Value := 0;
  262. end else if Value > 9 then begin
  263. Value := 9;
  264. end;
  265. if Value <> FCompressionLevel then begin
  266. DeinitCompressors;
  267. FCompressionLevel := Value;
  268. end;
  269. end;
  270. { TIdServerCompressionIntercept }
  271. procedure TIdServerCompressionIntercept.Init;
  272. begin
  273. end;
  274. function TIdServerCompressionIntercept.Accept(AConnection: TComponent): TIdConnectionIntercept;
  275. begin
  276. Result := TIdCompressionIntercept.Create(nil);
  277. TIdCompressionIntercept(Result).CompressionLevel := CompressionLevel;
  278. end;
  279. end.