BrookHTTPServer.pas 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  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. { Contains classes which composes a fast event-driven HTTP(S) server. }
  26. unit BrookHTTPServer;
  27. {$I BrookDefines.inc}
  28. interface
  29. uses
  30. SysUtils,
  31. Classes,
  32. Marshalling,
  33. libsagui,
  34. BrookUtility,
  35. BrookHandledClasses,
  36. BrookExtra,
  37. BrookHTTPAuthentication,
  38. BrookHTTPRequest,
  39. BrookHTTPResponse;
  40. resourcestring
  41. { Error message @code('Active server.'). }
  42. SBrookActiveServer = 'Active server.';
  43. { Error message @code('Cannot create server handle.'). }
  44. SBrookCannotCreateServerHandle = 'Cannot create server handle.';
  45. { Error message @code('TLS is not available.'). }
  46. SBrookTLSNotAvailable = Concat(
  47. 'TLS is not available. Please download the Sagui library with ',
  48. 'TLS support at: https://github.com/risoflora/libsagui/releases');
  49. { Error message @code('Private key cannot be empty.'). }
  50. SBrookEmptyPrivateKey = 'Private key cannot be empty.';
  51. { Error message @code('Certificate cannot be empty.'). }
  52. SBrookEmptyCertificate = 'Certificate cannot be empty.';
  53. type
  54. { Handles exceptions related to HTTP server security. }
  55. EBrookHTTPServerSecurity = class(Exception);
  56. { Class which holds the TLS properties for the HTTPS server. }
  57. TBrookHTTPServerSecurity = class(TPersistent)
  58. private
  59. FActive: Boolean;
  60. FPrivateKey: string;
  61. FPrivatePassword: string;
  62. FCertificate: string;
  63. FTrust: string;
  64. FDHParams: string;
  65. FPriorities: string;
  66. function IsActiveStored: Boolean;
  67. public
  68. { Copies properties from another security source.
  69. @param(ASource[in] Security source.) }
  70. procedure Assign(ASource: TPersistent); override;
  71. { Clears the common TLS properties. }
  72. procedure Clear; virtual;
  73. { Validates the common TLS properties. }
  74. procedure Validate; {$IFNDEF DEBUG}inline;{$ENDIF}
  75. published
  76. { Activates the TLS support. }
  77. property Active: Boolean read FActive write FActive stored IsActiveStored;
  78. { Content of the private key (key.pem) to be used by the HTTPS server. }
  79. property PrivateKey: string read FPrivateKey write FPrivateKey;
  80. { Password of the private key. }
  81. property PrivatePassword: string read FPrivatePassword
  82. write FPrivatePassword;
  83. { Content of the certificate (cert.pem) to be used by the HTTPS server. }
  84. property Certificate: string read FCertificate write FCertificate;
  85. { Content of the certificate (ca.pem) to be used by the HTTPS server for
  86. client authentication. }
  87. property Trust: string read FTrust write FTrust;
  88. { Content of the Diffie-Hellman parameters (dh.pem) to be used by the HTTPS
  89. server for key exchange. }
  90. property DHParams: string read FDHParams write FDHParams;
  91. { Content of the cipher algorithm. Default: @code(NORMAL). }
  92. property Priorities: string read FPriorities write FPriorities;
  93. end;
  94. { Event signature used by HTTP server to handle the clients authentication. }
  95. TBrookHTTPAuthenticateEvent = function(ASender: TObject;
  96. AAuthentication: TBrookHTTPAuthentication; ARequest: TBrookHTTPRequest;
  97. AResponse: TBrookHTTPResponse): Boolean of object;
  98. { Event signature used by HTTP server to handle errors in the clients
  99. authentication. }
  100. TBrookHTTPAuthenticateErrorEvent = procedure(ASender: TObject;
  101. AAuthentication: TBrookHTTPAuthentication; ARequest: TBrookHTTPRequest;
  102. AResponse: TBrookHTTPResponse; AException: Exception) of object;
  103. { Event signature used by HTTP server to handle requests. }
  104. TBrookHTTPRequestEvent = procedure(ASender: TObject;
  105. ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse) of object;
  106. { Event signature used by HTTP server to handle error in the requests. }
  107. TBrookHTTPRequestErrorEvent = procedure(ASender: TObject;
  108. ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse;
  109. AException: Exception) of object;
  110. { Event signature used by HTTP server to handle client connection events. }
  111. TBrookHTTPServerClientConnectionEvent = procedure(ASender: TObject;
  112. const AClient: Pointer; var AClosed: Boolean) of object;
  113. { Handles exceptions related to HTTP server. }
  114. EBrookHTTPServer = class(Exception);
  115. { Fast event-driven HTTP(S) server class. }
  116. TBrookHTTPServer = class(TBrookHandledComponent)
  117. private
  118. FHandle: Psg_httpsrv;
  119. FAuthenticated: Boolean;
  120. FHostName: string;
  121. FBacklog: Word;
  122. FConnectionLimit: Cardinal;
  123. FConnectionTimeout: Cardinal;
  124. FNoFavicon: Boolean;
  125. FPayloadLimit: NativeUInt;
  126. FUploadsLimit: UInt64;
  127. FActive: Boolean;
  128. FPort: UInt16;
  129. FPostBufferSize: NativeUInt;
  130. FThreaded: Boolean;
  131. FStreamedActive: Boolean;
  132. FStreamedAuthenticated: Boolean;
  133. FThreadPoolSize: Cardinal;
  134. FUploadsDir: string;
  135. FSecurity: TBrookHTTPServerSecurity;
  136. FOnAuthenticate: TBrookHTTPAuthenticateEvent;
  137. FOnAuthenticateError: TBrookHTTPAuthenticateErrorEvent;
  138. FOnRequest: TBrookHTTPRequestEvent;
  139. FOnRequestError: TBrookHTTPRequestErrorEvent;
  140. FOnClientConnection: TBrookHTTPServerClientConnectionEvent;
  141. FOnError: TBrookErrorEvent;
  142. FOnStart: TNotifyEvent;
  143. FOnStop: TNotifyEvent;
  144. function GetConnectionLimit: Cardinal;
  145. function GetConnectionTimeout: Cardinal;
  146. function GetPayloadLimit: NativeUInt;
  147. function GetUploadsLimit: UInt64;
  148. function GetPort: UInt16;
  149. function GetPostBufferSize: NativeUInt;
  150. function GetThreaded: Boolean;
  151. function GetThreadPoolSize: Cardinal;
  152. function GetUploadsDir: string;
  153. function IsActiveStored: Boolean;
  154. function IsAuthenticatedStored: Boolean;
  155. function IsHostNameStored: Boolean;
  156. function IsBacklogStored: Boolean;
  157. function IsConnectionLimitStored: Boolean;
  158. function IsConnectionTimeoutStored: Boolean;
  159. function IsNoFaviconStored: Boolean;
  160. function IsPayloadLimitStored: Boolean;
  161. function IsUploadsLimitStored: Boolean;
  162. function IsPortStored: Boolean;
  163. function IsPostBufferSizeStored: Boolean;
  164. function IsThreadedStored: Boolean;
  165. function IsThreadPoolSizeStored: Boolean;
  166. function IsUploadsDirStored: Boolean;
  167. procedure SetAuthenticated(AValue: Boolean);
  168. procedure SetHostName(const AValue: string);
  169. procedure SetBacklog(AValue: Word);
  170. procedure SetConnectionLimit(AValue: Cardinal);
  171. procedure SetConnectionTimeout(AValue: Cardinal);
  172. procedure SetPayloadLimit(AValue: NativeUInt);
  173. procedure SetSecurity(AValue: TBrookHTTPServerSecurity);
  174. procedure SetUploadsLimit(AValue: UInt64);
  175. procedure SetPort(AValue: UInt16);
  176. procedure SetPostBufferSize(AValue: NativeUInt);
  177. procedure SetThreaded(AValue: Boolean);
  178. procedure SetThreadPoolSize(AValue: Cardinal);
  179. procedure SetUploadsDir(const AValue: string);
  180. procedure InternalCreateServerHandle; {$IFNDEF DEBUG}inline;{$ENDIF}
  181. procedure InternalFreeServerHandle; {$IFNDEF DEBUG}inline;{$ENDIF}
  182. procedure InternalShutdownServer; {$IFNDEF DEBUG}inline;{$ENDIF}
  183. procedure InternalCheckServerOption(Aret: cint);
  184. {$IFNDEF DEBUG}inline;{$ENDIF}
  185. procedure InternalLibUnloadEvent(ASender: TObject);
  186. protected
  187. class function DoAuthenticationCallback(Acls: Pcvoid; Aauth: Psg_httpauth;
  188. Areq: Psg_httpreq; Ares: Psg_httpres): cbool; cdecl; static;
  189. class procedure DoRequestCallback(Acls: Pcvoid; Areq: Psg_httpreq;
  190. Ares: Psg_httpres); cdecl; static;
  191. class procedure DoClientConnectionCallback(Acls: Pcvoid;
  192. const Aclient: Pcvoid; Aclosed: Pcbool); cdecl; static;
  193. class procedure DoErrorCallback(Acls: Pcvoid;
  194. const Aerr: Pcchar); cdecl; static;
  195. function CreateAuthentication(
  196. AHandle: Pointer): TBrookHTTPAuthentication; virtual;
  197. function CreateSecurity: TBrookHTTPServerSecurity; virtual;
  198. function CreateRequest(AHandle: Pointer): TBrookHTTPRequest; virtual;
  199. function CreateResponse(AHandle: Pointer): TBrookHTTPResponse; virtual;
  200. function CreateError(const AMessage: string): Exception; virtual;
  201. procedure HandleAuthenticateError(AAuthentication: TBrookHTTPAuthentication;
  202. ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse;
  203. AException: Exception); virtual;
  204. function HandleAuthenticate(AAuthentication: TBrookHTTPAuthentication;
  205. ARequest: TBrookHTTPRequest;
  206. AResponse: TBrookHTTPResponse): Boolean; virtual;
  207. procedure HandleRequestError(ARequest: TBrookHTTPRequest;
  208. AResponse: TBrookHTTPResponse; AException: Exception); virtual;
  209. procedure HandleRequest(ARequest: TBrookHTTPRequest;
  210. AResponse: TBrookHTTPResponse); virtual;
  211. procedure HandleClientConnection(ASender: TObject; const AClient: Pointer;
  212. var AClosed: Boolean); virtual;
  213. procedure HandleError(ASender: TObject; AException: Exception); virtual;
  214. procedure Loaded; override;
  215. function GetHandle: Pointer; override;
  216. function GetMHDHandle: Pointer; virtual;
  217. procedure DoError(ASender: TObject; AException: Exception); virtual;
  218. function DoAuthenticate(ASender: TObject;
  219. AAuthentication: TBrookHTTPAuthentication; ARequest: TBrookHTTPRequest;
  220. AResponse: TBrookHTTPResponse): Boolean; virtual;
  221. procedure DoAuthenticateError(ASender: TObject;
  222. AAuthentication: TBrookHTTPAuthentication; ARequest: TBrookHTTPRequest;
  223. AResponse: TBrookHTTPResponse; AException: Exception); virtual;
  224. procedure DoRequest(ASender: TObject; ARequest: TBrookHTTPRequest;
  225. AResponse: TBrookHTTPResponse); virtual;
  226. procedure DoRequestError(ASender: TObject; ARequest: TBrookHTTPRequest;
  227. AResponse: TBrookHTTPResponse; AException: Exception); virtual;
  228. procedure DoClientConnection(ASender: TObject; const AClient: Pointer;
  229. var AClosed: Boolean); virtual;
  230. procedure CheckInactive; {$IFNDEF DEBUG}inline;{$ENDIF}
  231. procedure SetActive(AValue: Boolean); virtual;
  232. procedure DoOpen; virtual;
  233. procedure DoClose; virtual;
  234. public
  235. { Creates an instance of @code(TBrookHTTPServer).
  236. @param(AOwner[in] Owner component.) }
  237. constructor Create(AOwner: TComponent); override;
  238. { Destroys an instance of @code(TBrookHTTPServer). }
  239. destructor Destroy; override;
  240. { Starts the HTTP(S) server. }
  241. procedure Open;
  242. { Stops the HTTP(S) server. }
  243. procedure Close;
  244. { Contains the MHD instance. }
  245. property MHDHandle: Pointer read GetMHDHandle;
  246. published
  247. { Activates the HTTP(S) server. }
  248. property Active: Boolean read FActive write SetActive stored IsActiveStored;
  249. { Enables/disables the basic HTTP authentication. }
  250. property Authenticated: Boolean read FAuthenticated write SetAuthenticated
  251. stored IsAuthenticatedStored;
  252. { Host name for listening to connections. }
  253. property HostName: string read FHostName write SetHostName
  254. stored IsHostNameStored;
  255. { Port for listening to connections. }
  256. property Port: UInt16 read GetPort write SetPort stored IsPortStored
  257. default 0;
  258. { Maximum length of the queue of pending connections. Default: 511. }
  259. property Backlog: Word read FBacklog write SetBacklog stored IsBacklogStored
  260. default BROOK_BACKLOG;
  261. { Enables/disables the threaded model. If @true, the server creates one
  262. thread per connection. }
  263. property Threaded: Boolean read GetThreaded write SetThreaded
  264. stored IsThreadedStored default False;
  265. { Directory to store the uploaded files. }
  266. property UploadsDir: string read GetUploadsDir write SetUploadsDir
  267. stored IsUploadsDirStored;
  268. { Post buffering size. }
  269. property PostBufferSize: NativeUInt read GetPostBufferSize
  270. write SetPostBufferSize stored IsPostBufferSizeStored
  271. default BROOK_POST_BUFFER_SIZE;
  272. { Total payload limit. Use zero for no limit. }
  273. property PayloadLimit: NativeUInt read GetPayloadLimit write SetPayloadLimit
  274. stored IsPayloadLimitStored default BROOK_PAYLOAD_LIMIT;
  275. { Total uploads limit. Use zero for no limit. }
  276. property UploadsLimit: UInt64 read GetUploadsLimit write SetUploadsLimit
  277. stored IsUploadsLimitStored default BROOK_UPLOADS_LIMIT;
  278. { Thread pool size. Size greater than 1 enables the thread pooling. }
  279. property ThreadPoolSize: Cardinal read GetThreadPoolSize
  280. write SetThreadPoolSize stored IsThreadPoolSizeStored default 0;
  281. { Inactivity time (in seconds) to a client get time out. }
  282. property ConnectionTimeout: Cardinal read GetConnectionTimeout
  283. write SetConnectionTimeout stored IsConnectionTimeoutStored default 0;
  284. { Concurrent connections limit. Use zero for no limit. }
  285. property ConnectionLimit: Cardinal read GetConnectionLimit
  286. write SetConnectionLimit stored IsConnectionLimitStored default 0;
  287. { Enables/disables the favicon handling. If @true, it avoids @code(404) errors
  288. by sending an empty content (@code(204)) if path is @code('/favicon.ico'). }
  289. property NoFavicon: Boolean read FNoFavicon write FNoFavicon
  290. stored IsNoFaviconStored default False;
  291. { Holds the TLS properties for the HTTPS server. }
  292. property Security: TBrookHTTPServerSecurity read FSecurity
  293. write SetSecurity;
  294. { Event triggered when a client requests authentication. }
  295. property OnAuthenticate: TBrookHTTPAuthenticateEvent read FOnAuthenticate
  296. write FOnAuthenticate;
  297. { Event triggered when a client authentication raises errors. }
  298. property OnAuthenticateError: TBrookHTTPAuthenticateErrorEvent
  299. read FOnAuthenticateError write FOnAuthenticateError;
  300. { Event triggered when a client requests a content. }
  301. property OnRequest: TBrookHTTPRequestEvent read FOnRequest write FOnRequest;
  302. { Event triggered when a client request raises errors. }
  303. property OnRequestError: TBrookHTTPRequestErrorEvent read FOnRequestError
  304. write FOnRequestError;
  305. { Event triggered when a client connects to or disconnects from the server. }
  306. property OnClientConnection: TBrookHTTPServerClientConnectionEvent
  307. read FOnClientConnection write FOnClientConnection;
  308. { Event triggered when the HTTP server raises errors. }
  309. property OnError: TBrookErrorEvent read FOnError write FOnError;
  310. { Event triggered when the HTTP server starts successfully. }
  311. property OnStart: TNotifyEvent read FOnStart write FOnStart;
  312. { Event triggered when the HTTP server stops successfully. }
  313. property OnStop: TNotifyEvent read FOnStop write FOnStop;
  314. end;
  315. implementation
  316. { TBrookHTTPServerSecurity }
  317. procedure TBrookHTTPServerSecurity.Assign(ASource: TPersistent);
  318. var
  319. VSource: TBrookHTTPServerSecurity;
  320. begin
  321. if ASource is TBrookHTTPServerSecurity then
  322. begin
  323. VSource := ASource as TBrookHTTPServerSecurity;
  324. FPrivateKey := VSource.PrivateKey;
  325. FPrivatePassword := VSource.PrivatePassword;
  326. FCertificate := VSource.Certificate;
  327. FTrust := VSource.Trust;
  328. FDHParams := VSource.DHParams;
  329. FPriorities := VSource.Priorities;
  330. end
  331. else
  332. inherited Assign(ASource);
  333. end;
  334. function TBrookHTTPServerSecurity.IsActiveStored: Boolean;
  335. begin
  336. Result := FActive;
  337. end;
  338. procedure TBrookHTTPServerSecurity.Validate;
  339. begin
  340. if FPrivateKey.IsEmpty then
  341. raise EBrookHTTPServerSecurity.Create(SBrookEmptyPrivateKey);
  342. if FCertificate.IsEmpty then
  343. raise EBrookHTTPServerSecurity.Create(SBrookEmptyCertificate);
  344. end;
  345. procedure TBrookHTTPServerSecurity.Clear;
  346. begin
  347. FActive := False;
  348. FPrivateKey := '';
  349. FPrivatePassword := '';
  350. FCertificate := '';
  351. FTrust := '';
  352. FDHParams := '';
  353. FPriorities := '';
  354. end;
  355. { TBrookHTTPServer }
  356. constructor TBrookHTTPServer.Create(AOwner: TComponent);
  357. begin
  358. inherited Create(AOwner);
  359. FSecurity := CreateSecurity;
  360. SgLib.UnloadEvents.Add(InternalLibUnloadEvent, Self);
  361. FBacklog := BROOK_BACKLOG;
  362. FPostBufferSize := BROOK_POST_BUFFER_SIZE;
  363. FPayloadLimit := BROOK_PAYLOAD_LIMIT;
  364. FUploadsLimit := BROOK_UPLOADS_LIMIT;
  365. end;
  366. destructor TBrookHTTPServer.Destroy;
  367. begin
  368. try
  369. SetActive(False);
  370. finally
  371. FSecurity.Free;
  372. SgLib.UnloadEvents.Remove(InternalLibUnloadEvent);
  373. inherited Destroy;
  374. end;
  375. end;
  376. procedure TBrookHTTPServer.InternalCreateServerHandle;
  377. var
  378. VACb: sg_httpauth_cb;
  379. begin
  380. if FAuthenticated then
  381. VACb := DoAuthenticationCallback
  382. else
  383. VACb := nil;
  384. FHandle := sg_httpsrv_new2(VACb, DoRequestCallback, DoErrorCallback, Self);
  385. if not Assigned(FHandle) then
  386. raise EInvalidPointer.Create(SBrookCannotCreateServerHandle);
  387. end;
  388. procedure TBrookHTTPServer.InternalFreeServerHandle;
  389. begin
  390. sg_httpsrv_free(FHandle);
  391. FHandle := nil;
  392. end;
  393. procedure TBrookHTTPServer.InternalShutdownServer;
  394. begin
  395. sg_httpsrv_shutdown(FHandle);
  396. end;
  397. procedure TBrookHTTPServer.InternalCheckServerOption(Aret: cint);
  398. begin
  399. if Aret <> 0 then
  400. begin
  401. InternalFreeServerHandle;
  402. SgLib.CheckLastError(Aret);
  403. end;
  404. end;
  405. function TBrookHTTPServer.CreateAuthentication(
  406. AHandle: Pointer): TBrookHTTPAuthentication;
  407. begin
  408. Result := TBrookHTTPAuthentication.Create(AHandle);
  409. end;
  410. function TBrookHTTPServer.CreateSecurity: TBrookHTTPServerSecurity;
  411. begin
  412. Result := TBrookHTTPServerSecurity.Create;
  413. end;
  414. function TBrookHTTPServer.CreateRequest(AHandle: Pointer): TBrookHTTPRequest;
  415. begin
  416. Result := TBrookHTTPRequest.Create(AHandle);
  417. end;
  418. function TBrookHTTPServer.CreateResponse(AHandle: Pointer): TBrookHTTPResponse;
  419. begin
  420. Result := TBrookHTTPResponse.Create(AHandle);
  421. end;
  422. function TBrookHTTPServer.CreateError(const AMessage: string): Exception;
  423. begin
  424. Result := EBrookHTTPServer.Create(AMessage);
  425. end;
  426. class function TBrookHTTPServer.DoAuthenticationCallback(Acls: Pcvoid;
  427. Aauth: Psg_httpauth; Areq: Psg_httpreq; Ares: Psg_httpres): cbool; cdecl;
  428. var
  429. VSrv: TBrookHTTPServer;
  430. VAuth: TBrookHTTPAuthentication;
  431. VReq: TBrookHTTPRequest;
  432. VRes: TBrookHTTPResponse;
  433. begin
  434. VSrv := Acls;
  435. VReq := VSrv.CreateRequest(Areq);
  436. VRes := VSrv.CreateResponse(Ares);
  437. try
  438. if VSrv.FNoFavicon and VReq.IsFavicon then
  439. Exit(True);
  440. VAuth := VSrv.CreateAuthentication(Aauth);
  441. try
  442. Result := VSrv.HandleAuthenticate(VAuth, VReq, VRes);
  443. finally
  444. VAuth.Free;
  445. end;
  446. finally
  447. VRes.Free;
  448. VReq.Free;
  449. end;
  450. end;
  451. class procedure TBrookHTTPServer.DoRequestCallback(Acls: Pcvoid;
  452. Areq: Psg_httpreq; Ares: Psg_httpres); cdecl;
  453. var
  454. VSrv: TBrookHTTPServer;
  455. VReq: TBrookHTTPRequest;
  456. VRes: TBrookHTTPResponse;
  457. begin
  458. VSrv := Acls;
  459. VReq := VSrv.CreateRequest(Areq);
  460. VRes := VSrv.CreateResponse(Ares);
  461. try
  462. if VSrv.FNoFavicon and VReq.IsFavicon then
  463. VRes.SendEmpty
  464. else
  465. begin
  466. VSrv.HandleRequest(VReq, VRes);
  467. if VRes.IsEmpty and (not VReq.IsIsolated) then
  468. VRes.SendEmpty;
  469. end;
  470. finally
  471. VRes.Free;
  472. VReq.Free;
  473. end;
  474. end;
  475. class procedure TBrookHTTPServer.DoClientConnectionCallback(Acls: Pcvoid;
  476. const Aclient: Pcvoid; Aclosed: Pcbool); cdecl;
  477. var
  478. VSrv: TBrookHTTPServer;
  479. begin
  480. VSrv := Acls;
  481. VSrv.HandleClientConnection(VSrv, Aclient, PBoolean(Aclosed)^);
  482. end;
  483. class procedure TBrookHTTPServer.DoErrorCallback(Acls: Pcvoid;
  484. const Aerr: Pcchar); cdecl;
  485. var
  486. VSrv: TBrookHTTPServer;
  487. VExcept: Exception;
  488. begin
  489. VSrv := Acls;
  490. VExcept := VSrv.CreateError(TMarshal.ToString(Aerr));
  491. try
  492. VSrv.HandleError(VSrv, VExcept);
  493. finally
  494. VExcept.Free;
  495. end;
  496. end;
  497. procedure TBrookHTTPServer.CheckInactive;
  498. begin
  499. if (not (csLoading in ComponentState)) and Active then
  500. raise EInvalidOpException.Create(SBrookActiveServer);
  501. end;
  502. procedure TBrookHTTPServer.InternalLibUnloadEvent(ASender: TObject);
  503. begin
  504. if Assigned(ASender) then
  505. TBrookHTTPServer(ASender).Close;
  506. end;
  507. procedure TBrookHTTPServer.HandleAuthenticateError(
  508. AAuthentication: TBrookHTTPAuthentication; ARequest: TBrookHTTPRequest;
  509. AResponse: TBrookHTTPResponse; AException: Exception);
  510. begin
  511. AResponse.Reset;
  512. try
  513. DoAuthenticateError(Self, AAuthentication, ARequest, AResponse, AException);
  514. except
  515. on E: Exception do
  516. AResponse.Send(E.Message, BROOK_CT_TEXT_PLAIN, 500);
  517. end;
  518. end;
  519. function TBrookHTTPServer.HandleAuthenticate(
  520. AAuthentication: TBrookHTTPAuthentication; ARequest: TBrookHTTPRequest;
  521. AResponse: TBrookHTTPResponse): Boolean;
  522. begin
  523. try
  524. Result := DoAuthenticate(Self, AAuthentication, ARequest, AResponse);
  525. except
  526. on E: Exception do
  527. begin
  528. Result := False;
  529. HandleAuthenticateError(AAuthentication, ARequest, AResponse, E);
  530. end;
  531. end;
  532. end;
  533. procedure TBrookHTTPServer.HandleRequestError(ARequest: TBrookHTTPRequest;
  534. AResponse: TBrookHTTPResponse; AException: Exception);
  535. begin
  536. AResponse.Reset;
  537. try
  538. DoRequestError(Self, ARequest, AResponse, AException);
  539. except
  540. on E: Exception do
  541. AResponse.Send(E.Message, BROOK_CT_TEXT_PLAIN, 500);
  542. end;
  543. end;
  544. procedure TBrookHTTPServer.HandleRequest(ARequest: TBrookHTTPRequest;
  545. AResponse: TBrookHTTPResponse);
  546. begin
  547. try
  548. DoRequest(Self, ARequest, AResponse);
  549. except
  550. on E: Exception do
  551. HandleRequestError(ARequest, AResponse, E);
  552. end;
  553. end;
  554. procedure TBrookHTTPServer.HandleClientConnection(ASender: TObject;
  555. const AClient: Pointer; var AClosed: Boolean);
  556. begin
  557. DoClientConnection(ASender, AClient, AClosed);
  558. end;
  559. procedure TBrookHTTPServer.HandleError(ASender: TObject; AException: Exception);
  560. begin
  561. DoError(ASender, AException);
  562. end;
  563. procedure TBrookHTTPServer.Loaded;
  564. begin
  565. inherited Loaded;
  566. try
  567. if FStreamedAuthenticated then
  568. SetAuthenticated(True);
  569. if FStreamedActive then
  570. SetActive(True);
  571. except
  572. if csDesigning in ComponentState then
  573. begin
  574. if Assigned(ApplicationHandleException) then
  575. ApplicationHandleException(ExceptObject)
  576. else
  577. ShowException(ExceptObject, ExceptAddr);
  578. end
  579. else
  580. raise;
  581. end;
  582. end;
  583. function TBrookHTTPServer.GetHandle: Pointer;
  584. begin
  585. Result := FHandle;
  586. end;
  587. function TBrookHTTPServer.GetMHDHandle: Pointer;
  588. begin
  589. SgLib.Check;
  590. Result := sg_httpsrv_handle(FHandle);
  591. end;
  592. procedure TBrookHTTPServer.DoError(ASender: TObject;
  593. AException: Exception);
  594. begin
  595. if Assigned(FOnError) then
  596. FOnError(ASender, AException)
  597. else
  598. if Assigned(ApplicationShowException) then
  599. ApplicationShowException(AException)
  600. else if Assigned(ApplicationHandleException) then
  601. ApplicationHandleException(AException)
  602. else
  603. ShowException(AException, Pointer(AException));
  604. end;
  605. function TBrookHTTPServer.DoAuthenticate(ASender: TObject;
  606. AAuthentication: TBrookHTTPAuthentication; ARequest: TBrookHTTPRequest;
  607. AResponse: TBrookHTTPResponse): Boolean;
  608. begin
  609. Result := Assigned(FOnAuthenticate) and
  610. FOnAuthenticate(ASender, AAuthentication, ARequest, AResponse);
  611. end;
  612. procedure TBrookHTTPServer.DoAuthenticateError(ASender: TObject;
  613. AAuthentication: TBrookHTTPAuthentication; ARequest: TBrookHTTPRequest;
  614. AResponse: TBrookHTTPResponse; AException: Exception);
  615. begin
  616. if Assigned(FOnAuthenticateError) then
  617. FOnAuthenticateError(ASender, AAuthentication, ARequest, AResponse,
  618. AException)
  619. else
  620. HandleRequestError(ARequest, AResponse, AException);
  621. end;
  622. procedure TBrookHTTPServer.DoRequest(ASender: TObject;
  623. ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse);
  624. begin
  625. if Assigned(FOnRequest) then
  626. FOnRequest(ASender, ARequest, AResponse)
  627. else
  628. AResponse.SendEmpty;
  629. end;
  630. procedure TBrookHTTPServer.DoRequestError(ASender: TObject;
  631. ARequest: TBrookHTTPRequest; AResponse: TBrookHTTPResponse;
  632. AException: Exception);
  633. begin
  634. if Assigned(FOnRequestError) then
  635. FOnRequestError(ASender, ARequest, AResponse, AException)
  636. else
  637. AResponse.Send(AException.Message, BROOK_CT_TEXT_PLAIN, 500);
  638. end;
  639. procedure TBrookHTTPServer.DoClientConnection(ASender: TObject;
  640. const AClient: Pointer; var AClosed: Boolean);
  641. begin
  642. if Assigned(FOnClientConnection) then
  643. FOnClientConnection(ASender, AClient, AClosed);
  644. end;
  645. procedure TBrookHTTPServer.SetPort(AValue: UInt16);
  646. begin
  647. if not FStreamedActive then
  648. CheckInactive;
  649. FPort := AValue;
  650. end;
  651. procedure TBrookHTTPServer.SetPostBufferSize(AValue: NativeUInt);
  652. begin
  653. if not FStreamedActive then
  654. CheckInactive;
  655. FPostBufferSize := AValue;
  656. end;
  657. procedure TBrookHTTPServer.SetConnectionLimit(AValue: Cardinal);
  658. begin
  659. if not FStreamedActive then
  660. CheckInactive;
  661. FConnectionLimit := AValue;
  662. end;
  663. procedure TBrookHTTPServer.SetConnectionTimeout(AValue: Cardinal);
  664. begin
  665. if not FStreamedActive then
  666. CheckInactive;
  667. FConnectionTimeout := AValue;
  668. end;
  669. procedure TBrookHTTPServer.SetPayloadLimit(AValue: NativeUInt);
  670. begin
  671. if not FStreamedActive then
  672. CheckInactive;
  673. FPayloadLimit := AValue;
  674. end;
  675. procedure TBrookHTTPServer.SetSecurity(AValue: TBrookHTTPServerSecurity);
  676. begin
  677. if FSecurity = AValue then
  678. Exit;
  679. if Assigned(AValue) then
  680. FSecurity.Assign(AValue)
  681. else
  682. FSecurity.Clear;
  683. end;
  684. procedure TBrookHTTPServer.SetUploadsLimit(AValue: UInt64);
  685. begin
  686. if not FStreamedActive then
  687. CheckInactive;
  688. FUploadsLimit := AValue;
  689. end;
  690. procedure TBrookHTTPServer.SetThreaded(AValue: Boolean);
  691. begin
  692. if not FStreamedActive then
  693. CheckInactive;
  694. FThreaded := AValue;
  695. end;
  696. procedure TBrookHTTPServer.SetThreadPoolSize(AValue: Cardinal);
  697. begin
  698. if not FStreamedActive then
  699. CheckInactive;
  700. FThreadPoolSize := AValue;
  701. if FThreadPoolSize > 0 then
  702. System.IsMultiThread := True;
  703. end;
  704. procedure TBrookHTTPServer.SetUploadsDir(const AValue: string);
  705. begin
  706. if not FStreamedActive then
  707. CheckInactive;
  708. FUploadsDir := AValue;
  709. end;
  710. function TBrookHTTPServer.IsConnectionLimitStored: Boolean;
  711. begin
  712. Result := FConnectionLimit > 0;
  713. end;
  714. function TBrookHTTPServer.IsConnectionTimeoutStored: Boolean;
  715. begin
  716. Result := FConnectionTimeout > 0;
  717. end;
  718. function TBrookHTTPServer.IsNoFaviconStored: Boolean;
  719. begin
  720. Result := FNoFavicon;
  721. end;
  722. function TBrookHTTPServer.IsPayloadLimitStored: Boolean;
  723. begin
  724. Result := FPayloadLimit <> BROOK_PAYLOAD_LIMIT;
  725. end;
  726. function TBrookHTTPServer.IsUploadsLimitStored: Boolean;
  727. begin
  728. Result := FUploadsLimit <> BROOK_UPLOADS_LIMIT;
  729. end;
  730. function TBrookHTTPServer.IsActiveStored: Boolean;
  731. begin
  732. Result := FActive;
  733. end;
  734. function TBrookHTTPServer.GetPort: UInt16;
  735. begin
  736. if FActive and not (csDesigning in ComponentState) then
  737. begin
  738. SgLib.Check;
  739. FPort := sg_httpsrv_port(FHandle);
  740. end;
  741. Result := FPort;
  742. end;
  743. function TBrookHTTPServer.GetThreaded: Boolean;
  744. begin
  745. if FActive and not (csDesigning in ComponentState) then
  746. begin
  747. SgLib.Check;
  748. FThreaded := sg_httpsrv_is_threaded(FHandle);
  749. end;
  750. Result := FThreaded;
  751. end;
  752. function TBrookHTTPServer.GetUploadsDir: string;
  753. begin
  754. if FActive and not (csDesigning in ComponentState) then
  755. begin
  756. SgLib.Check;
  757. FUploadsDir := TMarshal.ToString(sg_httpsrv_upld_dir(FHandle));
  758. end;
  759. Result := FUploadsDir;
  760. end;
  761. function TBrookHTTPServer.GetPostBufferSize: NativeUInt;
  762. begin
  763. if FActive and not (csDesigning in ComponentState) then
  764. begin
  765. SgLib.Check;
  766. FPostBufferSize := sg_httpsrv_post_buf_size(FHandle);
  767. end;
  768. Result := FPostBufferSize;
  769. end;
  770. function TBrookHTTPServer.GetPayloadLimit: NativeUInt;
  771. begin
  772. if FActive and not (csDesigning in ComponentState) then
  773. begin
  774. SgLib.Check;
  775. FPayloadLimit := sg_httpsrv_payld_limit(FHandle);
  776. end;
  777. Result := FPayloadLimit;
  778. end;
  779. function TBrookHTTPServer.GetUploadsLimit: UInt64;
  780. begin
  781. if FActive and not (csDesigning in ComponentState) then
  782. begin
  783. SgLib.Check;
  784. FUploadsLimit := sg_httpsrv_uplds_limit(FHandle);
  785. end;
  786. Result := FUploadsLimit;
  787. end;
  788. function TBrookHTTPServer.GetThreadPoolSize: Cardinal;
  789. begin
  790. if FActive and not (csDesigning in ComponentState) then
  791. begin
  792. SgLib.Check;
  793. FThreadPoolSize := sg_httpsrv_thr_pool_size(FHandle);
  794. end;
  795. Result := FThreadPoolSize;
  796. end;
  797. function TBrookHTTPServer.GetConnectionTimeout: Cardinal;
  798. begin
  799. if FActive and not (csDesigning in ComponentState) then
  800. begin
  801. SgLib.Check;
  802. FConnectionTimeout := sg_httpsrv_con_timeout(FHandle);
  803. end;
  804. Result := FConnectionTimeout;
  805. end;
  806. function TBrookHTTPServer.GetConnectionLimit: Cardinal;
  807. begin
  808. if FActive and not (csDesigning in ComponentState) then
  809. begin
  810. SgLib.Check;
  811. FConnectionLimit := sg_httpsrv_con_limit(FHandle);
  812. end;
  813. Result := FConnectionLimit;
  814. end;
  815. function TBrookHTTPServer.IsAuthenticatedStored: Boolean;
  816. begin
  817. Result := FAuthenticated;
  818. end;
  819. function TBrookHTTPServer.IsHostNameStored: Boolean;
  820. begin
  821. Result := FHostName <> EmptyStr;
  822. end;
  823. function TBrookHTTPServer.IsBacklogStored: Boolean;
  824. begin
  825. Result := FBacklog <> BROOK_BACKLOG;
  826. end;
  827. function TBrookHTTPServer.IsPortStored: Boolean;
  828. begin
  829. Result := FPort <> 0;
  830. end;
  831. function TBrookHTTPServer.IsPostBufferSizeStored: Boolean;
  832. begin
  833. Result := FPostBufferSize <> BROOK_POST_BUFFER_SIZE;
  834. end;
  835. function TBrookHTTPServer.IsThreadedStored: Boolean;
  836. begin
  837. Result := FThreaded;
  838. end;
  839. function TBrookHTTPServer.IsThreadPoolSizeStored: Boolean;
  840. begin
  841. Result := FThreadPoolSize > 0;
  842. end;
  843. function TBrookHTTPServer.IsUploadsDirStored: Boolean;
  844. begin
  845. Result := not FUploadsDir.IsEmpty;
  846. end;
  847. procedure TBrookHTTPServer.SetAuthenticated(AValue: Boolean);
  848. begin
  849. if not FStreamedActive then
  850. CheckInactive;
  851. if AValue = FAuthenticated then
  852. Exit;
  853. if AValue and (csReading in ComponentState) then
  854. FStreamedAuthenticated := True;
  855. FAuthenticated := AValue;
  856. end;
  857. procedure TBrookHTTPServer.SetHostName(const AValue: string);
  858. begin
  859. if not FStreamedActive then
  860. CheckInactive;
  861. FHostName := AValue;
  862. end;
  863. procedure TBrookHTTPServer.SetBacklog(AValue: Word);
  864. begin
  865. if not FStreamedActive then
  866. CheckInactive;
  867. FBacklog := AValue;
  868. end;
  869. procedure TBrookHTTPServer.SetActive(AValue: Boolean);
  870. begin
  871. if AValue = FActive then
  872. Exit;
  873. if csDesigning in ComponentState then
  874. begin
  875. if not (csLoading in ComponentState) then
  876. SgLib.Check;
  877. FActive := AValue;
  878. end
  879. else
  880. if AValue then
  881. begin
  882. if csReading in ComponentState then
  883. FStreamedActive := True
  884. else
  885. DoOpen;
  886. end
  887. else
  888. DoClose;
  889. end;
  890. procedure TBrookHTTPServer.DoOpen;
  891. var
  892. M: TMarshaller;
  893. begin
  894. if Assigned(FHandle) then
  895. Exit;
  896. SgLib.Check;
  897. InternalCreateServerHandle;
  898. if not FUploadsDir.IsEmpty then
  899. InternalCheckServerOption(sg_httpsrv_set_upld_dir(FHandle,
  900. M.ToCString(FUploadsDir)));
  901. if FPostBufferSize > 0 then
  902. InternalCheckServerOption(sg_httpsrv_set_post_buf_size(FHandle,
  903. FPostBufferSize));
  904. if FPayloadLimit > 0 then
  905. InternalCheckServerOption(sg_httpsrv_set_payld_limit(FHandle,
  906. FPayloadLimit));
  907. if FUploadsLimit > 0 then
  908. InternalCheckServerOption(sg_httpsrv_set_uplds_limit(FHandle,
  909. FUploadsLimit));
  910. if FThreadPoolSize > 0 then
  911. InternalCheckServerOption(sg_httpsrv_set_thr_pool_size(FHandle,
  912. FThreadPoolSize));
  913. if FConnectionTimeout > 0 then
  914. InternalCheckServerOption(sg_httpsrv_set_con_timeout(FHandle,
  915. FConnectionTimeout));
  916. if FConnectionLimit > 0 then
  917. InternalCheckServerOption(sg_httpsrv_set_con_limit(FHandle,
  918. FConnectionLimit));
  919. InternalCheckServerOption(sg_httpsrv_set_cli_cb(FHandle,
  920. DoClientConnectionCallback, Self));
  921. if FSecurity.Active then
  922. begin
  923. FSecurity.Validate;
  924. if FHostName <> EmptyStr then
  925. begin
  926. if not Assigned(sg_httpsrv_tls_listen4) then
  927. raise ENotSupportedException.Create(SBrookTLSNotAvailable);
  928. FActive := sg_httpsrv_tls_listen4(FHandle,
  929. M.ToCNullableString(FSecurity.PrivateKey),
  930. M.ToCNullableString(FSecurity.PrivatePassword),
  931. M.ToCNullableString(FSecurity.Certificate),
  932. M.ToCNullableString(FSecurity.Trust),
  933. M.ToCNullableString(FSecurity.DHParams),
  934. M.ToCNullableString(FSecurity.Priorities), M.ToCString(FHostName),
  935. FPort, FBacklog, FThreaded);
  936. end
  937. else
  938. begin
  939. if not Assigned(sg_httpsrv_tls_listen3) then
  940. raise ENotSupportedException.Create(SBrookTLSNotAvailable);
  941. FActive := sg_httpsrv_tls_listen3(FHandle,
  942. M.ToCNullableString(FSecurity.PrivateKey),
  943. M.ToCNullableString(FSecurity.PrivatePassword),
  944. M.ToCNullableString(FSecurity.Certificate),
  945. M.ToCNullableString(FSecurity.Trust),
  946. M.ToCNullableString(FSecurity.DHParams),
  947. M.ToCNullableString(FSecurity.Priorities), FPort, FThreaded);
  948. end;
  949. end
  950. else
  951. begin
  952. if FHostName <> EmptyStr then
  953. FActive := sg_httpsrv_listen2(FHandle, M.ToCString(FHostName), FPort,
  954. FBacklog, FThreaded)
  955. else
  956. FActive := sg_httpsrv_listen(FHandle, FPort, FThreaded);
  957. end;
  958. if not FActive then
  959. InternalFreeServerHandle
  960. else
  961. if Assigned(FOnStart) then
  962. FOnStart(Self);
  963. end;
  964. procedure TBrookHTTPServer.DoClose;
  965. begin
  966. if not Assigned(FHandle) then
  967. Exit;
  968. SgLib.Check;
  969. InternalShutdownServer;
  970. InternalFreeServerHandle;
  971. FActive := Assigned(FHandle);
  972. if Assigned(FOnStop) then
  973. FOnStop(Self);
  974. end;
  975. procedure TBrookHTTPServer.Open;
  976. begin
  977. SetActive(True);
  978. end;
  979. procedure TBrookHTTPServer.Close;
  980. begin
  981. SetActive(False);
  982. end;
  983. end.