IdCmdTCPClient.pas 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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.18 2/8/05 5:24:48 PM RLebeau
  18. Updated Disconnect() to not wait for the listening thread to terminate until
  19. after the inherited Disconnect() is called, so that the socket is actually
  20. disconnected and the thread can terminate properly.
  21. Rev 1.17 2/1/05 12:38:30 AM RLebeau
  22. Removed unused CommandHandlersEnabled property
  23. Rev 1.16 6/11/2004 8:48:16 AM DSiders
  24. Added "Do not Localize" comments.
  25. Rev 1.15 5/18/04 9:12:26 AM RLebeau
  26. Bug fix for SetExceptionReply() property setter
  27. Rev 1.14 5/16/04 5:18:04 PM RLebeau
  28. Added setter method to ExceptionReply property
  29. Rev 1.13 5/10/2004 6:10:38 PM DSiders
  30. Removed unused member var FCommandHandlersInitialized.
  31. Rev 1.12 2004.03.06 1:33:00 PM czhower
  32. -Change to disconnect
  33. -Addition of DisconnectNotifyPeer
  34. -WriteHeader now write bufers
  35. Rev 1.11 2004.03.01 5:12:24 PM czhower
  36. -Bug fix for shutdown of servers when connections still existed (AV)
  37. -Implicit HELP support in CMDserver
  38. -Several command handler bugs
  39. -Additional command handler functionality.
  40. Rev 1.10 2004.02.03 4:17:10 PM czhower
  41. For unit name changes.
  42. Rev 1.9 2004.01.20 10:03:22 PM czhower
  43. InitComponent
  44. Rev 1.8 1/4/04 8:46:16 PM RLebeau
  45. Added OnBeforeCommandHandler and OnAfterCommandHandler events
  46. Rev 1.7 11/4/2003 10:25:40 PM DSiders
  47. Removed duplicate FReplyClass member in TIdCmdTCPClient (See
  48. TIdTCPConnection).
  49. Rev 1.6 10/21/2003 10:54:20 AM JPMugaas
  50. Fix for new API change.
  51. Rev 1.5 2003.10.18 9:33:24 PM czhower
  52. Boatload of bug fixes to command handlers.
  53. Rev 1.4 2003.10.02 10:16:26 AM czhower
  54. .Net
  55. Rev 1.3 2003.09.19 11:54:26 AM czhower
  56. -Completed more features necessary for servers
  57. -Fixed some bugs
  58. Rev 1.2 7/9/2003 10:55:24 PM BGooijen
  59. Restored all features
  60. Rev 1.1 7/9/2003 04:36:06 PM JPMugaas
  61. You now can override the TIdReply with your own type. This should illiminate
  62. some warnings about some serious issues. TIdReply is ONLY a base class with
  63. virtual methods.
  64. Rev 1.0 7/7/2003 7:06:40 PM SPerry
  65. Component that uses command handlers
  66. Rev 1.0 7/6/2003 4:47:26 PM SPerry
  67. Units that use Command handlers
  68. }
  69. unit IdCmdTCPClient;
  70. {
  71. Original author: Sergio Perry
  72. Description: TCP client that uses CommandHandlers
  73. }
  74. interface
  75. {$I IdCompilerDefines.inc}
  76. uses
  77. IdContext,
  78. IdException,
  79. IdGlobal,
  80. IdReply,
  81. IdResourceStringsCore,
  82. IdThread,
  83. IdTCPClient,
  84. IdCommandHandlers;
  85. type
  86. TIdCmdTCPClient = class;
  87. { Events }
  88. TIdCmdTCPClientAfterCommandHandlerEvent = procedure(ASender: TIdCmdTCPClient;
  89. AContext: TIdContext) of object;
  90. TIdCmdTCPClientBeforeCommandHandlerEvent = procedure(ASender: TIdCmdTCPClient;
  91. var AData: string; AContext: TIdContext) of object;
  92. { Listening Thread }
  93. TIdCmdClientContext = class(TIdContext)
  94. protected
  95. FClient: TIdCmdTCPClient;
  96. public
  97. property Client: TIdCmdTCPClient read FClient;
  98. end;
  99. TIdCmdTCPClientListeningThread = class(TIdThread)
  100. protected
  101. FContext: TIdCmdClientContext;
  102. FClient: TIdCmdTCPClient;
  103. FRecvData: String;
  104. //
  105. procedure Run; override;
  106. public
  107. constructor Create(AClient: TIdCmdTCPClient); reintroduce;
  108. destructor Destroy; override;
  109. //
  110. property Client: TIdCmdTCPClient read FClient;
  111. property RecvData: String read FRecvData write FRecvData;
  112. end;
  113. { TIdCmdTCPClient }
  114. TIdCmdTCPClient = class(TIdTCPClient)
  115. protected
  116. FExceptionReply: TIdReply;
  117. FListeningThread: TIdCmdTCPClientListeningThread;
  118. FCommandHandlers: TIdCommandHandlers;
  119. FOnAfterCommandHandler: TIdCmdTCPClientAfterCommandHandlerEvent;
  120. FOnBeforeCommandHandler: TIdCmdTCPClientBeforeCommandHandlerEvent;
  121. //
  122. procedure DoAfterCommandHandler(ASender: TIdCommandHandlers; AContext: TIdContext);
  123. procedure DoBeforeCommandHandler(ASender: TIdCommandHandlers; var AData: string;
  124. AContext: TIdContext);
  125. procedure DoReplyUnknownCommand(AContext: TIdContext; ALine: string); virtual;
  126. function GetCmdHandlerClass: TIdCommandHandlerClass; virtual;
  127. procedure InitComponent; override;
  128. procedure SetCommandHandlers(AValue: TIdCommandHandlers);
  129. procedure SetExceptionReply(AValue: TIdReply);
  130. public
  131. procedure Connect; override;
  132. destructor Destroy; override;
  133. procedure Disconnect(ANotifyPeer: Boolean); override;
  134. published
  135. property CommandHandlers: TIdCommandHandlers read FCommandHandlers write SetCommandHandlers;
  136. property ExceptionReply: TIdReply read FExceptionReply write SetExceptionReply;
  137. //
  138. property OnAfterCommandHandler: TIdCmdTCPClientAfterCommandHandlerEvent
  139. read FOnAfterCommandHandler write FOnAfterCommandHandler;
  140. property OnBeforeCommandHandler: TIdCmdTCPClientBeforeCommandHandlerEvent
  141. read FOnBeforeCommandHandler write FOnBeforeCommandHandler;
  142. end;
  143. EIdCmdTCPClientError = class(EIdException);
  144. EIdCmdTCPClientConnectError = class(EIdCmdTCPClientError);
  145. implementation
  146. uses
  147. IdReplyRFC, SysUtils;
  148. type
  149. TIdCmdClientContextAccess = class(TIdCmdClientContext)
  150. end;
  151. { Listening Thread }
  152. constructor TIdCmdTCPClientListeningThread.Create(AClient: TIdCmdTCPClient);
  153. begin
  154. // TODO: move this into TIdCmdTCPClient itself so the Context is always
  155. // available even if the thread is not running...
  156. //
  157. FClient := AClient;
  158. FContext := TIdCmdClientContext.Create(AClient, nil, nil);
  159. FContext.FClient := AClient;
  160. TIdCmdClientContextAccess(FContext).FOwnsConnection := False;
  161. //
  162. inherited Create(False);
  163. end;
  164. destructor TIdCmdTCPClientListeningThread.Destroy;
  165. begin
  166. inherited Destroy;
  167. FreeAndNil(FContext);
  168. end;
  169. procedure TIdCmdTCPClientListeningThread.Run;
  170. begin
  171. FRecvData := FClient.IOHandler.ReadLn;
  172. if not FClient.CommandHandlers.HandleCommand(FContext, FRecvData) then begin
  173. FClient.DoReplyUnknownCommand(FContext, FRecvData);
  174. end;
  175. //Synchronize(?);
  176. if not Terminated then begin
  177. FClient.IOHandler.CheckForDisconnect;
  178. end;
  179. end;
  180. { TIdCmdTCPClient }
  181. destructor TIdCmdTCPClient.Destroy;
  182. begin
  183. Disconnect;
  184. FreeAndNil(FExceptionReply);
  185. FreeAndNil(FCommandHandlers);
  186. inherited Destroy;
  187. end;
  188. procedure TIdCmdTCPClient.Connect;
  189. begin
  190. inherited Connect;
  191. //
  192. try
  193. FListeningThread := TIdCmdTCPClientListeningThread.Create(Self);
  194. except
  195. Disconnect(True);
  196. IndyRaiseOuterException(EIdCmdTCPClientConnectError.Create(RSNoCreateListeningThread));
  197. end;
  198. end;
  199. procedure TIdCmdTCPClient.Disconnect(ANotifyPeer: Boolean);
  200. begin
  201. if Assigned(FListeningThread) then begin
  202. FListeningThread.Terminate;
  203. end;
  204. try
  205. inherited Disconnect(ANotifyPeer);
  206. finally
  207. if Assigned(FListeningThread) and not IsCurrentThread(FListeningThread) then begin
  208. FListeningThread.WaitFor;
  209. FreeAndNil(FListeningThread);
  210. end;
  211. end;
  212. end;
  213. procedure TIdCmdTCPClient.DoAfterCommandHandler(ASender: TIdCommandHandlers;
  214. AContext: TIdContext);
  215. begin
  216. if Assigned(OnAfterCommandHandler) then begin
  217. OnAfterCommandHandler(Self, AContext);
  218. end;
  219. end;
  220. procedure TIdCmdTCPClient.DoBeforeCommandHandler(ASender: TIdCommandHandlers;
  221. var AData: string; AContext: TIdContext);
  222. begin
  223. if Assigned(OnBeforeCommandHandler) then begin
  224. OnBeforeCommandHandler(Self, AData, AContext);
  225. end;
  226. end;
  227. procedure TIdCmdTCPClient.DoReplyUnknownCommand(AContext: TIdContext; ALine: string);
  228. begin
  229. end;
  230. function TIdCmdTCPClient.GetCmdHandlerClass: TIdCommandHandlerClass;
  231. begin
  232. Result := TIdCommandHandler;
  233. end;
  234. procedure TIdCmdTCPClient.InitComponent;
  235. var
  236. LHandlerClass: TIdCommandHandlerClass;
  237. begin
  238. inherited InitComponent;
  239. FExceptionReply := FReplyClass.Create(nil);
  240. ExceptionReply.SetReply(500, 'Unknown Internal Error'); {do not localize}
  241. LHandlerClass := GetCmdHandlerClass;
  242. FCommandHandlers := TIdCommandHandlers.Create(Self, FReplyClass, nil, ExceptionReply, LHandlerClass);
  243. FCommandHandlers.OnAfterCommandHandler := DoAfterCommandHandler;
  244. FCommandHandlers.OnBeforeCommandHandler := DoBeforeCommandHandler;
  245. end;
  246. procedure TIdCmdTCPClient.SetCommandHandlers(AValue: TIdCommandHandlers);
  247. begin
  248. FCommandHandlers.Assign(AValue);
  249. end;
  250. procedure TIdCmdTCPClient.SetExceptionReply(AValue: TIdReply);
  251. begin
  252. FExceptionReply.Assign(AValue);
  253. end;
  254. end.