IdCompressionIntercept.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 10107: IdCompressionIntercept.pas
  11. {
  12. { Rev 1.4 2004.06.13 9:08:38 AM czhower
  13. { Comment cleanup
  14. }
  15. {
  16. Rev 1.3 6/27/2003 2:41:14 PM BGooijen
  17. Fixed bug where last part was not compressed/send
  18. }
  19. {
  20. { Rev 1.2 11/6/2003 17:31:40 GGrieve
  21. { fix for server Intercept
  22. }
  23. {
  24. Rev 1.1 4/3/2003 2:51:20 PM BGooijen
  25. Now calls DeinitCompressors on disconnect
  26. }
  27. {
  28. { Rev 1.0 2002.11.12 10:33:46 PM czhower
  29. }
  30. unit IdCompressionIntercept;
  31. { This file implements an Indy intercept component that compresses a data
  32. stream using the open-source zlib compression library. In order for this
  33. file to compile on Windows, the follow .obj files *must* be provided as
  34. delivered with this file:
  35. deflate.obj
  36. inflate.obj
  37. inftrees.obj
  38. trees.obj
  39. adler32.obj
  40. infblock.obj
  41. infcodes.obj
  42. infutil.obj
  43. inffast.obj
  44. On Linux, the shared-object file libz.so.1 *must* be available on the
  45. system. Most modern Linux distributions include this file.
  46. Simply set the CompressionLevel property to a value between 1 and 9 to
  47. enable compressing of the data stream. A setting of 0(zero) disables
  48. compression and the component is dormant. The sender *and* received must
  49. have compression enabled in order to properly decompress the data stream.
  50. They do *not* have to use the same CompressionLevel as long as they are
  51. both set to a value between 1 and 9.
  52. Original Author: Allen Bauer
  53. This source file is submitted to the Indy project on behalf of Borland
  54. Sofware Corporation. No warranties, express or implied are given with
  55. this source file.
  56. }
  57. {
  58. When compiling with < Delphi 7 and using the command line compiler you may encounter the following
  59. errors:
  60. IdCompressionIntercept.pas(331) Error: Incompatible types
  61. IdCompressionIntercept.pas(152) Error: Unsatisfied forward or external declaration: '_tr_init'
  62. ....
  63. IdCompressionIntercept.pas(234) Error: Unsatisfied forward or external declaration: 'inflateReset'
  64. Indy40.dpk(196) Fatal: Could not compile used unit 'IdCompressionIntercept.pas'
  65. To work around this issue this unit must be compiled separately when using the command line
  66. compiler and build the rest using /M. Do not use /B on the second build as it will recompile this
  67. unit. Using the Full??.BAT files will compile Indy properly.
  68. }
  69. interface
  70. {$I IdCompilerDefines.inc}
  71. uses
  72. {$IFDEF USEZLIBUNIT}
  73. ZLib,
  74. {$ENDIF}
  75. Classes, IdException, IdTCPClient, IdGlobal, IdTCPConnection, IdIntercept;
  76. type
  77. {$IFNDEF USEZLIBUNIT}
  78. TAlloc = function (AppData: Pointer; Items, Size: Integer): Pointer;
  79. {$IFDEF MSWINDOWS}
  80. register;
  81. {$ENDIF}
  82. {$IFDEF LINUX}
  83. cdecl;
  84. {$ENDIF}
  85. TFree = procedure (AppData, Block: Pointer);
  86. {$IFDEF MSWINDOWS}
  87. register;
  88. {$ENDIF}
  89. {$IFDEF LINUX}
  90. cdecl;
  91. {$ENDIF}
  92. // Internal structure. Ignore.
  93. TZStreamRec = packed record
  94. next_in: PChar; // next input byte
  95. avail_in: Integer; // number of bytes available at next_in
  96. total_in: Integer; // total nb of input bytes read so far
  97. next_out: PChar; // next output byte should be put here
  98. avail_out: Integer; // remaining free space at next_out
  99. total_out: Integer; // total nb of bytes output so far
  100. msg: PChar; // last error message, NULL if no error
  101. internal: Pointer; // not visible by applications
  102. zalloc: TAlloc; // used to allocate the internal state
  103. zfree: TFree; // used to free the internal state
  104. AppData: Pointer; // private data object passed to zalloc and zfree
  105. data_type: Integer; // best guess about the data type: ascii or binary
  106. adler: Integer; // adler32 value of the uncompressed data
  107. reserved: Integer; // reserved for future use
  108. end;
  109. {$ENDIF}
  110. EIdCompressionException = class(EIdException);
  111. EIdCompressorInitFailure = class(EIdCompressionException);
  112. EIdDecompressorInitFailure = class(EIdCompressionException);
  113. EIdCompressionError = class(EIdCompressionException);
  114. EIdDecompressionError = class(EIdCompressionException);
  115. TCompressionLevel = 0..9;
  116. TIdCompressionIntercept = class(TIdConnectionIntercept)
  117. protected
  118. FCompressionLevel: TCompressionLevel;
  119. FCompressRec: TZStreamRec;
  120. FDecompressRec: TZStreamRec;
  121. FRecvBuf: Pointer;
  122. FRecvCount, FRecvSize: Integer;
  123. FSendBuf: Pointer;
  124. FSendCount, FSendSize: Integer;
  125. procedure SetCompressionLevel(Value: TCompressionLevel);
  126. procedure InitCompressors;
  127. procedure DeinitCompressors;
  128. public
  129. destructor Destroy; override;
  130. procedure Disconnect; override;
  131. procedure Receive(ABuffer: TStream); override;
  132. procedure Send(ABuffer: TStream); override;
  133. published
  134. property CompressionLevel: TCompressionLevel read FCompressionLevel write SetCompressionLevel;
  135. end;
  136. TIdServerCompressionIntercept = class(TIdServerIntercept)
  137. private
  138. FCompressionLevel: TCompressionLevel;
  139. public
  140. procedure Init; override;
  141. function Accept(AConnection: TComponent): TIdConnectionIntercept; override;
  142. published
  143. property CompressionLevel: TCompressionLevel read FCompressionLevel write FCompressionLevel;
  144. end;
  145. implementation
  146. uses IdResourceStrings, SysUtils;
  147. {$IFNDEF USEZLIBUNIT}
  148. const
  149. Z_NO_FLUSH = 0;
  150. Z_PARTIAL_FLUSH = 1;
  151. Z_SYNC_FLUSH = 2;
  152. Z_FULL_FLUSH = 3;
  153. Z_FINISH = 4;
  154. Z_OK = 0;
  155. Z_STREAM_END = 1;
  156. Z_NEED_DICT = 2;
  157. Z_ERRNO = (-1);
  158. Z_STREAM_ERROR = (-2);
  159. Z_DATA_ERROR = (-3);
  160. Z_MEM_ERROR = (-4);
  161. Z_BUF_ERROR = (-5);
  162. Z_VERSION_ERROR = (-6);
  163. Z_NO_COMPRESSION = 0;
  164. Z_BEST_SPEED = 1;
  165. Z_BEST_COMPRESSION = 9;
  166. Z_DEFAULT_COMPRESSION = (-1);
  167. Z_FILTERED = 1;
  168. Z_HUFFMAN_ONLY = 2;
  169. Z_DEFAULT_STRATEGY = 0;
  170. Z_BINARY = 0;
  171. Z_ASCII = 1;
  172. Z_UNKNOWN = 2;
  173. Z_DEFLATED = 8;
  174. zlib_Version = '1.0.4'; {Do not Localize}
  175. {$IFDEF LINUX}
  176. zlib = 'libz.so.1'; {Do not Localize}
  177. {$ENDIF}
  178. {$IFDEF MSWINDOWS}
  179. {$L deflate.obj}
  180. {$L inflate.obj}
  181. {$L inftrees.obj}
  182. {$L trees.obj}
  183. {$L adler32.obj}
  184. {$L infblock.obj}
  185. {$L infcodes.obj}
  186. {$L infutil.obj}
  187. {$L inffast.obj}
  188. procedure _tr_init; external;
  189. procedure _tr_tally; external;
  190. procedure _tr_flush_block; external;
  191. procedure _tr_align; external;
  192. procedure _tr_stored_block; external;
  193. procedure adler32; external;
  194. procedure inflate_blocks_new; external;
  195. procedure inflate_blocks; external;
  196. procedure inflate_blocks_reset; external;
  197. procedure inflate_blocks_free; external;
  198. procedure inflate_set_dictionary; external;
  199. procedure inflate_trees_bits; external;
  200. procedure inflate_trees_dynamic; external;
  201. procedure inflate_trees_fixed; external;
  202. procedure inflate_trees_free; external;
  203. procedure inflate_codes_new; external;
  204. procedure inflate_codes; external;
  205. procedure inflate_codes_free; external;
  206. procedure _inflate_mask; external;
  207. procedure inflate_flush; external;
  208. procedure inflate_fast; external;
  209. procedure _memset(P: Pointer; B: Byte; count: Integer); cdecl;
  210. begin
  211. FillChar(P^, count, B);
  212. end;
  213. procedure _memcpy(dest, source: Pointer; count: Integer); cdecl;
  214. begin
  215. Move(source^, dest^, count);
  216. end;
  217. {$ENDIF}
  218. // deflate compresses data
  219. function deflateInit_(var strm: TZStreamRec; level: Integer; version: PChar;
  220. recsize: Integer): Integer;
  221. {$IFDEF MSWINDOWS}
  222. external;
  223. {$ENDIF}
  224. {$IFDEF LINUX}
  225. cdecl; external zlib name 'deflateInit_'; {Do not Localize}
  226. {$ENDIF}
  227. function deflate(var strm: TZStreamRec; flush: Integer): Integer;
  228. {$IFDEF MSWINDOWS}
  229. external;
  230. {$ENDIF}
  231. {$IFDEF LINUX}
  232. cdecl; external zlib name 'deflate'; {Do not Localize}
  233. {$ENDIF}
  234. function deflateEnd(var strm: TZStreamRec): Integer;
  235. {$IFDEF MSWINDOWS}
  236. external;
  237. {$ENDIF}
  238. {$IFDEF LINUX}
  239. cdecl; external zlib name 'deflateEnd'; {Do not Localize}
  240. {$ENDIF}
  241. // inflate decompresses data
  242. function inflateInit_(var strm: TZStreamRec; version: PChar;
  243. recsize: Integer): Integer;
  244. {$IFDEF MSWINDOWS}
  245. external;
  246. {$ENDIF}
  247. {$IFDEF LINUX}
  248. cdecl; external zlib name 'inflateInit_'; {Do not Localize}
  249. {$ENDIF}
  250. function inflate(var strm: TZStreamRec; flush: Integer): Integer;
  251. {$IFDEF MSWINDOWS}
  252. external;
  253. {$ENDIF}
  254. {$IFDEF LINUX}
  255. cdecl; external zlib name 'inflate'; {Do not Localize}
  256. {$ENDIF}
  257. function inflateEnd(var strm: TZStreamRec): Integer;
  258. {$IFDEF MSWINDOWS}
  259. external;
  260. {$ENDIF}
  261. {$IFDEF LINUX}
  262. cdecl; external zlib name 'inflateEnd'; {Do not Localize}
  263. {$ENDIF}
  264. function inflateReset(var strm: TZStreamRec): Integer;
  265. {$IFDEF MSWINDOWS}
  266. external;
  267. {$ENDIF}
  268. {$IFDEF LINUX}
  269. cdecl; external zlib name 'inflateReset'; {Do not Localize}
  270. {$ENDIF}
  271. function zlibAllocMem(AppData: Pointer; Items, Size: Integer): Pointer;
  272. {$IFDEF MSWINDOWS}
  273. register;
  274. {$ENDIF}
  275. {$IFDEF LINUX}
  276. cdecl;
  277. {$ENDIF}
  278. begin
  279. Result := AllocMem(Items * Size);
  280. end;
  281. procedure zlibFreeMem(AppData, Block: Pointer);
  282. {$IFDEF MSWINDOWS}
  283. register;
  284. {$ENDIF}
  285. {$IFDEF LINUX}
  286. cdecl;
  287. {$ENDIF}
  288. begin
  289. FreeMem(Block);
  290. end;
  291. {$ENDIF}
  292. { TIdCompressionIntercept }
  293. procedure TIdCompressionIntercept.DeinitCompressors;
  294. begin
  295. if Assigned(FCompressRec.zalloc) then
  296. begin
  297. deflateEnd(FCompressRec);
  298. FillChar(FCompressRec, SizeOf(FCompressRec), 0);
  299. end;
  300. if Assigned(FDecompressRec.zalloc) then
  301. begin
  302. inflateEnd(FDecompressRec);
  303. FillChar(FDecompressRec, SizeOf(FDecompressRec), 0);
  304. end;
  305. end;
  306. destructor TIdCompressionIntercept.Destroy;
  307. begin
  308. DeinitCompressors;
  309. FreeMem(FRecvBuf);
  310. FreeMem(FSendBuf);
  311. inherited;
  312. end;
  313. procedure TIdCompressionIntercept.Disconnect;
  314. begin
  315. inherited;
  316. DeinitCompressors;
  317. end;
  318. procedure TIdCompressionIntercept.InitCompressors;
  319. begin
  320. if not Assigned(FCompressRec.zalloc) then
  321. begin
  322. FCompressRec.zalloc := zlibAllocMem;
  323. FCompressRec.zfree := zlibFreeMem;
  324. if deflateInit_(FCompressRec, FCompressionLevel, zlib_Version, SizeOf(FCompressRec)) <> Z_OK then
  325. begin
  326. raise EIdCompressorInitFailure.Create(RSZLCompressorInitializeFailure);
  327. end;
  328. end;
  329. if not Assigned(FDecompressRec.zalloc) then
  330. begin
  331. FDecompressRec.zalloc := zlibAllocMem;
  332. FDecompressRec.zfree := zlibFreeMem;
  333. if inflateInit_(FDecompressRec, zlib_Version, SizeOf(FDecompressRec)) <> Z_OK then
  334. begin
  335. raise EIdDecompressorInitFailure.Create(RSZLDecompressorInitializeFailure);
  336. end;
  337. end;
  338. end;
  339. procedure TIdCompressionIntercept.Receive(ABuffer: TStream);
  340. var
  341. Buffer: array[0..2047] of Char;
  342. nChars, C: Integer;
  343. StreamEnd: Boolean;
  344. begin
  345. if FCompressionLevel in [1..9] then
  346. begin
  347. InitCompressors;
  348. StreamEnd := False;
  349. repeat
  350. nChars := ABuffer.Read(Buffer, SizeOf(Buffer));
  351. if nChars = 0 then Break;
  352. FDecompressRec.next_in := Buffer;
  353. FDecompressRec.avail_in := nChars;
  354. FDecompressRec.total_in := 0;
  355. while FDecompressRec.avail_in > 0 do
  356. begin
  357. if FRecvCount = FRecvSize then
  358. begin
  359. if FRecvSize = 0 then
  360. FRecvSize := 2048
  361. else
  362. Inc(FRecvSize, 1024);
  363. ReallocMem(FRecvBuf, FRecvSize);
  364. end;
  365. FDecompressRec.next_out := PChar(FRecvBuf) + FRecvCount;
  366. C := FRecvSize - FRecvCount;
  367. FDecompressRec.avail_out := C;
  368. FDecompressRec.total_out := 0;
  369. case inflate(FDecompressRec, Z_NO_FLUSH) of
  370. Z_STREAM_END:
  371. StreamEnd := True;
  372. Z_STREAM_ERROR,
  373. Z_DATA_ERROR,
  374. Z_MEM_ERROR:
  375. raise EIdDecompressionError.Create(RSZLDecompressionError);
  376. end;
  377. Inc(FRecvCount, C - FDecompressRec.avail_out);
  378. end;
  379. until StreamEnd;
  380. ABuffer.Size := 0;
  381. ABuffer.Write(FRecvBuf^, FRecvCount);
  382. FRecvCount := 0;
  383. end;
  384. end;
  385. procedure TIdCompressionIntercept.Send(ABuffer: TStream);
  386. var
  387. Buffer: array[0..1023] of Char;
  388. begin
  389. if FCompressionLevel in [1..9] then
  390. begin
  391. InitCompressors;
  392. // Make sure the Send buffer is large enough to hold the input stream data
  393. if ABuffer.Size > FSendSize then
  394. begin
  395. if ABuffer.Size > 2048 then
  396. FSendSize := ABuffer.Size + (ABuffer.Size + 1023) mod 1024
  397. else
  398. FSendSize := 2048;
  399. ReallocMem(FSendBuf, FSendSize);
  400. end;
  401. // Get the data from the input stream and save it off
  402. FSendCount := ABuffer.Read(FSendBuf^, ABuffer.Size);
  403. FCompressRec.next_in := FSendBuf;
  404. FCompressRec.avail_in := FSendCount;
  405. FCompressRec.avail_out := 0;
  406. // reset and clear the input stream in preparation for compression
  407. ABuffer.Size := 0;
  408. // As long as data is being outputted, keep compressing
  409. while FCompressRec.avail_out = 0 do
  410. begin
  411. FCompressRec.next_out := Buffer;
  412. FCompressRec.avail_out := SizeOf(Buffer);
  413. case deflate(FCompressRec, Z_SYNC_FLUSH) of
  414. Z_STREAM_ERROR,
  415. Z_DATA_ERROR,
  416. Z_MEM_ERROR: raise EIdCompressionError.Create(RSZLCompressionError);
  417. end;
  418. // Place the compressed data back into the input stream
  419. ABuffer.Write(Buffer, SizeOf(Buffer) - FCompressRec.avail_out);
  420. end;
  421. end;
  422. end;
  423. procedure TIdCompressionIntercept.SetCompressionLevel(Value: TCompressionLevel);
  424. begin
  425. if Value <> FCompressionLevel then
  426. begin
  427. DeinitCompressors;
  428. if Value < 0 then Value := 0;
  429. if Value > 9 then Value := 9;
  430. FCompressionLevel := Value;
  431. end;
  432. end;
  433. { TIdServerCompressionIntercept }
  434. function TIdServerCompressionIntercept.Accept(AConnection: TComponent): TIdConnectionIntercept;
  435. begin
  436. result := TIdCompressionIntercept.create(AConnection);
  437. (result as TIdCompressionIntercept).FCompressionLevel := FCompressionLevel;
  438. end;
  439. procedure TIdServerCompressionIntercept.Init;
  440. begin
  441. // nothing
  442. end;
  443. end.