BrookHTTPServer.pas 31 KB

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