fpwebsocketclient.pp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. {
  2. $Id: header,v 1.1 2000/07/13 06:33:45 michael Exp $
  3. This file is part of the Free Component Library (FCL)
  4. Copyright (c) 2021 - by the Free Pascal development team
  5. Websocket client implementation.
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$mode ObjFPC}{$H+}
  13. unit fpwebsocketclient;
  14. interface
  15. uses
  16. sysutils, classes, fpwebsocket, ssockets, sslsockets, fpopenssl;
  17. Type
  18. EWebSocketClient = Class(EWebSocket);
  19. TWSClientHandShakeEvent = Procedure(Sender : TObject; aHeaders : TStrings) of Object;
  20. TWSClientHandShakeResponseEvent = Procedure(Sender : TObject; aResponse : TWSHandShakeResponse; Var aAllow : Boolean) of Object;
  21. TWSErrorEvent = Procedure (Sender : TObject; E : Exception) of object;
  22. { TWSMessagePump }
  23. TWSMessagePump = Class (TComponent)
  24. private
  25. FInterval:Integer;
  26. FList: TThreadList;
  27. FReads: TSocketStreamArray;
  28. FExceptions : TSocketStreamArray;
  29. FOnError: TWSErrorEvent;
  30. procedure SetInterval(AValue: Integer);
  31. Protected
  32. function WaitForData: Boolean;
  33. Function CheckConnections : Boolean; virtual;
  34. Procedure ReadConnections;
  35. Property List : TThreadList Read FList;
  36. Public
  37. Constructor Create(aOwner : TComponent); override;
  38. Destructor Destroy; override;
  39. Procedure AddClient(aConnection : TWSClientConnection);
  40. Procedure RemoveClient(aConnection : TWSClientConnection);
  41. Procedure Execute; virtual; abstract;
  42. Procedure Terminate; virtual; abstract;
  43. Property Interval : Integer Read FInterval Write SetInterval;
  44. Property OnError : TWSErrorEvent Read FOnError Write FOnError;
  45. End;
  46. // Default message driver, works with thread that checks sockets for available data
  47. TWSThreadMessagePump = Class(TWSMessagePump)
  48. Private
  49. FThread : TThread;
  50. Procedure ThreadTerminated(Sender : TObject);
  51. Protected
  52. Type
  53. TMessageDriverThread = Class(TThread)
  54. Public
  55. FPump : TWSThreadMessagePump;
  56. Constructor Create(aPump : TWSThreadMessagePump; aTerminate : TNotifyEvent);
  57. Procedure Execute;override;
  58. End;
  59. Public
  60. Procedure Execute; override;
  61. Procedure Terminate; override;
  62. End;
  63. TCustomWebsocketClient = class;
  64. { TWebSocketClientConnection }
  65. TWebSocketClientConnection = class(TWSClientConnection)
  66. protected
  67. Procedure DoDisconnect; override;
  68. function GetClient: TCustomWebsocketClient; virtual;
  69. Public
  70. Property WebsocketClient : TCustomWebsocketClient Read GetClient;
  71. end;
  72. { TCustomWebsocketClient }
  73. TCustomWebsocketClient = Class(TComponent)
  74. private
  75. FOutGoingFrameMask: Integer;
  76. FPort: Integer;
  77. FActive: Boolean;
  78. FLoadActive : Boolean;
  79. FHostName: String;
  80. FUseSSL: Boolean;
  81. FResource: string;
  82. FConnectTimeout: Integer;
  83. FOptions: TWSOptions;
  84. FSocket : TInetSocket;
  85. FTransport : TWSClientTransport;
  86. FCheckTimeOut: Integer;
  87. FAutoCheckMessages: Boolean;
  88. FHandShake : TWSHandShakeRequest;
  89. FMessagePump: TWSMessagePump; // Do not free
  90. FHandshakeResponse: TWSHandShakeResponse;
  91. FOnSendHandShake: TWSClientHandshakeEvent;
  92. FOnHandshakeResponse: TWSClientHandshakeResponseEvent;
  93. FConnection: TWebSocketClientConnection;
  94. FOnMessageReceived: TWSMessageEvent;
  95. FOnControl: TWSControlEvent;
  96. FOnDisconnect: TNotifyEvent;
  97. FOnConnect: TNotifyEvent;
  98. procedure FreeConnectionObjects;
  99. procedure SetActive(const Value: Boolean);
  100. procedure SetHostName(const Value: String);
  101. procedure SetMessagePump(AValue: TWSMessagePump);
  102. procedure SetPort(const Value: Integer);
  103. procedure SetUseSSL(const Value: Boolean);
  104. procedure SetConnectTimeout(const Value: Integer);
  105. procedure SetResource(const Value: string);
  106. procedure SetCheckTimeOut(const Value: Integer);
  107. procedure SetOptions(const Value: TWSOptions);
  108. procedure SetAutoCheckMessages(const Value: Boolean);
  109. procedure SendHeaders(aHeaders: TStrings);
  110. procedure ConnectionDisconnected(Sender: TObject);
  111. Protected
  112. Procedure CheckInactive;
  113. Procedure Loaded; override;
  114. function CreateClientConnection(aTransport : TWSClientTransport): TWebSocketClientConnection; virtual;
  115. procedure MessageReceived(Sender: TObject; const aMessage : TWSMessage);
  116. Procedure ControlReceived(Sender: TObject; aType : TFrameType; const aData: TBytes);virtual;
  117. function CheckHandShakeResponse(aHeaders: TStrings): Boolean; virtual;
  118. function CreateHandShakeRequest: TWSHandShakeRequest; virtual;
  119. function CreateHandshakeResponse(aHeaders: TStrings): TWSHandShakeResponse; virtual;
  120. procedure SendHandShakeRequest; virtual;
  121. function ReadHandShakeResponse: Boolean; virtual;
  122. Function DoHandShake: Boolean;
  123. Property Transport: TWSClientTransport Read FTransport;
  124. Public
  125. Property Connection: TWebSocketClientConnection Read FConnection;
  126. Public
  127. Destructor Destroy; override;
  128. // Check for incoming messages
  129. Function CheckIncoming : TIncomingResult;
  130. // Connect and perform handshake
  131. Procedure Connect;
  132. // Disconnect from server.
  133. Procedure Disconnect(SendClose : boolean = true);
  134. // Send a ping message
  135. Procedure Ping(aMessage: UTF8String);
  136. // Send a pong message
  137. Procedure Pong(aMessage: UTF8String);
  138. // Send raw data (ftBinary)
  139. Procedure SendData(aBytes : TBytes);
  140. // Send a string message
  141. Procedure SendMessage(Const aMessage : String);
  142. Public
  143. // Connect/Disconnect
  144. Property Active : Boolean Read FActive Write SetActive;
  145. // Check for message timeout
  146. Property CheckTimeOut : Integer Read FCheckTimeOut Write SetCheckTimeOut;
  147. // Timeout for connect
  148. Property ConnectTimeout : Integer Read FConnectTimeout Write SetConnectTimeout;
  149. // Host to connect to
  150. Property HostName : String Read FHostName Write SetHostName;
  151. // Message driver
  152. Property MessagePump : TWSMessagePump Read FMessagePump Write SetMessagePump;
  153. // Options
  154. Property Options : TWSOptions Read FOptions Write SetOptions;
  155. // Mask to use for outgoing frames
  156. Property OutGoingFrameMask : Integer Read FOutGoingFrameMask Write FOutGoingFrameMask;
  157. // Port to connect to
  158. Property Port : Integer Read FPort Write SetPort;
  159. // Path/Document in HTTP URL for GET request
  160. Property Resource : string Read FResource Write SetResource;
  161. // User SSL when connecting
  162. Property UseSSL : Boolean Read FUseSSL Write SetUseSSL;
  163. // Events
  164. // Called when handshake is about to be sent
  165. Property OnSendHandShake : TWSClientHandshakeEvent Read FOnSendHandShake Write FOnSendHandshake;
  166. // Called when handshake response is received
  167. Property OnHandshakeResponse : TWSClientHandshakeResponseEvent Read FOnHandshakeResponse Write FOnHandshakeResponse;
  168. // Called when a text message is received.
  169. property OnMessageReceived: TWSMessageEvent read FOnMessageReceived write FOnMessageReceived;
  170. // Called when a connection is disconnected.
  171. property OnDisconnect: TNotifyEvent read FOnDisconnect write FOnDisconnect;
  172. // Called when a connection is established
  173. property OnConnect: TNotifyEvent read FOnConnect write FOnConnect;
  174. // Called when a control message is received.
  175. property OnControl: TWSControlEvent read FOnControl write FOnControl;
  176. End;
  177. TWebsocketClient = Class(TCustomWebsocketClient)
  178. Published
  179. Property HostName;
  180. Property Port;
  181. Property CheckTimeOut;
  182. Property ConnectTimeout;
  183. Property MessagePump;
  184. Property Options;
  185. Property Resource;
  186. Property UseSSL;
  187. Property OnSendHandShake;
  188. Property OnHandshakeResponse;
  189. property OnMessageReceived;
  190. property OnDisconnect;
  191. property OnConnect;
  192. property OnControl;
  193. Property OutGoingFrameMask;
  194. End;
  195. implementation
  196. uses sha1;
  197. { TWebSocketClientConnection }
  198. procedure TWebSocketClientConnection.DoDisconnect;
  199. begin
  200. If Assigned(WebSocketClient) then
  201. WebSocketClient.ConnectionDisconnected(Self);
  202. end;
  203. function TWebSocketClientConnection.GetClient: TCustomWebsocketClient;
  204. begin
  205. Result:=Owner as TCustomWebsocketClient;
  206. end;
  207. { TCustomWebsocketClient }
  208. procedure TCustomWebsocketClient.CheckInactive;
  209. begin
  210. If Active then
  211. Raise EWebSocketClient.Create(SErrConnectionActive);
  212. end;
  213. Function TCustomWebsocketClient.CheckIncoming : TIncomingResult;
  214. begin
  215. If Not Active then
  216. Raise EWebSocketClient.Create(SErrConnectionInActive);
  217. if Not Connection.HandshakeCompleted then
  218. Raise EWebSocketClient.Create(SErrHandshakeInComplete);
  219. Result:=Connection.CheckIncoming(CheckTimeout);
  220. if (Result=irClose) then
  221. begin
  222. Disconnect(False);
  223. end;
  224. end;
  225. procedure TCustomWebsocketClient.ControlReceived(Sender: TObject; aType : TFrameType; const aData: TBytes);
  226. begin
  227. If Assigned(FOnControl) then
  228. FOnControl(Sender, aType, aData);
  229. end;
  230. function TCustomWebsocketClient.CreateClientConnection(aTransport: TWSClientTRansport): TWebsocketClientConnection;
  231. begin
  232. Result:=TWebSocketClientConnection.Create(Self,aTransport,FOptions);
  233. end;
  234. procedure TCustomWebsocketClient.ConnectionDisconnected(Sender : TObject);
  235. begin
  236. FActive:=False;
  237. If Assigned(MessagePump) then
  238. MessagePump.RemoveClient(FConnection);
  239. If Assigned(OnDisconnect) then
  240. OnDisconnect(Self);
  241. // We cannot free the connection here, because it still needs to call it's own OnDisconnect.
  242. end;
  243. procedure TCustomWebsocketClient.Connect;
  244. var
  245. SSLHandler: TSSLSocketHandler;
  246. begin
  247. If Active then
  248. Exit;
  249. // Safety: Free any dangling objects before recreating
  250. FreeConnectionObjects;
  251. SSLHandler := nil;
  252. if UseSSL then
  253. begin
  254. SSLHandler := TSSLSocketHandler.GetDefaultHandler;
  255. SSLHandler.VerifyPeerCert := False;
  256. end;
  257. FSocket:=TInetSocket.Create(HostName,Port,ConnectTimeout, SSLHandler);
  258. FTransport:=TWSClientTransport.Create(FSocket);
  259. FConnection:=CreateClientConnection(FTransport);
  260. FConnection.OnMessageReceived:=@MessageReceived;
  261. FConnection.OnControl:=@ControlReceived;
  262. FCOnnection.OutgoingFrameMask:=Self.OutGoingFrameMask;
  263. if UseSSL then
  264. FSocket.Connect;
  265. FActive:=True;
  266. if not DoHandShake then
  267. Disconnect(False)
  268. else
  269. begin
  270. If Assigned(MessagePump) then
  271. MessagePump.AddClient(FConnection);
  272. if Assigned(OnConnect) then
  273. OnConnect(Self);
  274. end;
  275. end;
  276. destructor TCustomWebsocketClient.Destroy;
  277. begin
  278. DisConnect(False);
  279. FreeAndNil(FHandShake);
  280. FreeAndNil(FHandshakeResponse);
  281. FreeConnectionObjects;
  282. Inherited;
  283. end;
  284. Function TCustomWebsocketClient.CreateHandShakeRequest : TWSHandShakeRequest;
  285. begin
  286. Result:=TWSHandShakeRequest.Create('',Nil);
  287. end;
  288. procedure TCustomWebsocketClient.SendData(aBytes: TBytes);
  289. begin
  290. Connection.Send(aBytes);
  291. end;
  292. procedure TCustomWebsocketClient.SendHeaders(aHeaders : TStrings);
  293. Var
  294. S : String;
  295. B : TBytes;
  296. begin
  297. for S in AHeaders do
  298. begin
  299. B:=TEncoding.UTF8.GetAnsiBytes(S+#13#10);
  300. Connection.Transport.WriteBytes(B,Length(B));
  301. end;
  302. B:=TEncoding.UTF8.GetAnsiBytes(#13#10);
  303. Connection.Transport.WriteBytes(B,Length(B));
  304. end;
  305. procedure TCustomWebsocketClient.SendHandShakeRequest;
  306. Var
  307. aRequest : TWSHandShakeRequest;
  308. aHeaders : TStrings;
  309. begin
  310. aHeaders:=Nil;
  311. FreeAndNil(FHandShake);
  312. aRequest:=CreateHandShakeRequest;
  313. try
  314. aRequest.Host:=HostName;
  315. aRequest.Port:=Port;
  316. aRequest.Resource:=Resource;
  317. aHeaders:=TStringList.Create;
  318. aHeaders.NameValueSeparator:=':';
  319. aRequest.ToStrings(aHeaders);
  320. if Assigned(FOnSendHandshake) then
  321. FOnSendHandshake(self,aHeaders);
  322. // Do not use FClient.WriteHeader, it messes up the strings !
  323. SendHeaders(aHeaders);
  324. FHandShake:=aRequest;
  325. finally
  326. aHeaders.Free;
  327. if FhandShake<>aRequest then
  328. aRequest.Free;
  329. end;
  330. end;
  331. procedure TCustomWebsocketClient.SendMessage(const aMessage: String);
  332. begin
  333. Connection.Send(aMessage);
  334. end;
  335. Function TCustomWebsocketClient.CreateHandshakeResponse(aHeaders : TStrings) : TWSHandShakeResponse;
  336. begin
  337. Result:=TWSHandShakeResponse.Create('',aHeaders);
  338. end;
  339. Function TCustomWebsocketClient.CheckHandShakeResponse(aHeaders : TStrings) : Boolean;
  340. Var
  341. K : String;
  342. {%H-}hash : TSHA1Digest;
  343. B : TBytes;
  344. begin
  345. B:=[];
  346. FreeAndNil(FHandshakeResponse);
  347. FHandshakeResponse:=CreateHandshakeResponse(aHeaders);
  348. k := Trim(FHandshake.Key) + SSecWebSocketGUID;
  349. hash:=sha1.SHA1String(k);
  350. SetLength(B,SizeOf(hash));
  351. Move(hash[0],B[0],SizeOf(hash));
  352. k:=EncodeBytesBase64(B);
  353. Result:=SameText(K,FHandshakeResponse.Accept)
  354. and SameText(FHandshakeResponse.Upgrade,'websocket');
  355. end;
  356. Function TCustomWebsocketClient.ReadHandShakeResponse : Boolean;
  357. Var
  358. S : String;
  359. aHeaders : TStrings;
  360. begin
  361. Result:=False;
  362. aHeaders:=TStringList.Create;
  363. Try
  364. aHeaders.NameValueSeparator:=':';
  365. Repeat
  366. S:=Connection.Transport.ReadLn;
  367. aHeaders.Add(S);
  368. Until (S='');
  369. Result:=CheckHandShakeResponse(aHeaders);
  370. if Result and Assigned(FOnHandshakeResponse) then
  371. FOnHandshakeResponse(Self,FHandShakeResponse,Result);
  372. if Result then
  373. FConnection.HandshakeResponse:=FHandShakeResponse
  374. Finally
  375. aHeaders.Free;
  376. End;
  377. end;
  378. Function TCustomWebsocketClient.DoHandShake : Boolean;
  379. begin
  380. SendHandShakeRequest;
  381. Result:=ReadHandShakeResponse;
  382. end;
  383. procedure TCustomWebsocketClient.Loaded;
  384. begin
  385. inherited;
  386. if FLoadActive then
  387. Connect;
  388. end;
  389. procedure TCustomWebsocketClient.MessageReceived(Sender: TObject; const aMessage : TWSMessage) ;
  390. begin
  391. if Assigned(OnMessageReceived) and (TWSClientConnection(Sender).HandshakeCompleted) then
  392. OnMessageReceived(Self, AMessage);
  393. end;
  394. procedure TCustomWebsocketClient.Ping(aMessage: UTF8String);
  395. begin
  396. FConnection.Send(ftPing,TEncoding.UTF8.GetAnsiBytes(aMessage));
  397. end;
  398. procedure TCustomWebsocketClient.Pong(aMessage: UTF8String);
  399. begin
  400. FConnection.Send(ftPong,TEncoding.UTF8.GetAnsiBytes(aMessage));
  401. end;
  402. procedure TCustomWebsocketClient.FreeConnectionObjects;
  403. begin
  404. FreeAndNil(FConnection);
  405. FreeAndNil(FTransport);
  406. FreeAndNil(FSocket);
  407. end;
  408. procedure TCustomWebsocketClient.Disconnect(SendClose : boolean = true);
  409. begin
  410. if Not Active then
  411. Exit;
  412. if SendClose then
  413. Connection.Close('');
  414. if Assigned(MessagePump) then
  415. MessagePump.RemoveClient(Connection);
  416. If Assigned(OnDisconnect) then
  417. OnDisconnect(Self);
  418. FreeConnectionObjects;
  419. FActive:=False;
  420. end;
  421. procedure TCustomWebsocketClient.SetActive(const Value: Boolean);
  422. begin
  423. FLoadActive := Value;
  424. if (csDesigning in ComponentState) then
  425. exit;
  426. if Value then
  427. Connect
  428. else
  429. Disconnect;
  430. end;
  431. procedure TCustomWebsocketClient.SetAutoCheckMessages(const Value: Boolean);
  432. begin
  433. CheckInactive;
  434. FAutoCheckMessages := Value;
  435. end;
  436. procedure TCustomWebsocketClient.SetCheckTimeOut(const Value: Integer);
  437. begin
  438. CheckInactive;
  439. FCheckTimeOut := Value;
  440. end;
  441. procedure TCustomWebsocketClient.SetConnectTimeout(const Value: Integer);
  442. begin
  443. CheckInactive;
  444. FConnectTimeout := Value;
  445. end;
  446. procedure TCustomWebsocketClient.SetHostName(const Value: String);
  447. begin
  448. CheckInactive;
  449. FHostName := Value;
  450. end;
  451. procedure TCustomWebsocketClient.SetMessagePump(AValue: TWSMessagePump);
  452. begin
  453. if FMessagePump=AValue then Exit;
  454. If Assigned(FMessagePump) then
  455. FMessagePump.RemoveFreeNotification(Self);
  456. FMessagePump:=AValue;
  457. If Assigned(FMessagePump) then
  458. FMessagePump.FreeNotification(Self);
  459. end;
  460. procedure TCustomWebsocketClient.SetOptions(const Value: TWSOptions);
  461. begin
  462. CheckInactive;
  463. FOptions := Value;
  464. end;
  465. procedure TCustomWebsocketClient.SetPort(const Value: Integer);
  466. begin
  467. CheckInactive;
  468. FPort := Value;
  469. end;
  470. procedure TCustomWebsocketClient.SetResource(const Value: string);
  471. begin
  472. CheckInactive;
  473. FResource := Value;
  474. end;
  475. procedure TCustomWebsocketClient.SetUseSSL(const Value: Boolean);
  476. begin
  477. CheckInactive;
  478. FUseSSL := Value;
  479. end;
  480. { TTMSClientWebSocketConnection }
  481. { TWSMessagePump }
  482. procedure TWSMessagePump.AddClient(aConnection: TWSClientConnection);
  483. begin
  484. List.Add(aConnection);
  485. end;
  486. procedure TWSMessagePump.RemoveClient(aConnection: TWSClientConnection);
  487. begin
  488. FList.Remove(aConnection);
  489. end;
  490. procedure TWSMessagePump.SetInterval(AValue: Integer);
  491. begin
  492. if FInterval=AValue then Exit;
  493. FInterval:=AValue;
  494. end;
  495. Function TWSMessagePump.WaitForData : Boolean;
  496. Var
  497. dummy1,dummy2 : TSocketStreamArray;
  498. begin
  499. Dummy1:=Nil;
  500. Dummy2:=Nil;
  501. Result:=False;
  502. // FReadSet was populated by checkconnections
  503. SetLength(FExceptions,0);
  504. if Length(FReads)=0 then
  505. begin
  506. TThread.Sleep(FInterval);
  507. end
  508. else
  509. begin
  510. try
  511. // We take the first ont in the list.
  512. Result := FReadS[0].Select(FReads,dummy1,dummy2,FInterval);
  513. except
  514. Result := False;
  515. end;
  516. end;
  517. end;
  518. function TWSMessagePump.CheckConnections: Boolean;
  519. Var
  520. aList : TList;
  521. aClient: TWSClientConnection;
  522. aTrans : TWSClientTransport;
  523. I,aLen : Integer;
  524. begin
  525. Result:=False;
  526. aList := List.LockList;
  527. try
  528. aLen:=0;
  529. SetLength(FReads,aList.Count);
  530. for I := 0 to aList.Count - 1 do
  531. begin
  532. aClient := TWSClientConnection(aList.Items[I]);
  533. if assigned(aClient) then
  534. aTrans:=aClient.ClientTransport
  535. else
  536. aTrans:=Nil;
  537. if (aTrans<>nil) then
  538. begin
  539. // There is already data
  540. FReads[aLen]:=aTrans.Socket;
  541. Inc(aLen);
  542. end;
  543. end;
  544. finally
  545. List.UnlockList;
  546. end;
  547. if Not Result then
  548. Result:=WaitForData;
  549. end;
  550. constructor TWSMessagePump.Create(aOwner : TComponent);
  551. begin
  552. FList:=TThreadList.Create;
  553. FReads:=[];
  554. FExceptions:=[];
  555. Finterval:=50;
  556. end;
  557. destructor TWSMessagePump.Destroy;
  558. begin
  559. FreeAndNil(FList);
  560. inherited;
  561. end;
  562. procedure TWSMessagePump.ReadConnections;
  563. Var
  564. aList : TList;
  565. aClient: TWSClientConnection;
  566. I : Integer;
  567. begin
  568. try
  569. aList := List.LockList;
  570. try
  571. FReads:=[];
  572. for I := 0 to aList.Count - 1 do
  573. begin
  574. aClient:= TWSClientConnection(aList.Items[I]);
  575. if assigned(aClient.Transport) then
  576. aClient.CheckIncoming(1);
  577. end;
  578. finally
  579. List.UnlockList;
  580. end;
  581. except
  582. on E: Exception do
  583. if Assigned(OnError) then
  584. OnError(Self,E);
  585. end;
  586. end;
  587. { TWSThreadMessagePump }
  588. procedure TWSThreadMessagePump.Execute;
  589. begin
  590. FThread:=TMessageDriverThread.Create(Self,@ThreadTerminated);
  591. end;
  592. procedure TWSThreadMessagePump.ThreadTerminated(Sender: TObject);
  593. begin
  594. FThread:=Nil;
  595. end;
  596. procedure TWSThreadMessagePump.Terminate;
  597. begin
  598. FThread.Terminate;
  599. if Assigned(FThread) then
  600. FThread.WaitFor;
  601. end;
  602. { TWSThreadMessagePump.TMessageDriverThread }
  603. constructor TWSThreadMessagePump.TMessageDriverThread.Create(aPump: TWSThreadMessagePump; aTerminate : TNotifyEvent);
  604. begin
  605. FPump:=aPump;
  606. OnTerminate:=aTerminate;
  607. Inherited Create(False);
  608. end;
  609. procedure TWSThreadMessagePump.TMessageDriverThread.Execute;
  610. begin
  611. While Not Terminated do
  612. if FPump.CheckConnections then
  613. FPump.ReadConnections
  614. else
  615. TThread.Sleep(FPump.Interval);
  616. end;
  617. end.