BrookHTTPServer.pas 32 KB

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