BrookHTTPServer.pas 30 KB

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