IdCmdTCPClient.pas 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  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. {$I IdCompilerDefines.inc}
  75. interface
  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. 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. {$I IdObjectChecksOff.inc}
  161. TIdCmdClientContextAccess(FContext).FOwnsConnection := False;
  162. {$I IdObjectChecksOn.inc}
  163. //
  164. inherited Create(False);
  165. end;
  166. destructor TIdCmdTCPClientListeningThread.Destroy;
  167. begin
  168. inherited Destroy;
  169. FreeAndNil(FContext);
  170. end;
  171. procedure TIdCmdTCPClientListeningThread.Run;
  172. begin
  173. FRecvData := FClient.IOHandler.ReadLn;
  174. if not FClient.CommandHandlers.HandleCommand(FContext, FRecvData) then begin
  175. FClient.DoReplyUnknownCommand(FContext, FRecvData);
  176. end;
  177. //Synchronize(?);
  178. if not Terminated then begin
  179. FClient.IOHandler.CheckForDisconnect;
  180. end;
  181. end;
  182. { TIdCmdTCPClient }
  183. destructor TIdCmdTCPClient.Destroy;
  184. begin
  185. Disconnect;
  186. FreeAndNil(FExceptionReply);
  187. FreeAndNil(FCommandHandlers);
  188. inherited Destroy;
  189. end;
  190. procedure TIdCmdTCPClient.Connect;
  191. begin
  192. inherited Connect;
  193. //
  194. try
  195. FListeningThread := TIdCmdTCPClientListeningThread.Create(Self);
  196. except
  197. Disconnect(True);
  198. IndyRaiseOuterException(EIdCmdTCPClientConnectError.Create(RSNoCreateListeningThread));
  199. end;
  200. end;
  201. procedure TIdCmdTCPClient.Disconnect(ANotifyPeer: Boolean);
  202. begin
  203. if Assigned(FListeningThread) then begin
  204. FListeningThread.Terminate;
  205. end;
  206. try
  207. inherited Disconnect(ANotifyPeer);
  208. finally
  209. if Assigned(FListeningThread) and not IsCurrentThread(FListeningThread) then begin
  210. FListeningThread.WaitFor;
  211. FreeAndNil(FListeningThread);
  212. end;
  213. end;
  214. end;
  215. procedure TIdCmdTCPClient.DoAfterCommandHandler(ASender: TIdCommandHandlers;
  216. AContext: TIdContext);
  217. begin
  218. if Assigned(OnAfterCommandHandler) then begin
  219. OnAfterCommandHandler(Self, AContext);
  220. end;
  221. end;
  222. procedure TIdCmdTCPClient.DoBeforeCommandHandler(ASender: TIdCommandHandlers;
  223. var AData: string; AContext: TIdContext);
  224. begin
  225. if Assigned(OnBeforeCommandHandler) then begin
  226. OnBeforeCommandHandler(Self, AData, AContext);
  227. end;
  228. end;
  229. procedure TIdCmdTCPClient.DoReplyUnknownCommand(AContext: TIdContext; ALine: string);
  230. begin
  231. end;
  232. function TIdCmdTCPClient.GetCmdHandlerClass: TIdCommandHandlerClass;
  233. begin
  234. Result := TIdCommandHandler;
  235. end;
  236. procedure TIdCmdTCPClient.InitComponent;
  237. var
  238. LHandlerClass: TIdCommandHandlerClass;
  239. begin
  240. inherited InitComponent;
  241. FExceptionReply := FReplyClass.Create(nil);
  242. ExceptionReply.SetReply(500, 'Unknown Internal Error'); {do not localize}
  243. LHandlerClass := GetCmdHandlerClass;
  244. FCommandHandlers := TIdCommandHandlers.Create(Self, FReplyClass, nil, ExceptionReply, LHandlerClass);
  245. FCommandHandlers.OnAfterCommandHandler := DoAfterCommandHandler;
  246. FCommandHandlers.OnBeforeCommandHandler := DoBeforeCommandHandler;
  247. end;
  248. procedure TIdCmdTCPClient.SetCommandHandlers(AValue: TIdCommandHandlers);
  249. begin
  250. FCommandHandlers.Assign(AValue);
  251. end;
  252. procedure TIdCmdTCPClient.SetExceptionReply(AValue: TIdReply);
  253. begin
  254. FExceptionReply.Assign(AValue);
  255. end;
  256. end.