BrookHTTPServer.pas 32 KB

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