webideintf.pp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  1. unit webideintf;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. fpMimeTypes, Classes, SysUtils, StrUtils, httpdefs, fphttpclient,custhttpapp, fpjson, jsonparser, httproute;
  6. Const
  7. SFilesURL = '/Project/';
  8. SIDEURL = '/IDE/';
  9. Type
  10. TClientObject = Class(TObject)
  11. Private
  12. FID: Int64;
  13. public
  14. Procedure FromJSON(aJSON : TJSONObject); virtual; abstract;
  15. Procedure ToJSON(aJSON : TJSONObject); virtual; abstract;
  16. Property ID : Int64 Read FID Write FID;
  17. end;
  18. { TIDEClient }
  19. TIDEClient = Class(TClientObject)
  20. private
  21. FURL: String;
  22. Public
  23. Procedure FromJSON(aJSON : TJSONObject); override;
  24. Procedure ToJSON(aJSON : TJSONObject); override;
  25. Property URL : String Read FURL Write FURL;
  26. end;
  27. { TIDEExchange }
  28. TIDEExchange = Class(TClientObject)
  29. private
  30. FClientID: Int64;
  31. FName: String;
  32. FPayLoad: TJSONData;
  33. Public
  34. Destructor Destroy; override;
  35. Procedure FromJSON(aJSON : TJSONObject); override;
  36. Procedure ToJSON(aJSON : TJSONObject); override;
  37. Property ClientID : Int64 Read FClientID Write FClientID;
  38. Property Name : String Read FName Write FName;
  39. Property PayLoad : TJSONData Read FPayLoad Write FPayLoad;
  40. end;
  41. TIDEAction = Class(TIDEExchange)
  42. end;
  43. { TClientObjectList }
  44. TClientObjectList = Class(TThreadList)
  45. Public
  46. Function FindID(aID : int64) : TClientObject;
  47. end;
  48. { TIDECommand }
  49. TIDECommand = Class(TIDEExchange)
  50. private
  51. FConfirmed: Boolean;
  52. FNeedsConfirmation: Boolean;
  53. FSent: Boolean;
  54. Public
  55. Property NeedsConfirmation : Boolean Read FNeedsConfirmation Write FNeedsConfirmation;
  56. Property Sent : Boolean Read FSent Write FSent;
  57. Property Confirmed : Boolean Read FConfirmed Write FConfirmed;
  58. end;
  59. { TIDEThread }
  60. TIDEThread = Class(TThread)
  61. Private
  62. FHandler : TFPHTTPServerHandler;
  63. FExceptionClass : String;
  64. FExceptionMessage : String;
  65. Public
  66. Constructor Create(aHandler : TFPHTTPServerHandler);
  67. Procedure Execute; override;
  68. end;
  69. TIDENotification = Procedure(Sender : TObject; aExchange : TIDEExchange) of object;
  70. TIDEClientNotification = Procedure(Sender : TObject; aClient : TIDEClient) of object;
  71. TIDERequestNotification = Procedure(Sender : TObject; aURL : String) of object;
  72. { TIDEServer }
  73. TIDEServer = Class(TComponent)
  74. private
  75. FOnRequest: TIDERequestNotification;
  76. FQuitting : Boolean;
  77. FClients,
  78. FCommands,
  79. FActions : TClientObjectList;
  80. FIDCounter: Int64;
  81. FOnAction: TIDENotification;
  82. FOnClient: TIDEClientNotification;
  83. FOnClientRemoved: TIDEClientNotification;
  84. FOnConfirmCommand: TIDENotification;
  85. FProjectDir: String;
  86. FWebHandler : TFPHTTPServerHandler;
  87. FThread : TIDEThread;
  88. FLastAction : TIDEAction;
  89. FLastCommand : TIDECommand;
  90. FLastClient : TIDEClient;
  91. function CheckClient(aRequest: TRequest): INt64;
  92. procedure DeActivatedThread(Sender: TObject);
  93. function Do404(is404: boolean; aResponse: TResponse): Boolean;
  94. procedure DoEvent(aProc: TThreadMethod);
  95. procedure DoQuit(ARequest: TRequest; AResponse: TResponse);
  96. procedure DoRouteRequest(Sender: TObject; ARequest: TRequest; AResponse: TResponse);
  97. function GetAction(Index : Integer): TIDEAction;
  98. function GetActionCount: Integer;
  99. function GetPort: Integer;
  100. function GetActive: Boolean;
  101. procedure SetActive(AValue: Boolean);
  102. procedure SetPort(AValue: Integer);
  103. procedure SetProjectDir(AValue: String);
  104. Protected
  105. procedure RegisterRoutes; virtual;
  106. // HTTP request extraction
  107. procedure GetClientObjectFromRequest(ARequest: TRequest; AObject: TClientObject);
  108. function GetActionFromRequest(ARequest: TRequest): TIDEAction;
  109. function GetCommandFromRequest(ARequest: TRequest): TIDECommand;
  110. function GetClientFromRequest(ARequest: TRequest): TIDEClient;
  111. function GetJSONFromRequest(ARequest: TRequest): TJSONObject;
  112. // Sending responses
  113. procedure SendClientObjectResponse(AObject: TClientObject; AResponse: TResponse);
  114. Procedure SendJSONResponse(aJSON : TJSONObject; aResponse : TResponse);
  115. // HTTP route handlers
  116. procedure DoDeleteAction(ARequest: TRequest; AResponse: TResponse); virtual;
  117. procedure DoDeleteClient(ARequest: TRequest; AResponse: TResponse); virtual;
  118. procedure DoGetCommand(ARequest: TRequest; AResponse: TResponse);virtual;
  119. procedure DoGetFile(ARequest: TRequest; AResponse: TResponse);virtual;
  120. procedure DoPostAction(ARequest: TRequest; AResponse: TResponse);virtual;
  121. procedure DoPostClient(ARequest: TRequest; AResponse: TResponse);virtual;
  122. procedure DoPutCommand(ARequest: TRequest; AResponse: TResponse);virtual;
  123. // Event handler synchronisation. Rework this to objects
  124. Procedure DoOnAction;
  125. Procedure DoOnConfirmCommand;
  126. Procedure DoOnClientAdded;
  127. Procedure DoOnClientRemoved;
  128. Public
  129. Constructor Create(aOwner : TComponent); override;
  130. Destructor Destroy; override;
  131. Function GetNextCounter : Int64;
  132. // Public API to communicate with browser
  133. Function SendCommand(aCommand : TIDECommand) : Int64;
  134. Procedure GetClientActions(aClientID : Int64; aList : TFPList);
  135. Function DeleteAction(aID: Int64; Const aClientID : Int64 = -1): Boolean;
  136. // Public properties
  137. Property ProjectDir : String Read FProjectDir Write SetProjectDir;
  138. Property Port : Integer Read GetPort Write SetPort;
  139. Property Active : Boolean read GetActive write SetActive;
  140. Property ActionCount : Integer Read GetActionCount;
  141. Property Action[Index : Integer] : TIDEAction Read GetAction;
  142. // Events
  143. Property OnRequest : TIDERequestNotification Read FOnRequest Write FOnRequest;
  144. Property OnConfirmCommand : TIDENotification Read FOnConfirmCommand Write FOnConfirmCommand;
  145. Property OnAction : TIDENotification Read FOnAction Write FOnAction;
  146. Property OnClientAdded : TIDEClientNotification Read FOnClient Write FOnClient;
  147. Property OnClientRemoved : TIDEClientNotification Read FOnClientRemoved Write FOnClientRemoved;
  148. end;
  149. implementation
  150. { TClientObjectList }
  151. function TClientObjectList.FindID(aID: int64): TClientObject;
  152. Var
  153. L : TList;
  154. I : integer;
  155. begin
  156. Result:=Nil;
  157. L:=LockList;
  158. try
  159. I:=L.Count-1;
  160. While (Result=Nil) and (I>=0) do
  161. begin
  162. Result:=TClientObject(L[i]);
  163. if Result.ID<>aID then
  164. Result:=nil;
  165. Dec(I);
  166. end;
  167. finally
  168. UnlockList;
  169. end;
  170. end;
  171. { TIDEClient }
  172. procedure TIDEClient.FromJSON(aJSON: TJSONObject);
  173. begin
  174. FID:=aJSON.Get('id',Int64(-1));
  175. FURL:=aJSON.Get('url','');
  176. end;
  177. procedure TIDEClient.ToJSON(aJSON: TJSONObject);
  178. begin
  179. aJSON.Add('id',ID);
  180. aJSON.Add('url',url);
  181. end;
  182. { TIDEExchange }
  183. destructor TIDEExchange.Destroy;
  184. begin
  185. FreeAndNil(FPayload);
  186. Inherited;
  187. end;
  188. procedure TIDEExchange.FromJSON(aJSON: TJSONObject);
  189. Var
  190. P : TJSONObject;
  191. begin
  192. ID:=aJSON.Get('id',Int64(0));
  193. Name:=aJSON.Get('name','');
  194. P:=aJSON.Get('payload',TJSONObject(Nil));
  195. if Assigned(P) then
  196. Payload:=aJSON.Extract('payload');
  197. end;
  198. procedure TIDEExchange.ToJSON(aJSON: TJSONObject);
  199. begin
  200. aJSON.Add('id',ID);
  201. aJSON.Add('name',name);
  202. if Assigned(Payload) then
  203. aJSON.Add('payload',Payload.Clone);
  204. end;
  205. { TIDEThread }
  206. constructor TIDEThread.Create(aHandler: TFPHTTPServerHandler);
  207. begin
  208. FHandler:=AHandler;
  209. FreeOnTerminate:=True;
  210. Inherited Create(False);
  211. end;
  212. procedure TIDEThread.Execute;
  213. begin
  214. try
  215. FHandler.Run;
  216. FHandler:=nil;
  217. except
  218. On E : Exception do
  219. begin
  220. FExceptionClass:=E.ClassName;
  221. FExceptionMessage:=E.Message;
  222. end;
  223. end;
  224. end;
  225. { TIDEServer }
  226. function TIDEServer.GetAction(Index : Integer): TIDEAction;
  227. Var
  228. L : TList;
  229. begin
  230. L:=FActions.LockList;
  231. try
  232. Result:=TIDEAction(L.Items[Index]);
  233. finally
  234. FActions.UnlockList;
  235. end;
  236. end;
  237. procedure TIDEServer.DeActivatedThread(Sender: TObject);
  238. begin
  239. FThread:=Nil;
  240. end;
  241. function TIDEServer.GetActionCount: Integer;
  242. Var
  243. L : TList;
  244. begin
  245. L:=FActions.LockList;
  246. try
  247. Result:=L.Count;
  248. finally
  249. FActions.UnlockList;
  250. end;
  251. end;
  252. function TIDEServer.GetActive: Boolean;
  253. begin
  254. Result:=Assigned(FThread);
  255. end;
  256. function TIDEServer.GetPort: Integer;
  257. begin
  258. Result:=FWebHandler.Port;
  259. end;
  260. procedure TIDEServer.SetActive(AValue: Boolean);
  261. begin
  262. if Active=AValue then Exit;
  263. if AValue then
  264. begin
  265. FThread:=TIDEThread.Create(FWebHandler);
  266. FThread.OnTerminate:=@DeActivatedThread;
  267. end
  268. else
  269. begin
  270. FWebHandler.Terminate; // will cause thread to stop.
  271. try
  272. // Send a Quit request just in case. Normally this should fail.
  273. FQuitting:=True;
  274. TFPHTTPClient.SimpleGet(Format('http://localhost:%d/Quit',[Port]));
  275. except
  276. FQuitting:=False;
  277. end;
  278. end;
  279. end;
  280. procedure TIDEServer.SetPort(AValue: Integer);
  281. begin
  282. FWebHandler.Port:=aValue;
  283. end;
  284. procedure TIDEServer.SetProjectDir(AValue: String);
  285. begin
  286. if FProjectDir=AValue then Exit;
  287. FProjectDir:=IncludeTrailingPathDelimiter(AValue);
  288. end;
  289. procedure TIDEServer.DoOnAction;
  290. begin
  291. If Assigned(FOnAction) then
  292. FonAction(Self,FLastAction);
  293. FLastAction:=Nil;
  294. end;
  295. procedure TIDEServer.DoOnConfirmCommand;
  296. begin
  297. If Assigned(FOnAction) then
  298. FonAction(Self,FLastCommand);
  299. FLastCommand:=Nil;
  300. end;
  301. procedure TIDEServer.DoOnClientAdded;
  302. begin
  303. if Assigned(FOnClient) then
  304. FOnClient(Self,FLastClient);
  305. FLastClient:=Nil;
  306. end;
  307. procedure TIDEServer.DoOnClientRemoved;
  308. begin
  309. if Assigned(FOnClientRemoved) then
  310. FOnClientRemoved(Self,FLastClient);
  311. FLastClient:=Nil;
  312. end;
  313. procedure TIDEServer.DoGetCommand(ARequest: TRequest; AResponse: TResponse);
  314. Var
  315. L : TList;
  316. I : integer;
  317. J,C : TJSONObject;
  318. A :TJSONArray;
  319. Cmd : TIDECommand;
  320. L2 : TFPList;
  321. aClient : Int64;
  322. begin
  323. aClient:=CheckClient(aRequest);
  324. J:=nil;
  325. A:=nil;
  326. L:=FCommands.LockList;
  327. try
  328. L2:=TFPList.Create;
  329. J:=TJSONObject.Create;
  330. A:=TJSONArray.Create;
  331. J.Add('commands',A);
  332. For I:=0 to L.Count-1 do
  333. begin
  334. CMD:=TIDECommand(L[i]);
  335. if Not Cmd.Sent and (Cmd.ClientID=aClient) then
  336. begin
  337. C:=TJSONObject.Create;
  338. Cmd.ToJSON(C);
  339. A.Add(C);
  340. L2.Add(CMD);
  341. end;
  342. end;
  343. SendJSONResponse(J,aResponse);
  344. // Remove sent from list
  345. for I:=0 to L2.Count-1 do
  346. begin
  347. Cmd:=TIDECommand(L2[i]);
  348. if Cmd.NeedsConfirmation then
  349. Cmd.Sent:=True
  350. else
  351. begin
  352. Cmd.Free;
  353. L.Remove(Cmd);
  354. end;
  355. end;
  356. finally
  357. J.Free;
  358. FCommands.UnLockList;
  359. l2.Free;
  360. end;
  361. end;
  362. procedure TIDEServer.DoPutCommand(ARequest: TRequest; AResponse: TResponse);
  363. Var
  364. cmd,oCmd : TIDECommand;
  365. aID,aClient : Int64;
  366. begin
  367. aClient:=CheckClient(aRequest);
  368. aID:=StrToIntDef(aRequest.RouteParams['ID'],-1);
  369. cmd:=TIDECommand.Create;
  370. try
  371. GetClientObjectFromRequest(aRequest,Cmd);
  372. cmd.ClientID:=aClient;
  373. oCmd:=TIDECommand(FCommands.FindID(aID));
  374. if Do404((oCmd=Nil) or (oCmd.ClientID<>aClient),aResponse) then
  375. exit;
  376. // Later on we can add more modifications
  377. oCmd.Confirmed:=True;
  378. aResponse.Code:=204;
  379. aResponse.CodeText:='OK';
  380. aResponse.SendResponse;
  381. FLastCommand:=oCmd;
  382. DoEvent(@DoOnConfirmCommand);
  383. FCommands.Remove(oCmd);
  384. Finally
  385. cmd.Free;
  386. end;
  387. end;
  388. procedure TIDEServer.DoQuit(ARequest: TRequest; AResponse: TResponse);
  389. begin
  390. if FQuitting then
  391. aResponse.Code:=200
  392. else
  393. aResponse.Code:=401;
  394. aResponse.SendResponse;
  395. end;
  396. procedure TIDEServer.DoRouteRequest(Sender: TObject; ARequest: TRequest; AResponse: TResponse);
  397. begin
  398. If Assigned(FonRequest) then
  399. FOnRequest(Self,aRequest.URI);
  400. end;
  401. function TIDEServer.GetJSONFromRequest(ARequest: TRequest): TJSONObject;
  402. var
  403. D : TJSONData;
  404. begin
  405. if ARequest.ContentType<>'application/json' then
  406. Raise Exception.Create('Not valid JSON payload: content type must be application/json');
  407. D:=GetJSON(ARequest.Content);
  408. if Not (D is TJSONObject) then
  409. begin
  410. FreeAndNil(D);
  411. Raise EJSON.Create('Payload is valid JSON but not a JSON object');
  412. end;
  413. Result:=D as TJSONObject;
  414. end;
  415. procedure TIDEServer.SendJSONResponse(aJSON: TJSONObject; aResponse: TResponse);
  416. Var
  417. JS : TJSONStringType;
  418. begin
  419. JS:=aJSON.AsJSON;
  420. aResponse.FreeContentStream:=True;
  421. aResponse.ContentStream:=TMemoryStream.Create;
  422. aResponse.ContentStream.WriteBuffer(JS[1],Length(JS));
  423. aResponse.ContentLength:=Length(JS);
  424. aResponse.ContentType:='application/json';
  425. aResponse.SendResponse;
  426. end;
  427. procedure TIDEServer.GetClientObjectFromRequest(ARequest: TRequest; AObject: TClientObject);
  428. Var
  429. J : TJSONObject;
  430. begin
  431. J:=GetJSONFromRequest(aRequest);
  432. try
  433. AObject.FromJSON(J);
  434. finally
  435. J.Free;
  436. end;
  437. end;
  438. procedure TIDEServer.SendClientObjectResponse(AObject: TClientObject; AResponse: TResponse);
  439. Var
  440. J : TJSONObject;
  441. begin
  442. J:=TJSONObject.Create;
  443. try
  444. aObject.ToJSON(J);
  445. SendJSONResponse(J,aResponse);
  446. finally
  447. J.Free;
  448. end;
  449. end;
  450. function TIDEServer.GetActionFromRequest(ARequest: TRequest): TIDEAction;
  451. begin
  452. Result:=TIDEAction.Create;
  453. try
  454. GetClientObjectFromRequest(aRequest,Result);
  455. except
  456. Result.Free;
  457. raise;
  458. end;
  459. end;
  460. function TIDEServer.GetCommandFromRequest(ARequest: TRequest): TIDECommand;
  461. begin
  462. Result:=TIDECommand.Create;
  463. try
  464. GetClientObjectFromRequest(aRequest,Result);
  465. except
  466. Result.Free;
  467. Raise;
  468. end;
  469. end;
  470. function TIDEServer.GetClientFromRequest(ARequest: TRequest): TIDEClient;
  471. begin
  472. Result:=TIDEClient.Create;
  473. try
  474. GetClientObjectFromRequest(aRequest,Result);
  475. except
  476. Result.Free;
  477. Raise;
  478. end;
  479. end;
  480. procedure TIDEServer.DoPostAction(ARequest: TRequest; AResponse: TResponse);
  481. var
  482. A : TIDEAction;
  483. aId,aClient : Int64;
  484. begin
  485. aClient:=CheckClient(aRequest);
  486. aID:=StrToInt64Def(aRequest.RouteParams['ID'],-1);
  487. Try
  488. A:=GetACtionFromRequest(aRequest);
  489. A.ClientID:=aClient;
  490. if A.ID=0 then
  491. a.ID:=aID;
  492. FActions.Add(A);
  493. FLastAction:=A;
  494. DoEvent(@DoOnAction);
  495. AResponse.Code:=201;
  496. AResponse.Codetext:='Created';
  497. except
  498. On E: Exception do
  499. begin
  500. AResponse.Code:=400;
  501. AResponse.Codetext:='Invalid Param';
  502. AResponse.Content:='Invalid data ('+E.ClassName+'): '+E.Message;
  503. end;
  504. end;
  505. aResponse.SendResponse;
  506. end;
  507. function TIDEServer.CheckClient(aRequest: TRequest): INt64;
  508. Var
  509. S : String;
  510. begin
  511. S:=ARequest.RouteParams['Client'];
  512. if (S='') then
  513. Raise EJSON.Create('Missing client ID in request');
  514. if Not TryStrToInt64(S,Result) then
  515. Raise EJSON.CreateFmt('Invalid client ID: %s',[S]);
  516. end;
  517. procedure TIDEServer.DoDeleteAction(ARequest: TRequest; AResponse: TResponse);
  518. var
  519. SID : String;
  520. ID,aClient : Int64;
  521. begin
  522. Try
  523. aClient:=CheckClient(ARequest);
  524. SID:=ARequest.RouteParams['ID'];
  525. ID:=StrtoInt64Def(SID,-1);
  526. if Do404((ID=-1) or not (DeleteAction(ID,aClient)),aResponse) then
  527. exit;
  528. AResponse.Code:=204;
  529. AResponse.Codetext:='No content';
  530. aResponse.SendResponse;
  531. except
  532. On E: Exception do
  533. begin
  534. AResponse.Code:=400;
  535. AResponse.Codetext:='Invalid Param';
  536. AResponse.Content:='Invalid data ('+E.ClassName+'): '+E.Message;
  537. end;
  538. end;
  539. end;
  540. procedure TIDEServer.DoGetFile(ARequest: TRequest; AResponse: TResponse);
  541. Var
  542. FN : String;
  543. begin
  544. FN:=ARequest.URL;
  545. if AnsiStartsText(SFilesURL,FN) then
  546. Delete(FN,1,Length(SFilesURL));
  547. FN:=ExpandFileName(FProjectDir+FN);
  548. if Pos('..',ExtractRelativepath(FProjectDir,FN))<>0 then
  549. begin
  550. aResponse.Code:=401;
  551. aResponse.CodeText:='Forbidden';
  552. aResponse.Content:='<H1>Forbidden</H1>';
  553. end
  554. else if Do404(Not FileExists(FN),aResponse) then
  555. exit;
  556. aResponse.FreeContentStream:=True;
  557. aResponse.ContentStream:=TFileStream.Create(FN,fmOpenRead or fmShareDenyWrite);
  558. aResponse.ContentLength:=aResponse.ContentStream.Size;
  559. aResponse.ContentType:=MimeTypes.GetMimeType(ExtractFileExt(FN));
  560. if aResponse.ContentType='' then
  561. aResponse.ContentType:='text/html';
  562. aResponse.SendResponse;
  563. end;
  564. constructor TIDEServer.Create(aOwner: TComponent);
  565. begin
  566. Inherited;
  567. FProjectDir:=ExtractFilePath(Paramstr(0));
  568. FActions:=TClientObjectList.Create;
  569. FCommands:=TClientObjectList.Create;
  570. FClients:=TClientObjectList.Create;
  571. FWebHandler:=TFPHTTPServerHandler.Create(Self);
  572. FWebHandler.Port:=8080;
  573. RegisterRoutes;
  574. end;
  575. procedure TIDEServer.DoEvent(aProc : TThreadMethod);
  576. begin
  577. if Assigned(FThread) then
  578. FThread.Synchronize(aProc)
  579. else
  580. aProc;
  581. end;
  582. procedure TIDEServer.DoPostClient(ARequest: TRequest; AResponse: TResponse);
  583. Var
  584. aClient : TIDEClient;
  585. begin
  586. aClient:=GetClientFromRequest(aRequest);
  587. aClient.FID:=GetNextCounter;
  588. FClients.Add(aClient);
  589. SendClientObjectResponse(aClient,aResponse);
  590. FLastClient:=aClient;
  591. DoEvent(@DoOnClientAdded);
  592. end;
  593. function TIDEServer.Do404(is404: boolean; aResponse: TResponse): Boolean;
  594. begin
  595. Result:=is404;
  596. if Result then
  597. begin
  598. aResponse.Code:=404;
  599. aResponse.Codetext:='Not found';
  600. aResponse.SendResponse;
  601. end;
  602. end;
  603. procedure TIDEServer.DoDeleteClient(ARequest: TRequest; AResponse: TResponse);
  604. Var
  605. aClientID : Int64;
  606. aClient : TIDEClient;
  607. begin
  608. aClientID:=CheckClient(aRequest);
  609. aClient:=TIDEClient(FClients.FindID(aClientID));
  610. if Do404(not Assigned(aClient),aResponse) then
  611. exit;
  612. FLastClient:=aClient;
  613. DoEvent(@DoOnClientRemoved);
  614. FClients.Remove(aClient);
  615. end;
  616. procedure TIDEServer.RegisterRoutes;
  617. begin
  618. // get command
  619. HTTPRouter.RegisterRoute(SIDEURL+'Quit',rmGet,@DoQuit);
  620. HTTPRouter.RegisterRoute(SIDEURL+'Client/',rmPost,@DoPostClient);
  621. HTTPRouter.RegisterRoute(SIDEURL+'Client/:Client',rmDelete,@DoDeleteClient);
  622. HTTPRouter.RegisterRoute(SIDEURL+'Command/:Client/',rmGet,@DoGetCommand);
  623. // PUT command for confirm.
  624. HTTPRouter.RegisterRoute(SIDEURL+'Command/:Client/:ID',rmPut,@DoPutCommand);
  625. // POST action
  626. HTTPRouter.RegisterRoute(SIDEURL+'Action/:Client/:ID',rmPost,@DoPostAction);
  627. HTTPRouter.RegisterRoute(SIDEURL+'Action/:Client/:ID',rmDelete,@DoDeleteAction);
  628. // GET file
  629. HTTPRouter.RegisterRoute(SFilesURL+'*',rmGet,@DoGetFile,true);
  630. HTTPRouter.BeforeRequest:=@DoRouteRequest;
  631. end;
  632. destructor TIDEServer.Destroy;
  633. begin
  634. Active:=False;
  635. While Active do
  636. Sleep(20);
  637. FreeAndNil(FActions);
  638. FreeAndNil(FCommands);
  639. FreeAndNil(FClients);
  640. inherited Destroy;
  641. end;
  642. function TIDEServer.GetNextCounter: Int64;
  643. begin
  644. Inc(FIDCounter);
  645. Result:=FIDCounter;
  646. end;
  647. function TIDEServer.SendCommand(aCommand: TIDECommand): Int64;
  648. begin
  649. Result:=GetNextCounter;
  650. aCommand.ID:=Result;
  651. FCommands.Add(aCommand);
  652. end;
  653. function TIDEServer.DeleteAction(aID: Int64; const aClientID: Int64): Boolean;
  654. Var
  655. P : TIDEAction;
  656. L : TList;
  657. I : Integer;
  658. begin
  659. P:=nil;
  660. L:=FActions.LockList;
  661. try
  662. I:=L.Count-1;
  663. While (I>=0) and (P=Nil) do
  664. begin
  665. P:=TIDEAction(L[i]);
  666. if P.ID<>AID then P:=Nil;
  667. Dec(i)
  668. end;
  669. finally
  670. L.Free;
  671. end;
  672. Result:=(P<>Nil) and ((aClientID=-1) or (P.ClientID=aClientID));
  673. if Result then
  674. FActions.Remove(P);
  675. end;
  676. procedure TIDEServer.GetClientActions(aClientID: Int64; aList: TFPList);
  677. Var
  678. P : TIDEAction;
  679. L : TList;
  680. I : Integer;
  681. begin
  682. P:=nil;
  683. L:=FActions.LockList;
  684. try
  685. I:=L.Count-1;
  686. While (I>=0) and (P=Nil) do
  687. begin
  688. P:=TIDEAction(L[i]);
  689. if P.ClientID=aClientID then
  690. begin
  691. aList.Add(P);
  692. L.Delete(I);
  693. end;
  694. Dec(i);
  695. end;
  696. finally
  697. L.Free;
  698. end;
  699. end;
  700. end.