BrookHTTPServer.pas 32 KB

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