HTTPServerSSE_frMain.pas 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. (* _ _
  2. * | |__ _ __ ___ ___ | | __
  3. * | '_ \| '__/ _ \ / _ \| |/ /
  4. * | |_) | | | (_) | (_) | <
  5. * |_.__/|_| \___/ \___/|_|\_\
  6. *
  7. * Microframework which helps to develop web Pascal applications.
  8. *
  9. * Copyright (c) 2012-2021 Silvio Clecio <[email protected]>
  10. *
  11. * Brook framework is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation; either
  14. * version 2.1 of the License, or (at your option) any later version.
  15. *
  16. * Brook framework is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with Brook framework; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  24. *)
  25. unit HTTPServerSSE_frMain;
  26. interface
  27. uses
  28. System.SysUtils,
  29. System.UITypes,
  30. System.Classes,
  31. System.Actions,
  32. FMX.Types,
  33. FMX.ActnList,
  34. FMX.Graphics,
  35. FMX.Controls,
  36. FMX.StdCtrls,
  37. FMX.Edit,
  38. FMX.EditBox,
  39. FMX.NumberBox,
  40. FMX.DialogService,
  41. FMX.Forms,
  42. FMX.Controls.Presentation,
  43. BrookHandledClasses,
  44. BrookHTTPRequest,
  45. BrookHTTPResponse,
  46. BrookHTTPServer,
  47. Utility;
  48. type
  49. { TSSEStream }
  50. TSSEStream = class(TStream)
  51. private
  52. FCount: Cardinal;
  53. public
  54. function Read(var ABuffer; ACount: LongInt): LongInt; override;
  55. end;
  56. { TfrMain }
  57. TfrMain = class(TForm)
  58. lbPort: TLabel;
  59. edPort: TNumberBox;
  60. btStart: TButton;
  61. btStop: TButton;
  62. lbLink: TLabel;
  63. alMain: TActionList;
  64. acStart: TAction;
  65. acStop: TAction;
  66. BrookHTTPServer1: TBrookHTTPServer;
  67. pnTop: TPanel;
  68. procedure acStartExecute(Sender: TObject);
  69. procedure acStopExecute(Sender: TObject);
  70. procedure lbLinkMouseEnter(Sender: TObject);
  71. procedure lbLinkMouseLeave(Sender: TObject);
  72. procedure lbLinkClick(Sender: TObject);
  73. procedure BrookHTTPServer1Request(ASender: TObject;
  74. ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse);
  75. procedure BrookHTTPServer1RequestError(ASender: TObject;
  76. ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse;
  77. AException: Exception);
  78. procedure BrookHTTPServer1Error(ASender: TObject; AException: Exception);
  79. procedure BrookHTTPServer1Start(Sender: TObject);
  80. procedure BrookHTTPServer1Stop(Sender: TObject);
  81. procedure edPortChange(Sender: TObject);
  82. procedure edPortChangeTracking(Sender: TObject);
  83. public
  84. procedure UpdateControls; {$IFNDEF DEBUG}inline;{$ENDIF}
  85. end;
  86. const
  87. PAGE_COUNTING = Concat(
  88. '<!DOCTYPE html>', sLineBreak,
  89. '<html>', sLineBreak,
  90. '<head>', sLineBreak,
  91. '<title>SSE example</title>', sLineBreak,
  92. '</head><body><h2 id="counter">Please wait ...</h2>', sLineBreak,
  93. '<script>', sLineBreak,
  94. 'const es = new EventSource("/");', sLineBreak,
  95. 'es.onmessage = function (ev) {', sLineBreak,
  96. ' document.getElementById("counter").innerText = "Counting: " + ev.data;', sLineBreak,
  97. '};', sLineBreak,
  98. '</script>', sLineBreak,
  99. '</body>', sLineBreak,
  100. '</html>'
  101. );
  102. PAGE_ERROR = Concat(
  103. '<!DOCTYPE html>', sLineBreak,
  104. '<html>', sLineBreak,
  105. '<head>', sLineBreak,
  106. '<title>Error</title>', sLineBreak,
  107. '</head>', sLineBreak,
  108. '<body>', sLineBreak,
  109. '<font color="red">%s</font>', sLineBreak,
  110. '</body>', sLineBreak,
  111. '</html>'
  112. );
  113. HTML_HEADER = 'text/html; charset=utf-8';
  114. SSE_HEADER = 'text/event-stream';
  115. IGNORED_ERROR = 'Connection was closed while sending response body.';
  116. var
  117. frMain: TfrMain;
  118. implementation
  119. {$R *.fmx}
  120. { TSSEStream }
  121. function TSSEStream.Read(var ABuffer; ACount: LongInt): LongInt;
  122. var
  123. VMsg: string;
  124. VBuffer: TBytes;
  125. begin
  126. if FCount = 0 then
  127. VMsg := Concat('retry: 1000', sLineBreak)
  128. else
  129. begin
  130. VMsg := Concat('data: ', FCount.ToString, sLineBreak, sLineBreak);
  131. Sleep(1000);
  132. end;
  133. Inc(FCount);
  134. VBuffer := TEncoding.ANSI.GetBytes(VMsg);
  135. Result := TEncoding.ANSI.GetCharCount(VBuffer);
  136. Move(VBuffer[0], ABuffer, Result);
  137. end;
  138. { TfrMain }
  139. procedure TfrMain.UpdateControls;
  140. begin
  141. if Application.Terminated then
  142. Exit;
  143. if BrookHTTPServer1.Active then
  144. edPort.Value := BrookHTTPServer1.Port
  145. else
  146. BrookHTTPServer1.Port := edPort.Text.ToInteger;
  147. lbLink.Text := Concat('http://localhost:', edPort.Value.ToString);
  148. acStart.Enabled := not BrookHTTPServer1.Active;
  149. acStop.Enabled := not acStart.Enabled;
  150. edPort.Enabled := acStart.Enabled;
  151. lbLink.Enabled := not acStart.Enabled;
  152. end;
  153. procedure TfrMain.acStartExecute(Sender: TObject);
  154. begin
  155. BrookHTTPServer1.Open;
  156. end;
  157. procedure TfrMain.acStopExecute(Sender: TObject);
  158. begin
  159. BrookHTTPServer1.Close;
  160. end;
  161. procedure TfrMain.lbLinkMouseEnter(Sender: TObject);
  162. begin
  163. lbLink.Font.Style := lbLink.Font.Style + [TFontStyle.fsUnderline];
  164. end;
  165. procedure TfrMain.lbLinkMouseLeave(Sender: TObject);
  166. begin
  167. lbLink.Font.Style := lbLink.Font.Style - [TFontStyle.fsUnderline];
  168. end;
  169. procedure TfrMain.lbLinkClick(Sender: TObject);
  170. begin
  171. Utility.OpenURL(lbLink.Text);
  172. end;
  173. procedure TfrMain.BrookHTTPServer1Request(ASender: TObject;
  174. ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse);
  175. begin
  176. if ARequest.Headers['Accept'] = SSE_HEADER then
  177. begin
  178. AResponse.Headers.Add('Access-Control-Allow-Origin', '*');
  179. AResponse.Headers.Add('Content-Type', SSE_HEADER);
  180. AResponse.SendStream(TSSEStream.Create, 200);
  181. end
  182. else
  183. AResponse.Send(PAGE_COUNTING, HTML_HEADER, 200);
  184. end;
  185. procedure TfrMain.BrookHTTPServer1RequestError(ASender: TObject;
  186. ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse;
  187. AException: Exception);
  188. begin
  189. AResponse.SendFmt(PAGE_ERROR, [AException.Message], HTML_HEADER, 500);
  190. end;
  191. procedure TfrMain.BrookHTTPServer1Start(Sender: TObject);
  192. begin
  193. UpdateControls;
  194. end;
  195. procedure TfrMain.BrookHTTPServer1Stop(Sender: TObject);
  196. begin
  197. UpdateControls;
  198. end;
  199. procedure TfrMain.edPortChange(Sender: TObject);
  200. begin
  201. UpdateControls;
  202. end;
  203. procedure TfrMain.edPortChangeTracking(Sender: TObject);
  204. begin
  205. UpdateControls;
  206. end;
  207. procedure TfrMain.BrookHTTPServer1Error(ASender: TObject;
  208. AException: Exception);
  209. begin
  210. if AException.Message.TrimRight <> IGNORED_ERROR then
  211. inherited;
  212. end;
  213. end.