HTTPServerSSE_frMain.pas 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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. {$MODE DELPHI}
  27. interface
  28. uses
  29. SysUtils,
  30. Classes,
  31. StdCtrls,
  32. ActnList,
  33. Graphics,
  34. Spin,
  35. Dialogs,
  36. Forms,
  37. LCLIntf,
  38. BrookHTTPRequest,
  39. BrookHTTPResponse,
  40. BrookHTTPServer;
  41. type
  42. { TSSEStream }
  43. TSSEStream = class(TStream)
  44. private
  45. FCount: Cardinal;
  46. public
  47. function Read(var ABuffer; ACount: LongInt): LongInt; override;
  48. end;
  49. { TfrMain }
  50. TfrMain = class(TForm)
  51. acStart: TAction;
  52. acStop: TAction;
  53. alMain: TActionList;
  54. BrookHTTPServer1: TBrookHTTPServer;
  55. btStart: TButton;
  56. btStop: TButton;
  57. edPort: TSpinEdit;
  58. lbLink: TLabel;
  59. lbPort: TLabel;
  60. procedure acStartExecute(Sender: TObject);
  61. procedure acStopExecute(Sender: TObject);
  62. procedure BrookHTTPServer1Request(ASender: TObject;
  63. ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse);
  64. procedure BrookHTTPServer1RequestError(ASender: TObject;
  65. ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse;
  66. AException: Exception);
  67. procedure BrookHTTPServer1Start(Sender: TObject);
  68. procedure BrookHTTPServer1Stop(Sender: TObject);
  69. procedure edPortChange(Sender: TObject);
  70. procedure lbLinkClick(Sender: TObject);
  71. procedure lbLinkMouseEnter(Sender: TObject);
  72. procedure lbLinkMouseLeave(Sender: TObject);
  73. public
  74. procedure UpdateControls; {$IFNDEF DEBUG}inline;{$ENDIF}
  75. end;
  76. const
  77. PAGE_COUNTING = Concat(
  78. '<!DOCTYPE html>', sLineBreak,
  79. '<html>', sLineBreak,
  80. '<head>', sLineBreak,
  81. '<title>SSE example</title>', sLineBreak,
  82. '</head><body><h2 id="counter">Please wait ...</h2>', sLineBreak,
  83. '<script>', sLineBreak,
  84. 'const es = new EventSource("/");', sLineBreak,
  85. 'es.onmessage = function (ev) {', sLineBreak,
  86. ' document.getElementById("counter").innerText = "Counting: " + ev.data;', sLineBreak,
  87. '};', sLineBreak,
  88. '</script>', sLineBreak,
  89. '</body>', sLineBreak,
  90. '</html>'
  91. );
  92. PAGE_ERROR = Concat(
  93. '<!DOCTYPE html>', sLineBreak,
  94. '<html>', sLineBreak,
  95. '<head>', sLineBreak,
  96. '<title>Error</title>', sLineBreak,
  97. '</head>', sLineBreak,
  98. '<body>', sLineBreak,
  99. '<font color="red">%s</font>', sLineBreak,
  100. '</body>', sLineBreak,
  101. '</html>'
  102. );
  103. HTML_HEADER = 'text/html; charset=utf-8';
  104. SSE_HEADER = 'text/event-stream';
  105. IGNORED_ERROR = 'Connection was closed while sending response body.';
  106. var
  107. frMain: TfrMain;
  108. implementation
  109. {$R *.lfm}
  110. { TSSEStream }
  111. function TSSEStream.Read(var ABuffer; ACount: LongInt): LongInt;
  112. var
  113. VMsg: string;
  114. begin
  115. if FCount = 0 then
  116. VMsg := Concat('retry: 1000', sLineBreak)
  117. else
  118. begin
  119. VMsg := Concat('data: ', FCount.ToString, sLineBreak, sLineBreak);
  120. Sleep(1000);
  121. end;
  122. Inc(FCount);
  123. Result := Length(VMsg);
  124. Move(VMsg[1], ABuffer, Result);
  125. end;
  126. { TfrMain }
  127. procedure TfrMain.UpdateControls;
  128. begin
  129. if BrookHTTPServer1.Active then
  130. edPort.Value := BrookHTTPServer1.Port
  131. else
  132. BrookHTTPServer1.Port := edPort.Value;
  133. lbLink.Caption := Concat('http://localhost:', edPort.Value.ToString);
  134. acStart.Enabled := not BrookHTTPServer1.Active;
  135. acStop.Enabled := not acStart.Enabled;
  136. edPort.Enabled := acStart.Enabled;
  137. lbLink.Enabled := not acStart.Enabled;
  138. end;
  139. procedure TfrMain.acStartExecute(Sender: TObject);
  140. begin
  141. BrookHTTPServer1.Open;
  142. end;
  143. procedure TfrMain.acStopExecute(Sender: TObject);
  144. begin
  145. BrookHTTPServer1.Close;
  146. end;
  147. procedure TfrMain.lbLinkMouseEnter(Sender: TObject);
  148. begin
  149. lbLink.Font.Style := lbLink.Font.Style + [fsUnderline];
  150. end;
  151. procedure TfrMain.lbLinkMouseLeave(Sender: TObject);
  152. begin
  153. lbLink.Font.Style := lbLink.Font.Style - [fsUnderline];
  154. end;
  155. procedure TfrMain.lbLinkClick(Sender: TObject);
  156. begin
  157. OpenURL(lbLink.Caption);
  158. end;
  159. procedure TfrMain.BrookHTTPServer1Request(ASender: TObject;
  160. ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse);
  161. begin
  162. if ARequest.Headers['Accept'] = SSE_HEADER then
  163. begin
  164. AResponse.Headers.Add('Access-Control-Allow-Origin', '*');
  165. AResponse.Headers.Add('Content-Type', SSE_HEADER);
  166. AResponse.SendStream(TSSEStream.Create, 200);
  167. end
  168. else
  169. AResponse.Send(PAGE_COUNTING, HTML_HEADER, 200);
  170. end;
  171. procedure TfrMain.BrookHTTPServer1RequestError(ASender: TObject;
  172. ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse;
  173. AException: Exception);
  174. begin
  175. AResponse.SendFmt(PAGE_ERROR, [AException.Message], HTML_HEADER, 500);
  176. end;
  177. procedure TfrMain.BrookHTTPServer1Start(Sender: TObject);
  178. begin
  179. UpdateControls;
  180. end;
  181. procedure TfrMain.BrookHTTPServer1Stop(Sender: TObject);
  182. begin
  183. UpdateControls;
  184. end;
  185. procedure TfrMain.edPortChange(Sender: TObject);
  186. begin
  187. UpdateControls;
  188. end;
  189. end.