IdCustomTCPServer.pas 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183
  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. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.1 1/15/05 2:23:00 PM RLebeau
  18. Comment added to SetScheduler()
  19. Rev 1.0 12/2/2004 3:26:32 PM JPMugaas
  20. Moved most of TIdTCPServer here so we can use TIdTCPServer as an end point
  21. which requires an OnExecute event.
  22. Rev 1.68 11/29/04 11:50:26 PM RLebeau
  23. Updated ContextDisconected() to call DoDisconnect()
  24. Rev 1.67 11/27/04 3:28:36 AM RLebeau
  25. Updated to automatically set up the client IOHandler before calling
  26. DoConnect(), and to tear the IOHandler down before calling OnDisconnect().
  27. Rev 1.66 10/8/2004 10:11:02 PM BGooijen
  28. uncommented intercept code
  29. Rev 1.65 2004.08.13 10:55:38 czhower
  30. Removed IFDEF
  31. Rev 1.64 08.08.2004 10:43:10 OMonien
  32. temporary Thread.priority fix for Kylix
  33. Rev 1.63 6/11/2004 12:41:52 PM JPMugaas
  34. Reuse Address now reenabled.
  35. Rev 1.62 6/1/2004 1:22:28 PM DSiders
  36. Added TODO for TerminateWaitTimeout.
  37. Rev 1.61 28/04/2004 15:54:40 HHariri
  38. Changed thread priority for scheduler
  39. Rev 1.60 2004.04.22 11:44:48 PM czhower
  40. Boosted thread priority of listener thread.
  41. Rev 1.59 2004.03.06 10:40:34 PM czhower
  42. Changed IOHandler management to fix bug in server shutdowns.
  43. Rev 1.58 2004.03.01 5:12:40 PM czhower
  44. -Bug fix for shutdown of servers when connections still existed (AV)
  45. -Implicit HELP support in CMDserver
  46. -Several command handler bugs
  47. -Additional command handler functionality.
  48. Rev 1.57 2004.02.03 4:16:56 PM czhower
  49. For unit name changes.
  50. Rev 1.56 2004.01.20 10:03:36 PM czhower
  51. InitComponent
  52. Rev 1.55 1/3/2004 11:49:30 PM BGooijen
  53. the server creates a default binding for IPv6 now too, if IPv6 is supported
  54. Rev 1.54 2003.12.28 8:04:54 PM czhower
  55. Shutdown fix for .net.
  56. Rev 1.53 2003.11.29 6:03:46 PM czhower
  57. Active = True now works when set at design time.
  58. Rev 1.52 2003.10.21 12:19:02 AM czhower
  59. TIdTask support and fiber bug fixes.
  60. Rev 1.51 2003.10.18 9:33:30 PM czhower
  61. Boatload of bug fixes to command handlers.
  62. Rev 1.50 2003.10.18 8:04:28 PM czhower
  63. Fixed bug with setting active at design time.
  64. Rev 1.49 10/15/2003 11:10:00 PM DSiders
  65. Added localization comments.
  66. Added resource srting for exception raised in TIdTCPServer.SetScheduler.
  67. Rev 1.48 2003.10.15 4:34:38 PM czhower
  68. Bug fix for shutdown.
  69. Rev 1.47 2003.10.14 11:18:12 PM czhower
  70. Fix for AV on shutdown and other bugs
  71. Rev 1.46 2003.10.11 5:51:38 PM czhower
  72. -VCL fixes for servers
  73. -Chain suport for servers (Super core)
  74. -Scheduler upgrades
  75. -Full yarn support
  76. Rev 1.45 10/5/2003 9:55:26 PM BGooijen
  77. TIdTCPServer works on D7 and DotNet now
  78. Rev 1.44 10/5/2003 03:07:48 AM JPMugaas
  79. Should compile.
  80. Rev 1.43 2003.10.01 9:11:28 PM czhower
  81. .Net
  82. Rev 1.42 2003.09.30 1:23:08 PM czhower
  83. Stack split for DotNet
  84. Rev 1.41 2003.09.19 10:11:22 PM czhower
  85. Next stage of fiber support in servers.
  86. Rev 1.40 2003.09.19 11:54:34 AM czhower
  87. -Completed more features necessary for servers
  88. -Fixed some bugs
  89. Rev 1.39 2003.09.18 4:43:18 PM czhower
  90. -Removed IdBaseThread
  91. -Threads now have default names
  92. Rev 1.37 7/6/2003 8:04:10 PM BGooijen
  93. Renamed IdScheduler* to IdSchedulerOf*
  94. Rev 1.36 2003.06.30 9:41:06 PM czhower
  95. Fix for AV during server shut down.
  96. Rev 1.35 6/25/2003 3:57:58 PM BGooijen
  97. Disconnecting the context is now inside try...except
  98. Rev 1.34 6/8/2003 2:13:02 PM BGooijen
  99. Made ContextClass public
  100. Rev 1.33 6/5/2003 12:43:26 PM BGooijen
  101. changed short circuit fix code
  102. Rev 1.32 2003.06.04 10:14:08 AM czhower
  103. Removed short circuit dependency and fixed some older irrelevant code.
  104. Rev 1.31 6/3/2003 11:49:38 PM BGooijen
  105. removed AV in TIdTCPServer.DoExecute (hopefully)
  106. Rev 1.30 5/26/2003 04:29:58 PM JPMugaas
  107. Removed GenerateReply and ParseReply. Those are now obsolete duplicate
  108. functions in the new design.
  109. Rev 1.29 2003.05.26 10:35:26 PM czhower
  110. Fixed spelling typo.
  111. Rev 1.28 5/26/2003 12:20:00 PM JPMugaas
  112. Rev 1.27 2003.05.26 11:38:22 AM czhower
  113. Rev 1.26 5/25/2003 03:38:04 AM JPMugaas
  114. Rev 1.25 5/25/2003 03:26:38 AM JPMugaas
  115. Rev 1.24 5/20/2003 12:43:52 AM BGooijen
  116. changeable reply types
  117. Rev 1.23 5/13/2003 2:56:40 PM BGooijen
  118. changed GetGreating to SendGreeting
  119. Rev 1.21 4/4/2003 8:09:46 PM BGooijen
  120. moved some consts tidcmdtcpserver, changed DoExecute to return
  121. .connection.connected
  122. Rev 1.20 3/25/2003 9:04:06 PM BGooijen
  123. Scheduler in IOHandler is now updated when the scheduler is removed
  124. Rev 1.19 3/23/2003 11:33:34 PM BGooijen
  125. Updates the scheduler in the iohandler when scheduler/iohandler is changed
  126. Rev 1.18 3/22/2003 11:44:08 PM BGooijen
  127. ServerIntercept now logs connects/disconnects
  128. Rev 1.17 3/22/2003 1:46:02 PM BGooijen
  129. Better handling of exceptions in TIdListenerThread.Run (could cause mem leaks
  130. first (in non-paged-memory))
  131. Rev 1.16 3/21/2003 5:55:54 PM BGooijen
  132. Added code for serverIntercept
  133. Rev 1.15 3/21/2003 11:44:00 AM JPMugaas
  134. Updated with a OnBeforeConnect event for the TIdMappedPort components.
  135. Rev 1.14 3/20/2003 12:18:32 PM BGooijen
  136. Moved ReplyExceptionCode from TIdTCPServer to TIdCmdTCPServer
  137. Rev 1.13 3/13/2003 10:18:26 AM BGooijen
  138. Server side fibers, bug fixes
  139. Rev 1.12 2003.02.18 5:52:16 PM czhower
  140. Fix for warnings and logic error.
  141. Rev 1.11 1/23/2003 8:33:16 PM BGooijen
  142. Rev 1.10 1/23/2003 11:05:48 AM BGooijen
  143. Rev 1.9 1/20/2003 12:50:44 PM BGooijen
  144. Added a Contexts propperty, which contains all contexts for that server
  145. Moved the commandhandlers to TIdCmdTCPServer
  146. Rev 1.8 1-18-2003 0:00:30 BGooijen
  147. Removed TIdContext.OnCreate
  148. Added ContextCreated
  149. Rev 1.7 1-17-2003 23:44:32 BGooijen
  150. added support code for TIdContext.OnCreate
  151. Rev 1.6 1-17-2003 22:22:10 BGooijen
  152. new design
  153. Rev 1.5 1-10-2003 23:59:22 BGooijen
  154. Connection is now freed in destructor of TIdContext
  155. Rev 1.4 1-10-2003 19:46:22 BGooijen
  156. The context was not freed, now it is
  157. Rev 1.3 1-9-2003 11:52:28 BGooijen
  158. changed construction of TIdContext to Create(AServer: TIdTCPServer)
  159. added TIdContext property .Server
  160. Rev 1.2 1-3-2003 19:05:56 BGooijen
  161. added FContextClass:TIdContextClass to TIdTcpServer
  162. added Data:TObject to TIdContext
  163. Rev 1.1 1-1-2003 16:42:10 BGooijen
  164. Changed TIdThread to TIdYarn
  165. Added TIdContext
  166. Rev 1.0 11/13/2002 09:00:42 AM JPMugaas
  167. 2002-01-01 - Andrew P.Rybin
  168. - bug fix (MaxConnections, SetActive(FALSE)), TerminateListenerThreads, DoExecute
  169. 2002-04-17 - Andrew P.Rybin
  170. - bug fix: if exception raised in OnConnect, Threads.Remove and ThreadMgr.ReleaseThread are not called
  171. }
  172. unit IdCustomTCPServer;
  173. {
  174. Original Author and Maintainer:
  175. - Chad Z. Hower a.k.a Kudzu
  176. }
  177. interface
  178. {$I IdCompilerDefines.inc}
  179. //here to flip FPC into Delphi mode
  180. uses
  181. Classes,
  182. {$IFDEF HAS_UNIT_Generics_Collections}
  183. System.Generics.Collections,
  184. {$ENDIF}
  185. IdBaseComponent,
  186. IdComponent,IdContext, IdGlobal, IdException,
  187. IdIntercept, IdIOHandler, IdIOHandlerStack,
  188. IdReply, IdScheduler, IdServerIOHandler,
  189. IdServerIOHandlerStack, IdSocketHandle, IdTCPConnection,
  190. IdThread, IdYarn, SysUtils;
  191. const
  192. IdListenQueueDefault = 15;
  193. type
  194. TIdCustomTCPServer = class;
  195. // This is the thread that listens for incoming connections and spawns
  196. // new ones to handle each one
  197. TIdListenerThread = class(TIdThread)
  198. protected
  199. FBinding: TIdSocketHandle;
  200. FServer: TIdCustomTCPServer;
  201. FOnBeforeRun: TIdNotifyThreadEvent;
  202. //
  203. procedure AfterRun; override;
  204. procedure BeforeRun; override;
  205. procedure Run; override;
  206. public
  207. constructor Create(AServer: TIdCustomTCPServer; ABinding: TIdSocketHandle); reintroduce;
  208. //
  209. property Binding: TIdSocketHandle read FBinding;
  210. property Server: TIdCustomTCPServer read FServer;
  211. property OnBeforeRun: TIdNotifyThreadEvent read FOnBeforeRun write FOnBeforeRun;
  212. End;
  213. {$IFDEF HAS_GENERICS_TThreadList}
  214. TIdListenerThreadList = TThreadList<TIdListenerThread>;
  215. TIdListenerList = TList<TIdListenerThread>;
  216. {$ELSE}
  217. // TODO: flesh out to match TThreadList<TIdListenerThread> and TList<TIdListenerThread> for non-Generics compilers
  218. TIdListenerThreadList = TThreadList;
  219. TIdListenerList = TList;
  220. {$ENDIF}
  221. TIdListenExceptionEvent = procedure(AThread: TIdListenerThread; AException: Exception) of object;
  222. TIdServerThreadExceptionEvent = procedure(AContext: TIdContext; AException: Exception) of object;
  223. TIdServerThreadEvent = procedure(AContext: TIdContext) of object;
  224. TIdServerContext = class(TIdContext)
  225. protected
  226. FServer: TIdCustomTCPServer;
  227. public
  228. property Server: TIdCustomTCPServer read FServer;
  229. end;
  230. TIdServerContextClass = class of TIdServerContext;
  231. TIdCustomTCPServer = class(TIdComponent)
  232. protected
  233. FActive: Boolean;
  234. {$IF DEFINED(HAS_UNSAFE_OBJECT_REF)}[Unsafe]
  235. {$ELSEIF DEFINED(HAS_WEAK_OBJECT_REF}[Weak]
  236. {$IFEND} FScheduler: TIdScheduler;
  237. FBindings: TIdSocketHandles;
  238. FContextClass: TIdServerContextClass;
  239. {$IF DEFINED(HAS_UNSAFE_OBJECT_REF)}[Unsafe]
  240. {$ELSEIF DEFINED(HAS_WEAK_OBJECT_REF)}[Weak]
  241. {$IFEND} FIntercept: TIdServerIntercept;
  242. {$IF DEFINED(HAS_UNSAFE_OBJECT_REF)}[Unsafe]
  243. {$ELSEIF DEFINED(HAS_WEAK_OBJECT_REF)}[Weak]
  244. {$IFEND} FIOHandler: TIdServerIOHandler;
  245. FListenerThreads: TIdListenerThreadList;
  246. FListenQueue: integer;
  247. FMaxConnections: Integer;
  248. FReuseSocket: TIdReuseSocket;
  249. FTerminateWaitTime: Integer;
  250. FContexts: TIdContextThreadList;
  251. FOnContextCreated: TIdServerThreadEvent;
  252. FOnConnect: TIdServerThreadEvent;
  253. FOnDisconnect: TIdServerThreadEvent;
  254. FOnException: TIdServerThreadExceptionEvent;
  255. FOnExecute: TIdServerThreadEvent;
  256. FOnListenException: TIdListenExceptionEvent;
  257. FOnBeforeBind: TIdSocketHandleEvent;
  258. FOnAfterBind: TNotifyEvent;
  259. FOnBeforeListenerRun: TIdNotifyThreadEvent;
  260. FUseNagle : Boolean;
  261. //
  262. procedure CheckActive;
  263. procedure CheckOkToBeActive; virtual;
  264. procedure ContextCreated(AContext: TIdContext); virtual;
  265. procedure ContextConnected(AContext: TIdContext); virtual;
  266. procedure ContextDisconnected(AContext: TIdContext); virtual;
  267. function CreateConnection: TIdTCPConnection; virtual;
  268. procedure DoBeforeBind(AHandle: TIdSocketHandle); virtual;
  269. procedure DoAfterBind; virtual;
  270. procedure DoBeforeListenerRun(AThread: TIdThread); virtual;
  271. procedure DoConnect(AContext: TIdContext); virtual;
  272. procedure DoDisconnect(AContext: TIdContext); virtual;
  273. procedure DoException(AContext: TIdContext; AException: Exception); virtual;
  274. function DoExecute(AContext: TIdContext): Boolean; virtual;
  275. procedure DoListenException(AThread: TIdListenerThread; AException: Exception); virtual;
  276. procedure DoMaxConnectionsExceeded(AIOHandler: TIdIOHandler); virtual;
  277. procedure DoTerminateContext(AContext: TIdContext); virtual;
  278. function GetDefaultPort: TIdPort;
  279. procedure Loaded; override;
  280. {$IFDEF USE_OBJECT_REF_FREENOTIF}
  281. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  282. {$ENDIF}
  283. // This is needed for POP3's APOP authentication. For that,
  284. // you send a unique challenge to the client dynamically.
  285. procedure SendGreeting(AContext: TIdContext; AGreeting: TIdReply); virtual;
  286. procedure SetActive(AValue: Boolean); virtual;
  287. procedure SetBindings(const AValue: TIdSocketHandles); virtual;
  288. procedure SetDefaultPort(const AValue: TIdPort); virtual;
  289. procedure SetIntercept(const AValue: TIdServerIntercept); virtual;
  290. procedure SetIOHandler(const AValue: TIdServerIOHandler); virtual;
  291. procedure SetScheduler(const AValue: TIdScheduler); virtual;
  292. procedure Startup; virtual;
  293. procedure Shutdown; virtual;
  294. procedure TerminateAllThreads;
  295. // Occurs in the context of the peer thread
  296. property OnExecute: TIdServerThreadEvent read FOnExecute write FOnExecute;
  297. public
  298. constructor Create(AOwner: TComponent); override;
  299. destructor Destroy; override;
  300. //
  301. procedure StartListening;
  302. procedure StopListening;
  303. //
  304. property Contexts: TIdContextThreadList read FContexts;
  305. property ContextClass: TIdServerContextClass read FContextClass write FContextClass;
  306. published
  307. property Active: Boolean read FActive write SetActive default False;
  308. property Bindings: TIdSocketHandles read FBindings write SetBindings;
  309. property DefaultPort: TIdPort read GetDefaultPort write SetDefaultPort;
  310. property Intercept: TIdServerIntercept read FIntercept write SetIntercept;
  311. property IOHandler: TIdServerIOHandler read FIOHandler write SetIOHandler;
  312. property ListenQueue: integer read FListenQueue write FListenQueue default IdListenQueueDefault;
  313. property MaxConnections: Integer read FMaxConnections write FMaxConnections default 0;
  314. // right before/after binding sockets
  315. property OnBeforeBind: TIdSocketHandleEvent read FOnBeforeBind write FOnBeforeBind;
  316. property OnAfterBind: TNotifyEvent read FOnAfterBind write FOnAfterBind;
  317. property OnBeforeListenerRun: TIdNotifyThreadEvent read FOnBeforeListenerRun write FOnBeforeListenerRun;
  318. property OnContextCreated: TIdServerThreadEvent read FOnContextCreated write FOnContextCreated;
  319. // Occurs in the context of the peer thread
  320. property OnConnect: TIdServerThreadEvent read FOnConnect write FOnConnect;
  321. // Occurs in the context of the peer thread
  322. property OnDisconnect: TIdServerThreadEvent read FOnDisconnect write FOnDisconnect;
  323. // Occurs in the context of the peer thread
  324. property OnException: TIdServerThreadExceptionEvent read FOnException write FOnException;
  325. property OnListenException: TIdListenExceptionEvent read FOnListenException write FOnListenException;
  326. property ReuseSocket: TIdReuseSocket read FReuseSocket write FReuseSocket default rsOSDependent; // deprecated 'Use TIdSocketHandle.ReuseSocket';
  327. //UseNagle should be set to true in most cases.
  328. //See: http://tangentsoft.net/wskfaq/intermediate.html#disable-nagle and
  329. // http://tangentsoft.net/wskfaq/articles/lame-list.html#item19
  330. //The Nagle algorithm reduces the amount of needless traffic. Disabling Nagle
  331. //program’s throughput to degrade.
  332. property UseNagle: boolean read FUseNagle write FUseNagle default true;
  333. property TerminateWaitTime: Integer read FTerminateWaitTime write FTerminateWaitTime default 5000;
  334. property Scheduler: TIdScheduler read FScheduler write SetScheduler;
  335. end;
  336. EIdTCPServerError = class(EIdException);
  337. EIdNoExecuteSpecified = class(EIdTCPServerError);
  338. EIdTerminateThreadTimeout = class(EIdTCPServerError);
  339. implementation
  340. uses
  341. {$IF DEFINED(WINDOWS) AND DEFINED(DCC_2010_OR_ABOVE)}
  342. Windows,
  343. {$IFEND}
  344. IdGlobalCore,
  345. IdResourceStringsCore,
  346. IdSchedulerOfThreadDefault, IdStack,
  347. IdThreadSafe;
  348. { TIdCustomTCPServer }
  349. constructor TIdCustomTCPServer.Create(AOwner: TComponent);
  350. begin
  351. inherited Create(AOwner);
  352. FBindings := TIdSocketHandles.Create(Self);
  353. FContexts := TIdContextThreadList.Create;
  354. FContextClass := TIdServerContext;
  355. //
  356. FTerminateWaitTime := 5000;
  357. FListenQueue := IdListenQueueDefault;
  358. FListenerThreads := TIdListenerThreadList.Create;
  359. //TODO: When reestablished, use a sleeping thread instead
  360. // fSessionTimer := TTimer.Create(self);
  361. FUseNagle := true; // default
  362. end;
  363. procedure TIdCustomTCPServer.CheckActive;
  364. begin
  365. if Active and not (IsDesignTime or IsLoading) then begin
  366. raise EIdTCPServerError.Create(RSCannotPerformTaskWhileServerIsActive);
  367. end;
  368. end;
  369. procedure TIdCustomTCPServer.ContextCreated(AContext: TIdContext);
  370. begin
  371. if Assigned(FOnContextCreated) then begin
  372. FOnContextCreated(AContext);
  373. end;
  374. end;
  375. destructor TIdCustomTCPServer.Destroy;
  376. begin
  377. Active := False;
  378. SetIOHandler(nil);
  379. // Destroy bindings first
  380. FBindings.Free;
  381. //
  382. FContexts.Free;
  383. FListenerThreads.Free;
  384. //
  385. inherited Destroy;
  386. end;
  387. procedure TIdCustomTCPServer.DoBeforeBind(AHandle: TIdSocketHandle);
  388. begin
  389. if Assigned(FOnBeforeBind) then begin
  390. FOnBeforeBind(AHandle);
  391. end;
  392. end;
  393. procedure TIdCustomTCPServer.DoAfterBind;
  394. begin
  395. if Assigned(FOnAfterBind) then begin
  396. FOnAfterBind(Self);
  397. end;
  398. end;
  399. procedure TIdCustomTCPServer.SendGreeting(AContext: TIdContext; AGreeting: TIdReply);
  400. begin
  401. AContext.Connection.IOHandler.Write(AGreeting.FormattedReply);
  402. end;
  403. procedure TIdCustomTCPServer.ContextConnected(AContext: TIdContext);
  404. var
  405. // under ARC, convert weak references to strong references before working with them
  406. LServerIntercept: TIdServerIntercept;
  407. LConnIntercept: TIdConnectionIntercept;
  408. begin
  409. LServerIntercept := Intercept;
  410. if Assigned(LServerIntercept) then begin
  411. LConnIntercept := LServerIntercept.Accept(AContext.Connection);
  412. AContext.Connection.IOHandler.Intercept := LConnIntercept;
  413. if Assigned(LConnIntercept) then begin
  414. LConnIntercept.Connect(AContext.Connection);
  415. end;
  416. end;
  417. DoConnect(AContext);
  418. end;
  419. procedure TIdCustomTCPServer.ContextDisconnected(AContext: TIdContext);
  420. var
  421. // under ARC, convert weak references to strong references before working with them
  422. LIOHandler: TIdIOHandler;
  423. LIntercept: TIdConnectionIntercept;
  424. begin
  425. DoDisconnect(AContext);
  426. LIOHandler := AContext.Connection.IOHandler;
  427. if Assigned(LIOHandler) then begin
  428. LIntercept := LIOHandler.Intercept;
  429. if Assigned(LIntercept) then begin
  430. LIntercept.Disconnect;
  431. LIOHandler.Intercept := nil;
  432. LIntercept.Free;
  433. end;
  434. end;
  435. end;
  436. function TIdCustomTCPServer.CreateConnection: TIdTCPConnection;
  437. begin
  438. Result := TIdTCPConnection.Create(nil);
  439. end;
  440. procedure TIdCustomTCPServer.DoConnect(AContext: TIdContext);
  441. begin
  442. if Assigned(OnConnect) then begin
  443. OnConnect(AContext);
  444. end;
  445. end;
  446. procedure TIdCustomTCPServer.DoDisconnect(AContext: TIdContext);
  447. begin
  448. if Assigned(OnDisconnect) then begin
  449. OnDisconnect(AContext);
  450. end;
  451. end;
  452. procedure TIdCustomTCPServer.DoException(AContext: TIdContext; AException: Exception);
  453. begin
  454. if Assigned(OnException) then begin
  455. OnException(AContext, AException);
  456. end;
  457. end;
  458. function TIdCustomTCPServer.DoExecute(AContext: TIdContext): Boolean;
  459. var
  460. // under ARC, convert a weak reference to a strong reference before working with it
  461. LConn: TIdTCPConnection;
  462. begin
  463. if Assigned(OnExecute) then begin
  464. OnExecute(AContext);
  465. end;
  466. Result := False;
  467. if AContext <> nil then begin
  468. LConn := AContext.Connection;
  469. if LConn <> nil then begin
  470. Result := LConn.Connected;
  471. end;
  472. end;
  473. end;
  474. procedure TIdCustomTCPServer.DoListenException(AThread: TIdListenerThread; AException: Exception);
  475. begin
  476. if Assigned(FOnListenException) then begin
  477. FOnListenException(AThread, AException);
  478. end;
  479. end;
  480. function TIdCustomTCPServer.GetDefaultPort: TIdPort;
  481. begin
  482. Result := FBindings.DefaultPort;
  483. end;
  484. procedure TIdCustomTCPServer.Loaded;
  485. begin
  486. inherited Loaded;
  487. // Active = True must not be performed before all other props are loaded
  488. if Active then begin
  489. FActive := False;
  490. Active := True;
  491. end;
  492. end;
  493. // under ARC, all weak references to a freed object get nil'ed automatically
  494. {$IFDEF USE_OBJECT_REF_FREENOTIF}
  495. procedure TIdCustomTCPServer.Notification(AComponent: TComponent; Operation: TOperation);
  496. begin
  497. // Remove the reference to the linked components if they are deleted
  498. if (Operation = opRemove) then begin
  499. if (AComponent = FScheduler) then begin
  500. FScheduler := nil;
  501. end
  502. else if (AComponent = FIntercept) then begin
  503. FIntercept := nil;
  504. end
  505. else if (AComponent = FIOHandler) then begin
  506. FIOHandler := nil;
  507. end;
  508. end;
  509. inherited Notification(AComponent, Operation);
  510. end;
  511. {$ENDIF}
  512. procedure TIdCustomTCPServer.SetActive(AValue: Boolean);
  513. begin
  514. // At design time we just set the value and save it for run time.
  515. // During loading we ignore it till all other properties are set.
  516. // Loaded will recall it to toggle it
  517. if IsDesignTime or IsLoading then begin
  518. FActive := AValue;
  519. end
  520. else if FActive <> AValue then begin
  521. if AValue then begin
  522. CheckOkToBeActive;
  523. try
  524. Startup;
  525. except
  526. FActive := True;
  527. SetActive(False); // allow descendants to clean up
  528. raise;
  529. end;
  530. FActive := True;
  531. end else begin
  532. // Must set to False here. Shutdown() implementations call property setters that check this
  533. FActive := False;
  534. Shutdown;
  535. end;
  536. end;
  537. end;
  538. procedure TIdCustomTCPServer.SetBindings(const AValue: TIdSocketHandles);
  539. begin
  540. FBindings.Assign(AValue);
  541. end;
  542. procedure TIdCustomTCPServer.SetDefaultPort(const AValue: TIdPort);
  543. begin
  544. FBindings.DefaultPort := AValue;
  545. end;
  546. // RLebeau: not IFDEF'ing the entire method since it is virtual and could be
  547. // overridden in user code...
  548. procedure TIdCustomTCPServer.SetIntercept(const AValue: TIdServerIntercept);
  549. begin
  550. {$IFDEF USE_OBJECT_REF_FREENOTIF}
  551. if FIntercept <> AValue then
  552. begin
  553. // Remove self from the intercept's notification list
  554. if Assigned(FIntercept) then begin
  555. FIntercept.RemoveFreeNotification(Self);
  556. end;
  557. FIntercept := AValue;
  558. // Add self to the intercept's notification list
  559. if Assigned(FIntercept) then begin
  560. FIntercept.FreeNotification(Self);
  561. end;
  562. end;
  563. {$ELSE}
  564. // under ARC, all weak references to a freed object get nil'ed automatically
  565. FIntercept := AValue;
  566. {$ENDIF}
  567. end;
  568. procedure TIdCustomTCPServer.SetScheduler(const AValue: TIdScheduler);
  569. var
  570. // under ARC, convert weak references to strong references before working with them
  571. LScheduler: TIdScheduler;
  572. LIOHandler: TIdServerIOHandler;
  573. begin
  574. LScheduler := FScheduler;
  575. if LScheduler <> AValue then
  576. begin
  577. // RLebeau - is this really needed? What should happen if this
  578. // gets called by Notification() if the Scheduler is freed while
  579. // the server is still Active?
  580. if Active then begin
  581. raise EIdException.Create(RSTCPServerSchedulerAlreadyActive); // TODO: create a new Exception class for this
  582. end;
  583. // under ARC, all weak references to a freed object get nil'ed automatically
  584. // Free the default implicit Thread manager
  585. if Assigned(LScheduler) and (LScheduler.Owner = Self) then begin
  586. // Under D8 notification gets called after .Free of FreeAndNil, but before
  587. // its set to nil with a side effect of IDisposable. To counteract this we
  588. // set it to nil first.
  589. // -Kudzu
  590. FScheduler := nil;
  591. IdDisposeAndNil(LScheduler);
  592. end;
  593. {$IFDEF USE_OBJECT_REF_FREENOTIF}
  594. // Ensure we will no longer be notified when the component is freed
  595. if LScheduler <> nil then begin
  596. LScheduler.RemoveFreeNotification(Self);
  597. end;
  598. {$ENDIF}
  599. FScheduler := AValue;
  600. {$IFDEF USE_OBJECT_REF_FREENOTIF}
  601. // Ensure we will be notified when the component is freed, even is it's on
  602. // another form
  603. if AValue <> nil then begin
  604. AValue.FreeNotification(Self);
  605. end;
  606. {$ENDIF}
  607. LIOHandler := FIOHandler;
  608. if LIOHandler <> nil then begin
  609. LIOHandler.SetScheduler(AValue);
  610. end;
  611. end;
  612. end;
  613. procedure TIdCustomTCPServer.SetIOHandler(const AValue: TIdServerIOHandler);
  614. var
  615. // under ARC, convert a weak reference to a strong reference before working with it
  616. LIOHandler: TIdServerIOHandler;
  617. begin
  618. LIOHandler := FIOHandler;
  619. if LIOHandler <> AValue then begin
  620. // RLebeau - is this needed? SetScheduler() does it, so should SetIOHandler()
  621. // also do it? What should happen if this gets called by Notification() if the
  622. // IOHandler is freed while the server is still Active?
  623. {
  624. if Active then begin
  625. raise EIdException.Create(RSTCPServerIOHandlerAlreadyActive); // TODO: create a new Exception class for this
  626. end;
  627. }
  628. if Assigned(LIOHandler) and (LIOHandler.Owner = Self) then begin
  629. FIOHandler := nil;
  630. IdDisposeAndNil(LIOHandler);
  631. end;
  632. if Assigned(LIOHandler) then begin
  633. {$IFDEF USE_OBJECT_REF_FREENOTIF}
  634. // Ensure we will no longer be notified when the component is freed
  635. LIOHandler.RemoveFreeNotification(Self);
  636. {$ENDIF}
  637. // TODO: do we need this?
  638. // LIOHandler.SetScheduler(nil);
  639. end;
  640. FIOHandler := AValue;
  641. if AValue <> nil then begin
  642. {$IFDEF USE_OBJECT_REF_FREENOTIF}
  643. // Ensure we will be notified when the component is freed, even is it's on
  644. // another form
  645. AValue.FreeNotification(Self);
  646. {$ENDIF}
  647. AValue.SetScheduler(FScheduler);
  648. end;
  649. end;
  650. end;
  651. procedure TIdCustomTCPServer.StartListening;
  652. var
  653. LListenerThreads: TIdListenerList;
  654. LListenerThread: TIdListenerThread;
  655. I: Integer;
  656. LBinding: TIdSocketHandle;
  657. LName: string;
  658. begin
  659. LListenerThreads := FListenerThreads.LockList;
  660. try
  661. // Set up any sockets that are not already listening
  662. I := LListenerThreads.Count;
  663. try
  664. while I < Bindings.Count do begin
  665. LBinding := Bindings[I];
  666. LBinding.AllocateSocket;
  667. // do not overwrite if the default. This allows ReuseSocket to be set per binding
  668. if FReuseSocket <> rsOSDependent then begin
  669. LBinding.ReuseSocket := FReuseSocket;
  670. end;
  671. DoBeforeBind(LBinding);
  672. LBinding.Bind;
  673. LBinding.UseNagle := FUseNagle;
  674. Inc(I);
  675. end;
  676. except
  677. Dec(I); // the one that failed doesn't need to be closed
  678. while I >= 0 do begin
  679. Bindings[I].CloseSocket;
  680. Dec(I);
  681. end;
  682. raise;
  683. end;
  684. if I > LListenerThreads.Count then begin
  685. DoAfterBind;
  686. end;
  687. // Set up any threads that are not already running
  688. LName := Name;
  689. if LName = '' then begin
  690. LName := 'IdCustomTCPServer'; {do not localize}
  691. end;
  692. for I := LListenerThreads.Count to Bindings.Count - 1 do
  693. begin
  694. LBinding := Bindings[I];
  695. LBinding.Listen(FListenQueue);
  696. LListenerThread := TIdListenerThread.Create(Self, LBinding);
  697. try
  698. LListenerThread.Name := LName + ' Listener #' + IntToStr(I + 1); {do not localize}
  699. LListenerThread.OnBeforeRun := DoBeforeListenerRun;
  700. //Todo: Implement proper priority handling for Linux
  701. //http://www.midnightbeach.com/jon/pubs/2002/BorCon.London/Sidebar.3.html
  702. LListenerThread.Priority := tpListener;
  703. LListenerThreads.Add(LListenerThread);
  704. except
  705. LBinding.CloseSocket;
  706. LListenerThread.Free;
  707. raise;
  708. end;
  709. LListenerThread.Start;
  710. end;
  711. finally
  712. FListenerThreads.UnlockList;
  713. end;
  714. end;
  715. //APR-011207: for safe-close Ex: SQL Server ShutDown 1) stop listen 2) wait until all clients go out
  716. procedure TIdCustomTCPServer.StopListening;
  717. var
  718. LListenerThreads: TIdListenerList;
  719. LListener: TIdListenerThread;
  720. begin
  721. LListenerThreads := FListenerThreads.LockList;
  722. try
  723. // TODO: use two loops - one to close all of the sockets and signal all
  724. // of the threads to terminate, then another to free the threads.
  725. // This will be faster than doing everything one thread at a time...
  726. while LListenerThreads.Count > 0 do begin
  727. LListener := {$IFDEF HAS_GENERICS_TThreadList}LListenerThreads[0]{$ELSE}TIdListenerThread(LListenerThreads[0]){$ENDIF};
  728. // Stop listening
  729. LListener.Terminate;
  730. LListener.Binding.CloseSocket;
  731. // Tear down Listener thread
  732. LListener.WaitFor;
  733. LListener.Free;
  734. LListenerThreads.Delete(0); // RLebeau 2/17/2006
  735. end;
  736. finally
  737. FListenerThreads.UnlockList;
  738. end;
  739. end;
  740. //This is an ugly hack that's required because a ShortString does not seem
  741. //to be acceptable to D2009's Assert function.
  742. procedure AssertClassName(const ABool : Boolean; const AString : String); inline;
  743. begin
  744. Assert(ABool, AString);
  745. end;
  746. procedure TIdCustomTCPServer.TerminateAllThreads;
  747. var
  748. i: Integer;
  749. LContext: TIdContext;
  750. LList: TIdContextList;
  751. // under ARC, convert a weak reference to a strong reference before working with it
  752. LScheduler: TIdScheduler;
  753. begin
  754. // TODO: reimplement support for TerminateWaitTimeout
  755. //BGO: find out why TerminateAllThreads is sometimes called multiple times
  756. //Kudzu: Its because of notifications. It calls shutdown when the Scheduler is
  757. // set to nil and then again on destroy.
  758. if Contexts <> nil then begin
  759. LList := Contexts.LockList;
  760. try
  761. for i := 0 to LList.Count - 1 do begin
  762. LContext := {$IFDEF HAS_GENERICS_TList}LList.Items[i]{$ELSE}TIdContext(LList.Items[i]){$ENDIF};
  763. Assert(LContext<>nil);
  764. AssertClassName(LContext.Connection<>nil, LContext.ClassName);
  765. // RLebeau: allow descendants to perform their own cleanups before
  766. // closing the connection. FTP, for example, needs to abort an
  767. // active data transfer on a separate asociated connection
  768. DoTerminateContext(LContext);
  769. end;
  770. finally
  771. Contexts.UnLockList;
  772. end;
  773. end;
  774. // Scheduler may be nil during destroy which calls TerminateAllThreads
  775. // This happens with explicit schedulers
  776. LScheduler := FScheduler;
  777. if Assigned(LScheduler) then begin
  778. LScheduler.TerminateAllYarns;
  779. end;
  780. end;
  781. procedure TIdCustomTCPServer.DoBeforeListenerRun(AThread: TIdThread);
  782. begin
  783. if Assigned(OnBeforeListenerRun) then begin
  784. OnBeforeListenerRun(AThread);
  785. end;
  786. end;
  787. procedure TIdCustomTCPServer.DoMaxConnectionsExceeded(AIOHandler: TIdIOHandler);
  788. begin
  789. //
  790. end;
  791. procedure TIdCustomTCPServer.DoTerminateContext(AContext: TIdContext);
  792. begin
  793. // Dont call disconnect with true. Otherwise it frees the IOHandler and the thread
  794. // is still running which often causes AVs and other.
  795. //AContext.Connection.Disconnect(False);
  796. // RLebeau 9/10/2021: not calling disconnect here anymore. Just close the socket without
  797. // closing the IOHandler itself. Doing so can cause AVs and other, such as in
  798. // TIdSSLIOHandlerSocketOpenSSL, when Disconnect() calls IOHandler.Close() which
  799. // frees internal objects that may still be in use...
  800. AContext.Binding.CloseSocket;
  801. // TODO: since we are in the mist of a server shutdown, should the socket's SO_LINGER
  802. // option to enabled and set to 0 seconds to force an abortive (RSET) closure?
  803. end;
  804. procedure TIdCustomTCPServer.Shutdown;
  805. var
  806. // under ARC, convert weak references to strong references before working with them
  807. LIOHandler: TIdServerIOHandler;
  808. LScheduler: TIdScheduler;
  809. begin
  810. // tear down listening threads
  811. StopListening;
  812. // Tear down ThreadMgr
  813. try
  814. TerminateAllThreads;
  815. finally
  816. {//bgo TODO: fix this: and Threads.IsCountLessThan(1)}
  817. // DONE -oAPR: BUG! Threads still live, Mgr dead ;-(
  818. LScheduler := FScheduler;
  819. if Assigned(LScheduler) then begin
  820. if LScheduler.Owner = Self then begin
  821. {$IFDEF USE_OBJECT_ARC}LScheduler := nil;{$ENDIF}
  822. SetScheduler(nil);
  823. end else begin
  824. LScheduler := nil;
  825. end;
  826. end;
  827. end;
  828. LIOHandler := IOHandler;
  829. if LIOHandler <> nil then begin
  830. LIOHandler.Shutdown;
  831. end;
  832. end;
  833. // Linux/Unix does not allow an IPv4 socket and an IPv6 socket
  834. // to listen on the same port at the same time! Windows does not
  835. // have that problem...
  836. {$IF DEFINED(LINUX) OR DEFINED(SOLARIS) OR DEFINED(ANDROID)} // should this be UNIX instead?
  837. {$UNDEF CanCreateTwoBindings}
  838. {$ELSE}
  839. {$DEFINE CanCreateTwoBindings}
  840. {$IFEND}
  841. // TODO: Would this be solved by enabling the SO_REUSEPORT option on
  842. // platforms that support it?
  843. procedure TIdCustomTCPServer.Startup;
  844. var
  845. LScheduler: TIdScheduler;
  846. LIOHandler: TIdServerIOHandler;
  847. {$IFDEF CanCreateTwoBindings}
  848. LBinding: TIdSocketHandle;
  849. {$ENDIF}
  850. begin
  851. // Set up bindings
  852. if Bindings.Count = 0 then begin
  853. // TODO: on systems that support dual-stack sockets, create a single
  854. // Binding object that supports both IPv4 and IPv6 on the same socket...
  855. // TODO: remove the CanCreateTwoBindings conditional and just attempt
  856. // both IPv4 and IPv6 and ignore any failures...
  857. {$IFDEF CanCreateTwoBindings}LBinding := {$ENDIF}Bindings.Add; // IPv4 or IPv6 by default
  858. {$IFDEF CanCreateTwoBindings}
  859. // TODO: maybe add a property so the developer can switch this behavior on/off
  860. case LBinding.IPVersion of
  861. Id_IPv4: begin
  862. if GStack.SupportsIPv6 then begin
  863. Bindings.Add.IPVersion := Id_IPv6;
  864. end;
  865. end;
  866. Id_IPv6: begin
  867. if GStack.SupportsIPv4 then begin
  868. Bindings.Add.IPVersion := Id_IPv4;
  869. end;
  870. end;
  871. end;
  872. {$ENDIF}
  873. end;
  874. // Setup IOHandler
  875. LIOHandler := FIOHandler;
  876. if not Assigned(LIOHandler) then begin
  877. LIOHandler := TIdServerIOHandlerStack.Create(Self);
  878. SetIOHandler(LIOHandler);
  879. end;
  880. LIOHandler.Init;
  881. // Set up scheduler
  882. LScheduler := FScheduler;
  883. if not Assigned(FScheduler) then begin
  884. LScheduler := TIdSchedulerOfThreadDefault.Create(Self);
  885. SetScheduler(LScheduler);
  886. // Useful in debugging and for thread names
  887. LScheduler.Name := Name + 'Scheduler'; {do not localize}
  888. end;
  889. LScheduler.Init;
  890. StartListening;
  891. end;
  892. procedure TIdCustomTCPServer.CheckOkToBeActive;
  893. begin
  894. //nothing here. Override in a descendant for an end-point
  895. end;
  896. { TIdListenerThread }
  897. procedure TIdListenerThread.AfterRun;
  898. begin
  899. inherited AfterRun;
  900. // Close just own binding. The rest will be closed from their coresponding
  901. // threads
  902. FBinding.CloseSocket;
  903. end;
  904. procedure TIdListenerThread.BeforeRun;
  905. begin
  906. inherited BeforeRun;
  907. if Assigned(FOnBeforeRun) then begin
  908. FOnBeforeRun(Self);
  909. end;
  910. end;
  911. constructor TIdListenerThread.Create(AServer: TIdCustomTCPServer; ABinding: TIdSocketHandle);
  912. begin
  913. inherited Create;
  914. FBinding := ABinding;
  915. FServer := AServer;
  916. end;
  917. type
  918. TIdServerContextAccess = class(TIdServerContext)
  919. end;
  920. procedure TIdListenerThread.Run;
  921. var
  922. LContext: TIdServerContext;
  923. LIOHandler: TIdIOHandler;
  924. LPeer: TIdTCPConnection;
  925. LYarn: TIdYarn;
  926. begin
  927. Assert(Server<>nil);
  928. Assert(Server.IOHandler<>nil);
  929. LContext := nil;
  930. LPeer := nil;
  931. LYarn := nil;
  932. try
  933. // GetYarn can raise exceptions
  934. LYarn := Server.Scheduler.AcquireYarn;
  935. // TODO: under Windows at least, use SO_CONDITIONAL_ACCEPT to allow
  936. // the user to reject connections before they are accepted. Somehow
  937. // expose an event here for the user to decide with...
  938. LIOHandler := Server.IOHandler.Accept(Binding, Self, LYarn);
  939. if LIOHandler = nil then begin
  940. // Listening has finished
  941. Stop;
  942. Abort;
  943. end else begin
  944. // We have accepted the connection and need to handle it
  945. LPeer := TIdTCPConnection.Create(nil);
  946. // under ARC, the TIdTCPConnection.IOHandler property is a weak/unsafe reference.
  947. // TIdServerIOHandler.Accept() returns an IOHandler with no Owner assigned,
  948. // so lets make the TIdTCPConnection become the Owner in order to keep the
  949. // IOHandler alive when this method exits.
  950. //
  951. // Let's assign Ownership unconditionally on all platforms...
  952. //
  953. LPeer.InsertComponent(LIOHandler);
  954. LPeer.IOHandler := LIOHandler;
  955. end;
  956. // LastRcvTimeStamp := Now; // Added for session timeout support
  957. // ProcessingTimeout := False;
  958. // Check MaxConnections
  959. if (Server.MaxConnections > 0) and (not Server.Contexts.IsCountLessThan(Server.MaxConnections)) then begin
  960. FServer.DoMaxConnectionsExceeded(LIOHandler);
  961. LPeer.Disconnect;
  962. Abort;
  963. end;
  964. // Create and init context
  965. LContext := Server.FContextClass.Create(LPeer, LYarn, Server.Contexts);
  966. LContext.FServer := Server;
  967. // We set these instead of having the context call them directly
  968. // because they are protected methods. Also its good to keep
  969. // Context indepent of the server as well.
  970. LContext.OnBeforeRun := Server.ContextConnected;
  971. LContext.OnRun := Server.DoExecute;
  972. LContext.OnAfterRun := Server.ContextDisconnected;
  973. LContext.OnException := Server.DoException;
  974. //
  975. Server.ContextCreated(LContext);
  976. //
  977. // If all ok, lets start the yarn
  978. Server.Scheduler.StartYarn(LYarn, LContext);
  979. except
  980. on E: Exception do begin
  981. // RLebeau 1/11/07: TIdContext owns the Peer by default so
  982. // take away ownership here so the Peer is not freed twice
  983. if LContext <> nil then begin
  984. {$I IdObjectChecksOff.inc}
  985. TIdServerContextAccess(LContext).FOwnsConnection := False;
  986. {$I IdObjectChecksOn.inc}
  987. end;
  988. LContext.Free;
  989. LPeer.Free;
  990. // Must terminate - likely has not started yet
  991. if LYarn <> nil then begin
  992. Server.Scheduler.TerminateYarn(LYarn);
  993. end;
  994. // EAbort is used to kick out above and destroy yarns and other, but
  995. // we dont want to show the user
  996. // TODO: should we include EIdSilentException here, too?
  997. // To ignore EIdConnClosedGracefully, for instance...
  998. if not (E is EAbort) then begin
  999. Server.DoListenException(Self, E);
  1000. end;
  1001. end;
  1002. end;
  1003. end;
  1004. end.