
Purpose: HTTP/HTTPS Server Classes
- this unit is a part of the Open Source Synopse mORMot framework 2, licensed under a MPL/GPL/LGPL three license - see LICENSE.md
| Unit Name | Description | |
|---|---|---|
| mormot.core.base | Framework Core Shared Types and RTL-like Functions | |
| mormot.core.buffers | Framework Core Low-Level Memory Buffer Process | |
| mormot.core.data | Framework Core Low-Level Data Processing Functions | |
| mormot.core.datetime | Framework Core Low-Level Date and Time Support | |
| mormot.core.json | Framework Core Low-Level JSON Processing | |
| mormot.core.log | Framework Core Logging | |
| mormot.core.os | Framework Core Low-Level Wrappers to the Operating-System API | |
| mormot.core.rtti | Framework Core Low-Level Cross-Compiler RTTI Definitions | |
| mormot.core.search | Framework Core Text, Binary and Time Search Engines | |
| mormot.core.text | Framework Core Low-Level Text Processing | |
| mormot.core.threads | Framework Core Multi-Threading Support | |
| mormot.core.unicode | Framework Core Low-Level Unicode UTF-8 UTF-16 Ansi Conversion | |
| mormot.core.zip | Framework Core Zip/Deflate Compression Support | |
| mormot.crypt.core | Framework Core Cryptographic Process (Hashing and Cypher) | |
| mormot.crypt.secure | Framework Core Authentication and Security Features | |
| mormot.lib.winhttp | Windows HTTP and WebSockets API Libraries | |
| mormot.net.client | HTTP/HTTPS Client Classes | |
| mormot.net.http | HTTP/HTTPS Abstract Process Classes and Definitions | |
| mormot.net.sock | Low-level access to the OperatingSystem Sockets API (e.g. WinSock2) |
| Objects | Description | |
|---|---|---|
| EHttpPeerCache | Exception class raised on THttpPeerCache issues | |
| EHttpServer | Exception raised during HTTP process | |
| EUriRouter | Exception class raised during TUriRouter.Rewrite/Run registration | |
| THttpApiServer | HTTP server using fast http.sys kernel-mode server | |
| THttpApiWebSocketConnection | Structure representing a single WebSocket connection | |
| THttpApiWebSocketServer | HTTP & WebSocket server using fast http.sys kernel-mode server | |
| THttpApiWebSocketServerProtocol | Protocol Handler of websocket endpoints events | |
| THttpPeerCache | Implement a local peer-to-peer download cache via UDP and TCP | |
| THttpPeerCacheHash | Store a hash value and its algorithm, for THttpPeerCacheMessage.Hash | |
| THttpPeerCacheMessage | One UDP request frame used during THttpPeerCache discovery | |
| THttpPeerCacheProcess | One THttpPeerCache.OnDownload instance | |
| THttpPeerCacheSettings | Define how THttpPeerCache handles its process | |
| THttpPeerCacheThread | Background UDP server thread, associated to a THttpPeerCache instance | |
| THttpPeerCrypt | Abstract parent to THttpPeerCache for its cryptographic core | |
| THttpServer | Main HTTP server Thread using the standard Sockets API (e.g. WinSock) | |
| THttpServerGeneric | Abstract parent class to implement a HTTP server | |
| THttpServerRequest | A generic input/output structure used for HTTP server requests | |
| THttpServerResp | HTTP response Thread as used by THttpServer Socket API based class | |
| THttpServerSocket | Socket API based HTTP/1.1 server class used by THttpServer Threads | |
| THttpServerSocketGeneric | Abstract parent class for both THttpServer and THttpAsyncServer | |
| TSynThreadPoolHttpApiWebSocketServer | A Thread Pool, used for fast handling of WebSocket requests | |
| TSynThreadPoolTHttpServer | A simple Thread Pool, used for fast handling HTTP requests of a THttpServer | |
| TSynWebSocketGuard | Thread for closing deprecated WebSocket connections | |
| TUdpServerThread | Abstract UDP server thread | |
| TUriRouter | Efficient server-side URI routing for THttpServerGeneric | |
| TUriTree | Implement a Radix Tree to hold all registered URI for a given HTTP method | |
| TUriTreeNode | Implement a Radix Tree node to hold one URI registration | |
| TUriTreeNodeData | Context information, as cloned by TUriTreeNode.Split() |
TUdpServerThread = class(TLoggedThread)
Abstract UDP server thread
constructor Create(LogClass: TSynLogClass; const BindAddress, BindPort, ProcessName: RawUtf8; TimeoutMS: integer); reintroduce;
Initialize and bind the server instance, in non-suspended state
destructor Destroy; override;
Finalize the processing thread
TUriTreeNodeData = record
Context information, as cloned by TUriTreeNode.Split()
Execute: TOnHttpServerRequest;
The callback registered by Run() for this URI
ExecuteOpaque: pointer;
An additional pointer value, assigned to Ctxt.RouteOpaque of Execute()
ToUri: RawUtf8;
The Rewrite() URI text
ToUriErrorStatus: word ;
The HTTP error code for a Rewrite() with an integer ToUri (e.g. '404')
ToUriMethod: TUriRouterMethod;
The URI method to be used after ToUri rewrite
ToUriPosLen: TIntegerDynArray;
[pos1,len1,valndx1,pos2,len2,valndx2,...] trios from ToUri content
ToUriStaticLen: integer;
The size of all ToUriPosLen[] static content
TUriTreeNode = class(TRadixTreeNodeParams)
Implement a Radix Tree node to hold one URI registration
Data: TUriTreeNodeData;
All context information, as cloned by Split()
function Split(const Text: RawUtf8): TRadixTreeNode; override;
Overriden to support the additional Data fields
TUriTree = class(TRadixTreeParams)
Implement a Radix Tree to hold all registered URI for a given HTTP method
function Root: TUriTreeNode;
Access to the root node of this tree
EUriRouter = class(ERadixTree)
Exception class raised during TUriRouter.Rewrite/Run registration
TUriRouter = class(TSynPersistentRWLightLock)
Efficient server-side URI routing for THttpServerGeneric
- Process() is done with no memory allocation for a static route, using a very efficient Radix Tree for path lookup, over a thread-safe non-blocking URI parsing with values extractions for rewrite or execution
- here are some numbers from TNetworkProtocols._TUriTree on my laptop:
1000 URI lookups in 37us i.e. 25.7M/s, aver. 37ns 1000 URI static rewrites in 80us i.e. 11.9M/s, aver. 80ns 1000 URI parametrized rewrites in 117us i.e. 8.1M/s, aver. 117ns 1000 URI static execute in 91us i.e. 10.4M/s, aver. 91ns 1000 URI parametrized execute in 162us i.e. 5.8M/s, aver. 162ns
constructor Create(aNodeClass: TRadixTreeNodeClass; aOptions: TRadixTreeOptions = []); reintroduce;
Initialize this URI routing engine
destructor Destroy; override;
Finalize this URI routing engine
function Lookup(const aUri, aUriMethod: RawUtf8): TUriTreeNode;
Search for a given URI match
- could be used e.g. in OnBeforeBody() to quickly reject an invalid URI
- this method is thread-safe
function Process(Ctxt: THttpServerRequestAbstract): integer;
Perform URI parsing and rewrite/execution within HTTP server Ctxt members
- should return 0 to continue the process, on a HTTP status code to abort if the request has been handled by a TOnHttpServerRequest callback
- this method is thread-safe
procedure Clear(aMethods: TUriRouterMethods = [urmGet .. high(TUriRouterMethod)]);
Erase all previous registrations, optionally for a given HTTP method
- currently, there is no way to delete a route once registered, to optimize the process thread-safety: use Clear then re-register
procedure Delete(const aUri: RawUtf8; const aExecute: TOnHttpServerRequest; aExecuteOpaque: pointer = nil); overload;
Just a wrapper around Run([urmDelete], aUri, aExecute) registration method
procedure Delete(const aFrom, aTo: RawUtf8; aToMethod: TUriRouterMethod = urmDelete); overload;
Just a wrapper around Rewrite(urmDelete, aFrom, aToMethod, aTo)
- e.g. Route.Delete('/doremove', '/root/myservice/delete', urmPost);
procedure Get(const aFrom, aTo: RawUtf8; aToMethod: TUriRouterMethod = urmGet); overload;
Just a wrapper around Rewrite(urmGet, aFrom, aToMethod, aTo)
- e.g. Route.Get('/info', 'root/timestamp/info');
- e.g. Route.Get('/user/<id>', '/root/userservice/new?id=<id>'); will rewrite internally '/user/1234' URI as '/root/userservice/new?id=1234'
- e.g. Route.Get('/user/<int:id>', '/root/userservice/new?id=<id>'); to ensure id is a real integer before redirection
- e.g. Route.Get('/admin.php', '403');
- e.g. Route.Get('/*', '/static/*'); with '*' synonymous to '<path:path>'
procedure Get(const aUri: RawUtf8; const aExecute: TOnHttpServerRequest; aExecuteOpaque: pointer = nil); overload;
Just a wrapper around Run([urmGet], aUri, aExecute) registration method
- e.g. Route.Get('/plaintext', DoPlainText);
procedure Head(const aUri: RawUtf8; const aExecute: TOnHttpServerRequest; aExecuteOpaque: pointer = nil); overload;
Just a wrapper around Run([urmHead], aUri, aExecute) registration method
procedure Head(const aFrom, aTo: RawUtf8; aToMethod: TUriRouterMethod = urmHead); overload;
Just a wrapper around Rewrite(urmHead, aFrom, aToMethod, aTo)
- e.g. Route.Head('/doremove', '/root/myservice/Head', urmPost);
procedure Options(const aFrom, aTo: RawUtf8; aToMethod: TUriRouterMethod = urmOptions); overload;
Just a wrapper around Rewrite(urmOptions, aFrom, aToMethod, aTo)
- e.g. Route.Options('/doremove', '/root/myservice/Options', urmPost);
procedure Options(const aUri: RawUtf8; const aExecute: TOnHttpServerRequest; aExecuteOpaque: pointer = nil); overload;
Just a wrapper around Run([urmOptions], aUri, aExecute) registration method
procedure Post(const aUri: RawUtf8; const aExecute: TOnHttpServerRequest; aExecuteOpaque: pointer = nil); overload;
Just a wrapper around Run([urmPost], aUri, aExecute) registration method
procedure Post(const aFrom, aTo: RawUtf8; aToMethod: TUriRouterMethod = urmPost); overload;
Just a wrapper around Rewrite(urmPost, aFrom, aToMethod, aTo)
- e.g. Route.Post('/doconvert', '/root/myservice/convert');
procedure Put(const aFrom, aTo: RawUtf8; aToMethod: TUriRouterMethod = urmPut); overload;
Just a wrapper around Rewrite(urmPut, aFrom, aToMethod, aTo)
- e.g. Route.Put('/domodify', '/root/myservice/update', urmPost);
procedure Put(const aUri: RawUtf8; const aExecute: TOnHttpServerRequest; aExecuteOpaque: pointer = nil); overload;
Just a wrapper around Run([urmPut], aUri, aExecute) registration method
procedure Rewrite(aFrom: TUriRouterMethod; const aFromUri: RawUtf8; aTo: TUriRouterMethod; const aToUri: RawUtf8);
Register an URI rewrite with optional <param> place holders
- <param> will be replaced in aToUri
- if aToUri is an '200'..'599' integer, it will return it as HTTP error
- otherwise, the URI will be rewritten into aToUri, e.g.
Rewrite(urmGet, '/info', urmGet, 'root/timestamp/info'); Rewrite(urmGet, '/path/from/<from>/to/<to>', urmPost, '/root/myservice/convert?from=<from>&to=<to>'); // for IMyService.Convert Rewrite(urmGet, '/index.php', '400'); // to avoid fuzzing Rewrite(urmGet, '/*', '/static/*' // '*' synonymous to '<path:path>'
procedure Run(aFrom: TUriRouterMethods; const aFromUri: RawUtf8; const aExecute: TOnHttpServerRequest; aExecuteOpaque: pointer = nil);
Assign a TOnHttpServerRequest callback with a given URI
- <param> place holders will be parsed and available in callback as Ctxt['param'] default property or Ctxt.RouteInt64/RouteEquals methods
- could be used e.g. for standard REST process as
Route.Run([urmGet], '/user/<user>/pic', DoUserPic) // retrieve a list
Route.Run([urmGet, urmPost, urmPut, urmDelete],
'/user/<user>/pic/<id>', DoUserPic) // CRUD picture accessprocedure RunMethods(RouterMethods: TUriRouterMethods; Instance: TObject; const Prefix: RawUtf8 = '/');
Assign the published methods of a class instance to their URI via RTTI
- the signature of each method should match TOnHttpServerRequest
- the method name is used for the URI, e.g. Instance.user as '/user', with exact case matching, and replacing _ in the method name by '-', e.g. Instance.cached_query as '/cached-query'
property Deletes: integer read fEntries[urmDelete];
How many DELETE rules have been registered
property Gets: integer read fEntries[urmGet];
How many GET rules have been registered
property Heads: integer read fEntries[urmHead];
How many HEAD rules have been registered
property Optionss: integer read fEntries[urmOptions];
How many OPTIONS rules have been registered
property Posts: integer read fEntries[urmPost];
How many POST rules have been registered
property Puts: integer read fEntries[urmPut];
How many PUT rules have been registered
property Tree: TUriRouterTree read fTree;
Access to the internal per-method TUriTree instance
- some Tree[] may be nil if the HTTP method has not been registered yet
- used only for testing/validation purpose
property TreeOptions: TRadixTreeOptions read fTreeOptions write fTreeOptions;
How the TUriRouter instance should be created
- should be set before calling Run/Rewrite registration methods
EHttpServer = class(ESynException)
Exception raised during HTTP process
THttpServerRequest = class(THttpServerRequestAbstract)
A generic input/output structure used for HTTP server requests
- URL/Method/InHeaders/InContent properties are input parameters
- OutContent/OutContentType/OutCustomHeader are output parameters
constructor Create(aServer: THttpServerGeneric; aConnectionID: THttpServerConnectionID; aConnectionThread: TSynThread; aConnectionFlags: THttpServerRequestFlags; aConnectionOpaque: PHttpServerConnectionOpaque); virtual;
Initialize the context, associated to a HTTP server instance
destructor Destroy; override;
Finalize this execution context
function RouteOpaque: pointer; override;
An additional custom parameter, as provided to TUriRouter.Setup
function SetAsyncResponse: integer;
Notify the server that it should wait for the OnAsyncResponse callback
- would raise an EHttpServer exception if OnAsyncResponse is not set
- returns HTTP_ASYNCRESPONSE (777) internal code as recognized e.g. by THttpAsyncServer
function SetupResponse(var Context: THttpRequestContext; CompressGz, MaxSizeAtOnce: integer): PRawByteStringBuffer;
Prepare one reusable HTTP State Machine for sending the response
function TempJsonWriter(var temp: TTextWriterStackBuffer): TJsonWriter;
Low-level initialization of the associated TJsonWriter instance
- will reset and reuse an TJsonWriter associated to this execution context
- as called by SetOutJson() oveloaded methods using RTTI
- a local TTextWriterStackBuffer should be provided as temporary buffer
procedure Recycle(aConnectionID: THttpServerConnectionID; aConnectionThread: TSynThread; aConnectionFlags: THttpServerRequestFlags; aConnectionOpaque: PHttpServerConnectionOpaque);
Could be called before Prepare() to reuse an existing instance
procedure SetErrorMessage(const Fmt: RawUtf8; const Args: array of const);
Just a wrapper around fErrorMessage := FormatString()
procedure SetOutJson(Value: pointer; TypeInfo: PRttiInfo); overload;
Serialize a given value as JSON into OutContent and OutContentType fields
procedure SetOutJson(Value: TObject); overload;
Serialize a given TObject as JSON into OutContent and OutContentType fields
property ErrorMessage: string read fErrorMessage write fErrorMessage;
Optional error message which will be used by SetupResponse
property FullUrl: SynUnicode read GetFullUrl;
For THttpApiServer, input parameter containing the caller full URL
property HttpApiRequest: PHTTP_REQUEST read fHttpApiRequest;
For THttpApiServer, points to a PHTTP_REQUEST structure
property OnAsyncResponse: TOnHttpServerRequestAsyncResponse read fOnAsyncResponse write fOnAsyncResponse;
A callback used for asynchronous response to the client
- only implemented by the THttpAsyncServer by now
property Server: THttpServerGeneric read fServer;
The associated server instance
- may be a THttpServer or a THttpApiServer class
THttpServerGeneric = class(TNotifiedThread)
Abstract parent class to implement a HTTP server
- do not use it, but rather THttpServer/THttpAsyncServer or THttpApiServer
constructor Create(const OnStart, OnStop: TOnNotifyThread; const ProcessName: RawUtf8; ProcessOptions: THttpServerOptions); reintroduce; virtual;
Initialize the server instance
destructor Destroy; override;
Release all memory and handlers used by this server
function Callback(Ctxt: THttpServerRequest; aNonBlocking: boolean): cardinal; virtual;
Server can send a request back to the client, when the connection has been upgraded e.g. to WebSockets
- InURL/InMethod/InContent properties are input parameters (InContentType is ignored)
- OutContent/OutContentType/OutCustomHeader are output parameters
- Ctxt.ConnectionID should be set, so that the method could know which connnection is to be used - returns HTTP_NOTFOUND (404) if unknown
- result of the function is the HTTP error code (200 if OK, e.g.)
- warning: this void implementation will raise an EHttpServer exception - inherited classes should override it, e.g. as in TWebSocketServerRest
function CanNotifyCallback: boolean;
TRUE if the inherited class is able to handle callbacks
- only TWebSocketServer/TWebSocketAsyncServer have this ability by now
function ReplaceRoute(another: TUriRouter): TUriRouter;
Thread-safe replace the TUriRouter instance
- returns the existing instance: caller should keep it for a few seconds untouched prior to Free it, to let finish any pending background process
function Request(Ctxt: THttpServerRequestAbstract): cardinal; virtual;
Override this function to customize your http server
- InURL/InMethod/InContent properties are input parameters
- OutContent/OutContentType/OutCustomHeader are output parameters
- result of the function is the HTTP error code (200 if OK, e.g.),
- OutCustomHeader is available to handle Content-Type/Location
- if OutContentType is STATICFILE_CONTENT_TYPE (i.e. '!STATICFILE'), then OutContent is the UTF-8 filename of a file to be sent directly to the client via http.sys or NGINX's X-Accel-Redirect; the OutCustomHeader should contain the eventual 'Content-type: ....' value
- default implementation is to call the OnRequest event (if existing), and will return HTTP_NOTFOUND if OnRequest was not set
- warning: this process must be thread-safe (can be called by several threads simultaneously, but with a given Ctxt instance for each)
function Route: TUriRouter;
Specify URI routes for internal URI rewrites or callback execution
- rules registered here will be processed before main Request/OnRequest
- URI rewrites allow to extend the default routing, e.g. from TRestServer
- callbacks execution allow efficient server-side processing with parameters
- static routes could be defined e.g. Route.Get('/', '/root/default')
- <param> place holders could be defined for proper URI rewrite e.g. Route.Post('/user/<id>', '/root/userservice/new?id=<id>') will rewrite internally '/user/1234' URI as '/root/userservice/new?id=1234'
- could be used e.g. for standard REST process via event callbacks with Ctxt['user'] or Ctxt.RouteInt64('id') parameter extraction in DoUserPic:
Route.Run([urmGet], '/user/<user>/pic', DoUserPic) // retrieve a list
Route.Run([urmGet, urmPost, urmPut, urmDelete],
'/user/<user>/pic/<id>', DoUserPic) // CRUD picture access- warning: with the THttpApiServer, URIs will be limited by the actual root URI registered at http.sys level - there is no such limitation with the socket servers, which bind to a port, so handle all URIs on it
procedure RegisterCompress(aFunction: THttpSocketCompress; aCompressMinSize: integer = 1024; aPriority: integer = 10); virtual;
Will register a compression algorithm
- used e.g. to compress on the fly the data, with standard gzip/deflate or custom (synlz) protocols
- you can specify a minimal size (in bytes) before which the content won't be compressed (1024 by default, corresponding to a MTU of 1500 bytes)
- the first registered algorithm will be the prefered one for compression within each priority level (the lower aPriority first)
procedure SetFavIcon(const FavIconContent: RawByteString = 'default');
Will route a GET to /favicon.ico to the given .ico file content
- if none is supplied, the default Synopse/mORMot icon is used
- if '' is supplied, /favicon.ico will return a 404 error status
- warning: with THttpApiServer, may require a proper URI registration
procedure Shutdown;
You can call this method to prepare the HTTP server for shutting down
property Analyzer: THttpAnalyzer read fAnalyzer;
Access to the HTTP analyzer initialized with hsoTelemetryCsv or hsoTelemetryJson options
- you can customize this process via Analyzer.DestFolder
property ApiVersion: RawUtf8 read GetApiVersion;
Returns the API version used by the inherited implementation
property ConnectionsActive: cardinal read GetConnectionsActive;
Returns the number of current HTTP connections
- may not include HTTP/1.0 short-living connections
property HttpQueueLength: cardinal read GetHttpQueueLength write SetHttpQueueLength;
Defines request/response internal queue length
- default value if 1000, which sounds fine for most use cases
- for THttpApiServer, will return 0 if the system does not support HTTP API 2.0 (i.e. under Windows XP or Server 2003)
- for THttpServer or THttpAsyncServer, will shutdown any incoming accepted socket if the internal number of pending requests exceed this limit
- increase this value if you don't have any load-balancing in place, and in case of e.g. many 503 HTTP answers or if many "QueueFull" messages appear in HTTP.sys log files (normally in C:\Windows\System32\LogFiles\HTTPERR\httperr*.log) - may appear with thousands of concurrent clients accessing at once the same server - see @http://msdn.microsoft.com/en-us/library/windows/desktop/aa364501
- you can use this property with a reverse-proxy as load balancer, e.g. with nginx configured as such:
location / {
proxy_pass http://balancing_upstream;
proxy_next_upstream error timeout invalid_header http_500 http_503;
proxy_connect_timeout 2;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Conn-ID $connection
}see https://synopse.info/forum/viewtopic.php?pid=28174#p28174
property Logger: THttpLogger read fLogger;
Access to the HTTP logger initialized with hsoEnableLogging option
- you can customize the logging process via Logger.Format, Logger.DestFolder, Logger.DefaultRotate, Logger.DefaultRotateFiles properties and Logger.DefineHost() method
- equals nil if hsoEnableLogging was not set in the constructor
property MaximumAllowedContentLength: cardinal read fMaximumAllowedContentLength write SetMaximumAllowedContentLength;
Reject any incoming request with a body size bigger than this value
- default to 0, meaning any input size is allowed
- returns HTTP_PAYLOADTOOLARGE = 413 error if "Content-Length" incoming header overflow the supplied number of bytes
property OnAfterRequest: TOnHttpServerRequest read fOnAfterRequest write SetOnAfterRequest;
Event handler called after request is processed but before response is sent back to client
- main purpose is to apply post-processor, not part of request logic
- if handler returns value > 0 it will override the OnRequest response code
- warning: this handler must be thread-safe (could be called from several threads), and is NOT called after Route() callbacks execution
property OnAfterResponse: TOnHttpServerAfterResponse read fOnAfterResponse write SetOnAfterResponse;
Event handler called after response is sent back to client
- main purpose is to apply post-response analysis, logging, etc...
- warning: this handler must be thread-safe (could be called from several threads), and IS called after Route() callbacks execution
property OnBeforeBody: TOnHttpServerBeforeBody read fOnBeforeBody write SetOnBeforeBody;
Event handler called just before the body is retrieved from the client
- should return HTTP_SUCCESS=200 to continue the process, or an HTTP error code to reject the request immediately, and close the connection
property OnBeforeRequest: TOnHttpServerRequest read fOnBeforeRequest write SetOnBeforeRequest;
Event handler called after HTTP body has been retrieved, before OnRequest
- may be used e.g. to return a HTTP_ACCEPTED (202) status to client and continue a long-term job inside the OnRequest handler in the same thread; or to modify incoming information before passing it to main business logic, (header preprocessor, body encoding etc...)
- if the handler returns > 0 server will send a response immediately, unless return code is HTTP_ACCEPTED (202), then OnRequest will be called
- warning: this handler must be thread-safe (could be called from several threads), and is NOT called before Route() callbacks execution
property OnHttpThreadStart: TOnNotifyThread read fOnThreadStart write fOnThreadStart;
Event handler called after each working Thread is just initiated
- called in the thread context at first place in THttpServerGeneric.Execute
property OnHttpThreadTerminate: TOnNotifyThread read fOnThreadTerminate write SetOnTerminate;
Event handler called when a working Thread is terminating
- called in the corresponding thread context
- the TThread.OnTerminate event will be called within a Synchronize() wrapper, so it won't fit our purpose
- to be used e.g. to call CoUnInitialize from thread in which CoInitialize was made, for instance via a method defined as such:
procedure TMyServer.OnHttpThreadTerminate(Sender: TThread); begin // TSqlDBConnectionPropertiesThreadSafe fMyConnectionProps.EndCurrentThread; end;
- is used e.g. by TRest.EndCurrentThread for proper multi-threading
property OnRequest: TOnHttpServerRequest read fOnRequest write SetOnRequest;
Main event handler called by the default implementation of the virtual Request method to process a given request
- OutCustomHeader will handle Content-Type/Location
- if OutContentType is STATICFILE_CONTENT_TYPE (i.e. '!STATICFILE'), then OutContent is the UTF-8 filename of a file to be sent directly to the client via http.sys or NGINX's X-Accel-Redirect; the OutCustomHeader should contain the eventual 'Content-type: ....' value
- warning: this process must be thread-safe (can be called by several threads simultaneously)
property OnSendFile: TOnHttpServerSendFile read fOnSendFile write fOnSendFile;
Custom event handler used to send a local file for STATICFILE_CONTENT_TYPE
- see also NginxSendFileFrom() method
property Options: THttpServerOptions read fOptions write SetOptions;
Allow to customize this HTTP server instance
- some inherited classes may have only partial support of those options
property ProcessName: RawUtf8 read fProcessName write fProcessName;
The associated process name
property RemoteConnIDHeader: RawUtf8 read fRemoteConnIDHeader write SetRemoteConnIDHeader;
The value of a custom HTTP header containing the real client connection ID
- by default, Ctxt.ConnectionID information will be retrieved from our socket layer - but if the server runs behind some proxy service, you should define here the HTTP header name which indicates the real remote connection, for example as 'X-Conn-ID', setting in nginx config:
proxy_set_header X-Conn-ID $connection
property RemoteIPHeader: RawUtf8 read fRemoteIPHeader write SetRemoteIPHeader;
The value of a custom HTTP header containing the real client IP
- by default, the RemoteIP information will be retrieved from the socket layer - but if the server runs behind some proxy service, you should define here the HTTP header name which indicates the true remote client IP value, mostly as 'X-Real-IP' or 'X-Forwarded-For'
property Router: TUriRouter read fRoute;
Read access to the URI router, as published property (e.g. for logs)
- use the Route function to actually setup the routing
- may be nil if Route has never been accessed, i.e. no routing was set
property RouterClass: TRadixTreeNodeClass read fRouterClass write SetRouterClass;
Allow to customize the Route() implementation Radix Tree node class
- if not set, will use TUriTreeNode as defined in this unit
- raise an Exception if set twice, or after Route() is called
property ServerName: RawUtf8 read fServerName write SetServerName;
The Server name, UTF-8 encoded, e.g. 'mORMot2 (Linux)'
- will be served as "Server: ..." HTTP header
- for THttpApiServer, when called from the main instance, will propagate the change to all cloned instances, and included in any HTTP API 2.0 log
THttpServerSocket = class(THttpSocket)
Socket API based HTTP/1.1 server class used by THttpServer Threads
constructor Create(aServer: THttpServer); reintroduce;
Create the socket according to a server
- will register the THttpSocketCompress functions from the server
- once created, caller should call AcceptRequest() to accept the socket
- if TLS is enabled, ensure server certificates are initialized once
function GetConnectionOpaque: PHttpServerConnectionOpaque;
Access to the internal two PtrUInt tags of this connection
- may be nil behind a reverse proxy (i.e. Server.RemoteConnIDHeader<>'')
function GetRequest(withBody: boolean; headerMaxTix: Int64): THttpServerSocketGetRequestResult; virtual;
Main object function called after aClientSock := Accept + Create:
- get Command, Method, URL, Headers and Body (if withBody is TRUE)
- get sent data in Content (if withBody=true and ContentLength<>0)
- returned enumeration will indicates the processing state
property KeepAliveClient: boolean read fKeepAliveClient write fKeepAliveClient;
True if the client is HTTP/1.1 and 'Connection: Close' is not set
- default HTTP/1.1 behavior is "keep alive", unless 'Connection: Close' is specified, cf. RFC 2068 page 108: "HTTP/1.1 applications that do not support persistent connections MUST include the "close" connection option in every message"
property Method: RawUtf8 read Http.CommandMethod;
Contains the method ('GET','POST'.. e.g.) after GetRequest()
property RemoteConnectionID: THttpServerConnectionID read fRemoteConnectionID;
The recognized connection ID, after a call to GetRequest()
- identifies either the raw connection on the current server, or the custom header value as set by a local proxy, e.g. THttpServerGeneric.RemoteConnIDHeader='X-Conn-ID' for nginx
property Server: THttpServer read fServer;
The associated HTTP Server instance - may be nil
property URL: RawUtf8 read Http.CommandUri;
Contains the URL ('/' e.g.) after GetRequest()
THttpServerResp = class(TSynThread)
HTTP response Thread as used by THttpServer Socket API based class
- Execute procedure get the request and calculate the answer, using the thread for a single client connection, until it is closed
- you don't have to overload the protected THttpServerResp Execute method: override THttpServer.Request() function or, if you need a lower-level access (change the protocol, e.g.) THttpServer.Process() method itself
constructor Create(aServerSock: THttpServerSocket; aServer: THttpServer); reintroduce; overload; virtual;
Initialize the response thread for the corresponding incoming socket
- this version will handle KeepAlive, for such an incoming request
constructor Create(aSock: TNetSocket; const aSin: TNetAddr; aServer: THttpServer); reintroduce; overload;
Initialize the response thread for the corresponding incoming socket
- this version will get the request directly from an incoming socket
procedure Shutdown; virtual;
Called by THttpServer.Destroy on existing connections
- set Terminate and close the socket
property ConnectionID: THttpServerConnectionID read fConnectionID;
The unique identifier of this connection
property Server: THttpServer read fServer;
The associated main HTTP server instance
property ServerSock: THttpServerSocket read fServerSock;
The associated socket to communicate with the client
TSynThreadPoolTHttpServer = class(TSynThreadPool)
A simple Thread Pool, used for fast handling HTTP requests of a THttpServer
- will handle multi-connection with less overhead than creating a thread for each incoming request
- will create a THttpServerResp response thread, if the incoming request is identified as HTTP/1.1 keep alive, or HTTP body length is bigger than 1 MB
constructor Create(Server: THttpServer; NumberOfThreads: integer = 32); reintroduce;
Initialize a thread pool with the supplied number of threads
- Task() overridden method processs the HTTP request set by Push()
- up to 256 threads can be associated to a Thread Pool
property BigBodySize: integer read fBigBodySize write fBigBodySize;
When Content-Length is bigger than this value, by-pass the threadpool and creates a dedicated THttpServerResp thread
- default is THREADPOOL_BIGBODYSIZE = 16 MB, but can set a bigger value e.g. behind a buffering proxy if you trust the client not to make DOD
property MaxBodyThreadCount: integer read fMaxBodyThreadCount write fMaxBodyThreadCount;
How many stand-alone THttpServerResp threads can be initialized when a HTTP request comes in
- default is THREADPOOL_MAXWORKTHREADS = 512, but since each thread consume system memory, you should not go so high
- above this value, the thread pool will be used
THttpServerSocketGeneric = class(THttpServerGeneric)
Abstract parent class for both THttpServer and THttpAsyncServer
constructor Create(const aPort: RawUtf8; const OnStart, OnStop: TOnNotifyThread; const ProcessName: RawUtf8; ServerThreadPoolCount: integer = 32; KeepAliveTimeOut: integer = 30000; ProcessOptions: THttpServerOptions = []); reintroduce; virtual;
Create a Server Thread, ready to be bound and listening on a port
- this constructor will raise a EHttpServer exception if binding failed
- expects the port to be specified as string, e.g. '1234'; you can optionally specify a server address to bind to, e.g. '1.2.3.4:1234'
- can listed to local Unix Domain Sockets file in case port is prefixed with 'unix:', e.g. 'unix:/run/myapp.sock' - faster and safer than TCP
- on Linux in case aPort is empty string will check if external fd is passed by systemd and use it (so called systemd socked activation)
- you can specify a number of threads to be initialized to handle incoming connections. Default is 32, which may be sufficient for most cases, maximum is 256. If you set 0, the thread pool will be disabled and one thread will be created for any incoming connection
- you can also tune (or disable with 0) HTTP/1.1 keep alive delay and how incoming request Headers[] are pushed to the processing method
- this constructor won't actually do the port binding, which occurs in the background thread: caller should therefore call WaitStarted after THttpServer.Create()
function AuthorizeServerMem: TDigestAuthServerMem;
Set after a call to SetAuthDigest/SetAuthBasic
- return nil if no such call was made, or not with a TDigestAuthServerMem
- return a TDigestAuthServerMem so that SetCredential/GetUsers are available
function WebSocketsEnable( const aWebSocketsURI, aWebSocketsEncryptionKey: RawUtf8; aWebSocketsAjax: boolean = false; aWebSocketsBinaryOptions: TWebSocketProtocolBinaryOptions = [pboSynLzCompress]): pointer; virtual;
Defines the WebSockets protocols to be used for this Server
- this default implementation will raise an exception
- returns the associated PWebSocketProcessSettings reference on success
procedure InitializeTlsAfterBind;
Could be called after WaitStarted(seconds,'','','') to setup TLS
- validate Sock.TLS.CertificateFile/PrivateKeyFile/PrivatePassword
- otherwise TLS is initialized at first incoming connection, which could be too late in case of invalid Sock.TLS parameters
procedure NginxSendFileFrom(const FileNameLeftTrim: TFileName);
Enable NGINX X-Accel internal redirection for STATICFILE_CONTENT_TYPE
- will define internally a matching OnSendFile event handler
- generating "X-Accel-Redirect: " header, trimming any supplied left case-sensitive file name prefix, e.g. with NginxSendFileFrom('/var/www'):
# Will serve /var/www/protected_files/myfile.tar.gz
# When passed URI /protected_files/myfile.tar.gz
location /protected_files {
internal;
root /var/www;
}- call this method several times to register several folders
procedure SetAuthorizeBasic(const BasicRealm: RawUtf8; const OnBasicAuth: TOnHttpServerBasicAuth); overload;
Allow optional BASIC authentication for some URIs via a callback
- if OnBeforeBody returns 401, the OnBasicAuth callback will be executed to negotiate Basic authentication with the client
procedure SetAuthorizeBasic(const Basic: IBasicAuthServer); overload;
Allow optional BASIC authentication for some URIs via IBasicAuthServer
- if OnBeforeBody returns 401, Digester.OnCheckCredential will be called to negotiate Basic authentication with the client
- the supplied Digester will be owned by this instance: it could be either a TDigestAuthServerFile with its own storage, or a TDigestAuthServerMem instance expecting manual SetCredential() calls
procedure SetAuthorizeDigest(const Digest: IDigestAuthServer);
Allow optional DIGEST authentication for some URIs
- if OnBeforeBody returns 401, Digester.ServerInit and ServerAuth will be called to negotiate Digest authentication with the client
- the supplied Digester will be owned by this instance - typical use is with a TDigestAuthServerFile
procedure SetAuthorizeNone;
Remove any previous SetAuthorizeBasic/SetAuthorizeDigest registration
procedure WaitStarted(Seconds: integer; const CertificateFile: TFileName; const PrivateKeyFile: TFileName = ''; const PrivateKeyPassword: RawUtf8 = ''; const CACertificatesFile: TFileName = ''); overload;
Ensure the HTTP server thread is actually bound to the specified port
- TCrtSocket.Bind() occurs in the background in the Execute method: you should call and check this method result just after THttpServer.Create
- initial THttpServer design was to call Bind() within Create, which works fine on Delphi + Windows, but fails with a EThreadError on FPC/Linux
- raise a EHttpServer if binding failed within the specified period (if port is free, it would be almost immediate)
- calling this method is optional, but if the background thread didn't actually bind the port, the server will be stopped and unresponsive with no explicit error message, until it is terminated
- for hsoEnableTls support, you should either specify the private key and certificate here, or set TLS.PrivateKeyFile/CertificateFile fields - the benefit of this method parameters is that the certificates are loaded and checked now by calling InitializeTlsAfterBind, not at the first client connection (which may be too late)
procedure WaitStarted(Seconds: integer = 30; TLS: PNetTlsContext = nil); overload;
Ensure the HTTP server thread is actually bound to the specified port
- for hsoEnableTls support, allow to specify all server-side TLS events, including callbacks, as supported by OpenSSL
- will raise EHttpServer if the server did not start properly, e.g. could not bind the port within the supplied time
procedure WaitStartedHttps(Seconds: integer = 30; UsePreComputed: boolean = false);
Ensure the server thread is bound as self-signed HTTPS server
- wrap InitNetTlsContextSelfSignedServer() and WaitStarted() with some temporary key files, which are deleted once started
- as used e.g. by TRestHttpServer for secTLSSelfSigned
property Authorize: THttpServerRequestAuthentication read fAuthorize;
Set after a call to SetAuthDigest/SetAuthBasic
property ExecuteState: THttpServerExecuteState read GetExecuteState;
The low-level thread execution thread
property HeaderRetrieveAbortDelay: cardinal read fHeaderRetrieveAbortDelay write fHeaderRetrieveAbortDelay;
Milliseconds delay to reject a connection due to too long header retrieval
- default is 0, i.e. not checked (typical behind a reverse proxy)
property RegisterCompressGzStatic: boolean read GetRegisterCompressGzStatic write SetRegisterCompressGzStatic;
If we should search for local .gz cached file when serving static files
property ServerKeepAliveTimeOut: cardinal read fServerKeepAliveTimeOut write fServerKeepAliveTimeOut;
Time, in milliseconds, for the HTTP/1.1 connections to be kept alive
- default is 30000 ms, i.e. 30 seconds
- setting 0 here (or in KeepAliveTimeOut constructor parameter) will disable keep-alive, and fallback to HTTP.1/0 for all incoming requests (may be a good idea e.g. behind a NGINX reverse proxy)
- see THttpApiServer.SetTimeOutLimits(aIdleConnection) parameter
property Sock: TCrtSocket read fSock;
Access to the main server low-level Socket
- it's a raw TCrtSocket, which only need a socket to be bound, listening and accept incoming request
- for THttpServer inherited class, will own its own instance, then THttpServerSocket/THttpServerResp are created for every connection
- for THttpAsyncServer inherited class, redirect to TAsyncServer.fServer
property SockPort: RawUtf8 read fSockPort;
The bound TCP port, as specified to Create() constructor
property StatBanned: integer index grBanned read GetStat;
How many HTTP connections have been not accepted by hsoBan40xIP option
property StatBodyProcessed: integer index grBodyReceived read GetStat;
How many HTTP bodies have been processed
property StatHeaderClosed: integer index grClosed read GetStat;
How many HTTP connections have been closed
property StatHeaderException: integer index grException read GetStat;
How many invalid HTTP headers raised an exception
property StatHeaderProcessed: integer index grHeaderReceived read GetStat;
How many HTTP headers have been processed
property StatHeaderTimeout: integer index grTimeout read GetStat;
How many HTTP requests were rejected after HeaderRetrieveAbortDelay timeout
property StatIntercepted: integer index grIntercepted read GetStat;
How many HTTP requests were intercepted by the OnHeaderParser event handler
property StatOversizedPayloads: integer index grOversizedPayload read GetStat;
How many HTTP requests pushed more than MaximumAllowedContentLength bytes
property StatRejected: integer index grRejected read GetStat;
How many HTTP requests were rejected by the OnBeforeBody event handler
property StatUpgraded: integer index grUpgraded read GetStat;
How many HTTP connections were upgraded e.g. to WebSockets
property StatWwwAuthenticate: integer index grWwwAuthenticate read GetStat;
How many HTTP 401 "WWW-Authenticate:" responses have been returned
THttpServer = class(THttpServerSocketGeneric)
Main HTTP server Thread using the standard Sockets API (e.g. WinSock)
- bind to a port and listen to incoming requests
- assign this requests to THttpServerResp threads from a ThreadPool
- it implements a HTTP/1.1 compatible server, according to RFC 2068 specifications
- if the client is also HTTP/1.1 compatible, KeepAlive connection is handled: multiple requests will use the existing connection and thread; this is faster and uses less resources, especialy under Windows
- a Thread Pool is used internally to speed up HTTP/1.0 connections - a typical use, under Linux, is to run this class behind a NGINX frontend, configured as https reverse proxy, leaving default "proxy_http_version 1.0" and "proxy_request_buffering on" options for best performance, and setting KeepAliveTimeOut=0 in the THttpServer.Create constructor
- consider using THttpAsyncServer from mormot.net.async if a very high number of concurrent connections is expected, e.g. if using HTTP/1.0 behind a https reverse proxy is not an option
- under Windows, will trigger the firewall UAC popup at first run
- don't forget to use Free method when you are finished
- a typical HTTPS server usecase could be:
fHttpServer := THttpServer.Create('443', nil, nil, '', 32, 30000, [hsoEnableTls]);
fHttpServer.WaitStarted('cert.pem', 'privkey.pem', ''); // cert.pfx for SSPI
// now certificates will be initialized and usedconstructor Create(const aPort: RawUtf8; const OnStart, OnStop: TOnNotifyThread; const ProcessName: RawUtf8; ServerThreadPoolCount: integer = 32; KeepAliveTimeOut: integer = 30000; ProcessOptions: THttpServerOptions = []); override;
Create a socket-based HTTP Server, ready to be bound and listening on a port
- ServerThreadPoolCount < 0 would use a single thread to rule them all
- ServerThreadPoolCount = 0 would create one thread per connection
- ServerThreadPoolCount > 0 would leverage the thread pool, and create one thread only for kept-alive or upgraded connections
destructor Destroy; override;
Release all memory and handlers
property Banned: THttpAcceptBan read fBanned;
Set if hsoBan40xIP has been defined
- indicates e.g. how many accept() have been rejected from their IP
- you can customize its behavior once the server is started by resetting its Seconds/Max/WhiteIP properties, before any connections are made
property OnAcceptIdle: TOnPollSocketsIdle read fOnAcceptIdle write fOnAcceptIdle;
Low-level callback called every few seconds of inactive Accept()
- is called every 5 seconds by default, but could be every second if hsoBan40xIP option (i.e. the Banned property) has been set
- on Windows, requires some requests to trigger the event, because it seems that accept() has timeout only on POSIX systems
property OnHeaderParsed: TOnHttpServerHeaderParsed read fOnHeaderParsed write fOnHeaderParsed;
Low-level callback called before OnBeforeBody and allow quick execution directly from THttpServerSocket.GetRequest
property ServerConnectionActive: integer read fServerConnectionActive write fServerConnectionActive;
Will contain the current number of connections to the server
property ServerConnectionCount: integer read fServerConnectionCount write fServerConnectionCount;
Will contain the total number of connections to the server
- it's the global count since the server started
property ThreadPool: TSynThreadPoolTHttpServer read fThreadPool;
The associated thread pool
- may be nil if ServerThreadPoolCount was 0 on constructor
THttpPeerCacheHash = packed record
Store a hash value and its algorithm, for THttpPeerCacheMessage.Hash
- we store and compare in our implementation the algorithm in addition to the hash, to avoid any potential attack about (unlikely) hash collisions between algorithms, and allow any change of algo restrictions in the future
Algo: THashAlgo;
The algorithm used for Hash
Hash: THash512Rec;
Up to 512-bit of raw binary hash, according to Algo
THttpPeerCacheMessage = packed record
One UDP request frame used during THttpPeerCache discovery
- requests and responses have the same binary layout
- some fields may be void or irrelevant, and the structure is padded with random up to 192 bytes
- over the wire, packets are encrypted and authenticated via AES-GCM-128 with an ending salted checksum for quick anti-fuzzing
BroadcastIP4: cardinal;
The IPv4 broadcast address the local network interface
- e.g. 192.168.1.255
Connections: word;
Number of background download connections currently on this server
DestIP4: cardinal;
The destination IPv4 of this frame
- contains 0 for a broadcast
- allows to filter response frames when broadcasted on POSIX
Hardware: TMacAddressKind;
The hardware model of this network interface
Hash: THttpPeerCacheHash;
Up to 512-bit of binary Hash (and algo) of the requested file content
IP4: cardinal;
The local IPv4 which sent this frame
- e.g. 192.168.1.1
Kind: THttpPeerCacheMessageKind;
The content of this binary frame
MaskIP4: cardinal;
The IPv4 network mask of the local network interface
- e.g. 255.255.255.0
Opaque: QWord;
Some internal state representation, e.g. sent back as pcfBearer
Os: TOperatingSystemVersion;
The Operating System of the Sender
Padding: array[0 .. 42] of byte;
Some random padding up to 192 bytes, used for future content revisions
- e.g. for a TEccPublicKey (ECDHE) and additional fields
RangeEnd: Int64;
The Range ending position of the file content (included)
RangeStart: Int64;
The Range offset of the requested file content
Seq: cardinal;
32-bit sequence number
Size: Int64;
The known full size of this file
Speed: cardinal;
The link speed (in Mbits per second) of the local network interface
Timestamp: cardinal;
The local UnixTimeMinimalUtc value
Uuid: TGuid;
The UUID of the Sender
THttpPeerCacheSettings = class(TSynPersistent)
Define how THttpPeerCache handles its process
constructor Create; override;
Set the default settings
- i.e. Port=8099, LimitMBPerSec=10, LimitClientCount=32, RejectInstablePeersMin=4, CacheTempMaxMB=1000, CacheTempMaxMin=60, CacheTempMinBytes=CachePermMinBytes=2048, BroadcastTimeoutMS=10 HttpTimeoutMS=500 and BroadcastMaxResponses=24
property BroadcastMaxResponses: integer read fBroadcastMaxResponses write fBroadcastMaxResponses;
How many responses UDP broadcast should take into account
- default is 24
property BroadcastTimeoutMS: integer read fBroadcastTimeoutMS write fBroadcastTimeoutMS;
How many milliseconds UDP broadcast should wait for a response
- default is 10 ms which seems enough on a local network
- on Windows, this value is indicative, likely to have 15ms resolution
property CachePermMinBytes: integer read fCachePermMinBytes write fCachePermMinBytes;
Above how many bytes the peer network should be asked for a permanent file
- there is no size limitation if the file is already in the permanent cache, or if the waoNoMinimalSize option is specified by WGet()
- default is 2048 bytes, i.e. 2KB, which is just two network MTU trips
property CachePermPath: TFileName read fCachePermPath write fCachePermPath;
Location of the permanent cached files, available for remote requests
- in respect to CacheTempPath, this folder won't be purged
- the files are cached using their THttpPeerCacheHash values as filename
- if this value equals '', or pcoNoServer is defined in Options, permanent caching would be disabled
property CacheTempMaxMB: integer read fCacheTempMaxMB write fCacheTempMaxMB;
After how many MB in CacheTempPath the folder should be cleaned
- default is 1000, i.e. just below 1 GB
- THttpPeerCache.Create will also always ensure that this value won't take more than 25% of the CacheTempPath folder available space
property CacheTempMaxMin: integer read fCacheTempMaxMin write fCacheTempMaxMin;
After how many minutes files in CacheTempPath could be cleaned
- i.e. the Time-To-Live (TTL) of temporary files
- default is 60 minutes, i.e. 1 hour
property CacheTempMinBytes: integer read fCacheTempMinBytes write fCacheTempMinBytes;
Above how many bytes the peer network should be asked for a temporary file
- there is no size limitation if the file is already in the temporary cache, or if the waoNoMinimalSize option is specified by WGet()
- default is 2048 bytes, i.e. 2KB
property CacheTempPath: TFileName read fCacheTempPath write fCacheTempPath;
Location of the temporary cached files, available for remote requests
- the files are cached using their THttpPeerCacheHash values as filename
- this folder will be purged according to CacheTempMaxMB/CacheTempMaxMin
- if this value equals '', or pcoNoServer is defined in Options, temporary caching would be disabled
property HttpTimeoutMS: integer read fHttpTimeoutMS write fHttpTimeoutMS;
The socket level timeout for HTTP requests
- default to low 500 ms because should be local
property InterfaceFilter: TMacAddressFilter read fInterfaceFilter write fInterfaceFilter;
How GetMacAddress() should find the network, if InterfaceName is not set
property InterfaceName: RawUtf8 read fInterfaceName write fInterfaceName;
Local TMacAddress.Name, Address or IP to be used for UDP and TCP
- Name and Address will be searched case-insensitive
- IP could be exact or eventually a bitmask pattern (e.g. 192.168.1.255)
- if not set, will fallback to the best local makEthernet/makWifi network with broadcasting abilities
- matching TMacAddress.IP will be used with the Port property value to bind the TCP/HTTP server and broadcast the UDP discovery packets, so that only this network interface will be involved to find cache peers
property LimitClientCount: integer read fLimitClientCount write fLimitClientCount;
Can limit how many peer clients can be served content at the same time
- would prevent any overload, to avoid Denial of Service
- default is 32, which means 32 threads with the default THttpServer
property LimitMBPerSec: integer read fLimitMBPerSec write fLimitMBPerSec;
Can limit the peer bandwidth used, in data MegaBytes per second
- will be assigned to each TStreamRedirect.LimitPerSecond instance
- default is 10 MB/s of data, i.e. aroung 100-125 MBit/s on network
- you may set 0 to disable any bandwidth limitation
- you may set -1 to use the default TStreamRedirect.LimitPerSecond value
property Options: THttpPeerCacheOptions read fOptions write fOptions;
Allow to customize the process
property Port: TNetPort read fPort write fPort;
The local port used for UDP and TCP process
- value should match on all peers for proper discovery
- UDP for discovery, TCP for HTTP/HTTPS content delivery
- is 8099 by default, which is unassigned by IANA
property RejectInstablePeersMin: integer read fRejectInstablePeersMin write fRejectInstablePeersMin;
RejectInstablePeersMin will set a delay (in minutes) to ignore any peer which sent invalid UDP frames or HTTP/HTTPS requests
- should be a positive small power of two <= 128
- default is 4, for a 4 minutes time-to-live of IP banishments
- you may set 0 to disable the whole IP ban safety mechanism at UDP level
- use pcoNoBanIP option to disable the IP ban mechanism at HTTP level
THttpPeerCrypt = class(TInterfacedObjectWithCustomCreate)
Abstract parent to THttpPeerCache for its cryptographic core
constructor Create(const aSharedSecret: RawByteString); reintroduce;
Initialize the cryptography of this peer-to-peer node instance
- warning: inherited class should also call AfterSettings once fSettings is defined
destructor Destroy; override;
Finalize this class instance
EHttpPeerCache = class(ESynException)
Exception class raised on THttpPeerCache issues
THttpPeerCacheThread = class(TUdpServerThread)
Background UDP server thread, associated to a THttpPeerCache instance
constructor Create(Owner: THttpPeerCache); reintroduce;
Initialize the background UDP server thread
destructor Destroy; override;
Finalize this instance
THttpPeerCache = class(THttpPeerCrypt)
Implement a local peer-to-peer download cache via UDP and TCP
- UDP broadcasting is used for local peers discovery
- TCP is bound to a local THttpServer/THttpAsyncServer content delivery
- will maintain its own local folders of cached files, stored by hash
constructor Create(aSettings: THttpPeerCacheSettings; const aSharedSecret: RawByteString; aHttpServerClass: THttpServerSocketGenericClass = nil; aHttpServerThreadCount: integer = 2; aLogClass: TSynLogClass = nil; const aUuid: RawUtf8 = ''); reintroduce;
Initialize this peer-to-peer cache instance
- any supplied aSettings should be owned by the caller (e.g from a main settings class instance)
- aSharedSecret is used to cipher and authenticate each UDP frame between all peer nodes, and also generate HTTP authentication bearers
- if aSettings = nil, default values will be used by this instance
- you can supply THttpAsyncServer class to replace default THttpServer
- may raise some exceptions if the HTTP server cannot be started
destructor Destroy; override;
Finalize this peer-to-peer cache instance
function OnBeforeBody(var aUrl, aMethod, aInHeaders, aInContentType, aRemoteIP, aBearerToken: RawUtf8; aContentLength: Int64; aFlags: THttpServerRequestFlags): cardinal; virtual;
Method called by the HttpServer before any request is processed
- will reject anything but a GET with a proper bearer, from the right IP
function OnDownload(Sender: THttpClientSocket; var Params: THttpClientSocketWGet; const Url: RawUtf8; ExpectedFullSize: Int64; OutStream: TStreamRedirect): integer; virtual;
IWGetAlternate main processing method, as used by THttpClientSocketWGet
- will transfer Sender.Server/Port/RangeStart/RangeEnd into OutStream
- OutStream.LimitPerSecond will be overriden during the call
- could return 0 to fallback to a regular GET (e.g. not cached)
function OnDownloading(const Params: THttpClientSocketWGet; const Partial: TFileName; ExpectedFullSize: Int64): THttpPartialID;
IWGetAlternate main processing method, as used by THttpClientSocketWGet
- make this .part file available as pcfResponsePartial
- returns PartialID > 0 sequence
function OnRequest(Ctxt: THttpServerRequestAbstract): cardinal; virtual;
Method called by the HttpServer to process a request
- statically serve a local file from decoded bearer hash
function Ping: THttpPeerCacheMessageDynArray;
Broadcast a pcfPing on the network interface and return the responses
procedure OnDowloaded(var Params: THttpClientSocketWGet; const Partial: TFileName; PartialID: integer); virtual;
IWGetAlternate main processing method, as used by THttpClientSocketWGet
- if a file has been downloaded from the main repository, this method should be called to copy the content into this instance files cache
procedure OnDownloadFailed(const Params: THttpClientSocketWGet);
IWGetAlternate main processing method, as used by THttpClientSocketWGet
- OnDownload() may have returned corrupted data: local cache file is likely to be deleted, for safety
procedure OnDownloadingFailed(ID: THttpPartialID);
IWGetAlternate main processing method, as used by THttpClientSocketWGet notify the alternate download implementation that OnDownloading() failed
- e.g. THttpPeerCache is likely to abort publishing this partial file
procedure OnIdle(tix64: Int64);
Is called on a regular basis for background regular process
- is called from THttpPeerCacheThread.OnIdle
- e.g. to implement optional CacheTempMaxMin disk space release, actually reading and purging the CacheTempPath folder every minute
- could call Instable.DoRotate every minute to refresh IP banishments
property HttpServer: THttpServerGeneric read fHttpServer;
The associated HTTP/HTTPS server delivering cached context
property Instable: THttpAcceptBan read fInstable;
The current state of banned IP from incorrect UDP/HTTP requests
- follow RejectInstablePeersMin settings
property Mac: TMacAddress read fMac;
The network interface used for UDP and TCP process
property NetworkBroadcast: RawUtf8 read fMac.Broadcast;
The IP used for UDP and TCP process broadcast
property NetworkInterface: RawUtf8 read fMac.Name;
Which network interface is used for UDP and TCP process
property Settings: THttpPeerCacheSettings read fSettings;
Define how this instance handles its process
property Uuid: TGuid read fUuid write fUuid;
The UUID used to identify this node
- is filled by GetComputerUuid() from SMBios by default
- could be customized if necessary after Create()
THttpPeerCacheProcess = class(TSynPersistent)
One THttpPeerCache.OnDownload instance
THttpApiServer = class(THttpServerGeneric)
HTTP server using fast http.sys kernel-mode server
- The HTTP Server API enables applications to communicate over HTTP without using Microsoft Internet Information Server (IIS). Applications can register to receive HTTP requests for particular URLs, receive HTTP requests, and send HTTP responses. The HTTP Server API includes TLS support so that applications can exchange data over secure HTTP connections without IIS. It is also designed to work with I/O completion ports.
- The HTTP Server API is supported on Windows Server 2003 operating systems and on Windows XP with Service Pack 2 (SP2). Be aware that Microsoft IIS 5 running on Windows XP with SP2 is not able to share port 80 with other HTTP applications running simultaneously.
constructor Create(QueueName: SynUnicode = ''; const OnStart: TOnNotifyThread = nil; const OnStop: TOnNotifyThread = nil; const ProcessName: RawUtf8 = ''; ProcessOptions: THttpServerOptions = []); reintroduce;
Initialize the HTTP Service
- will raise an exception if http.sys is not available e.g. before Windows XP SP2) or if the request queue creation failed
- if you override this contructor, put the AddUrl() methods within, and you can set CreateSuspended to FALSE
- if you will call AddUrl() methods later, set CreateSuspended to TRUE, then call explicitly the Resume method, after all AddUrl() calls, in order to start the server
constructor CreateClone(From: THttpApiServer); virtual;
Create a HTTP/1.1 processing clone from the main thread
- do not use directly - is called during thread pool creation
destructor Destroy; override;
Release all associated memory and handles
function AddUrl(const aRoot, aPort: RawUtf8; Https: boolean = false; const aDomainName: RawUtf8 = '*'; aRegisterUri: boolean = false; aContext: Int64 = 0): integer;
Register the URLs to Listen On
- e.g. AddUrl('root','888')
- aDomainName could be either a fully qualified case-insensitive domain name, an IPv4 or IPv6 literal string, or a wildcard ('+' will bound to all domain names for the specified port, '*' will accept the request when no other listening hostnames match the request for that port)
- return 0 (NO_ERROR) on success, an error code if failed: under Vista and Seven, you could have ERROR_ACCESS_DENIED if the process is not running with enough rights (by default, UAC requires administrator rights for adding an URL to http.sys registration list) - solution is to call the THttpApiServer.AddUrlAuthorize class method during program setup
- if this method is not used within an overridden constructor, default Create must have be called with CreateSuspended = TRUE and then call the Resume method after all Url have been added
- if aRegisterUri is TRUE, the URI will be registered (need adminitrator rights) - default is FALSE, as defined by Windows security policy
class function AddUrlAuthorize(const aRoot, aPort: RawUtf8; Https: boolean = false; const aDomainName: RawUtf8 = '*'; OnlyDelete: boolean = false): string;
Will authorize a specified URL prefix
- will allow to call AddUrl() later for any user on the computer
- if aRoot is left '', it will authorize any root for this port
- must be called with Administrator rights: this class function is to be used in a Setup program for instance, especially under Vista or Seven, to reserve the Url for the server
- add a new record to the http.sys URL reservation store
- return '' on success, an error message otherwise
- will first delete any matching rule for this URL prefix
- if OnlyDelete is true, will delete but won't add the new authorization; in this case, any error message at deletion will be returned
function HasApi2: boolean;
Can be used to check if the HTTP API 2.0 is available
function RemoveUrl(const aRoot, aPort: RawUtf8; Https: boolean = false; const aDomainName: RawUtf8 = '*'): integer;
Un-register the URLs to Listen On
- this method expect the same parameters as specified to AddUrl()
- return 0 (NO_ERROR) on success, an error code if failed (e.g.
-1 if the corresponding parameters do not match any previous AddUrl)
procedure Clone(ChildThreadCount: integer);
Will clone this thread into multiple other threads
- could speed up the process on multi-core CPU
- will work only if the OnProcess property was set (this is the case e.g. in TRestHttpServer.Create() constructor)
- maximum value is 256 - higher should not be worth it
procedure LogStart(const aLogFolder: TFileName; aType: THttpApiLoggingType = hltW3C; const aSoftwareName: TFileName = ''; aRolloverType: THttpApiLoggingRollOver = hlrDaily; aRolloverSize: cardinal = 0; aLogFields: THttpApiLogFields = [hlfDate..hlfSubStatus]; aFlags: THttpApiLoggingFlags = [hlfUseUtf8Conversion]);
Enable HTTP API 2.0 logging
- will raise an EHttpApiServer exception if the old HTTP API 1.x is used so you should better test the availability of the method first:
if aServer.HasApi2 then LogStart(....);
- this method won't do anything on the cloned instances, but the main instance logging state will be replicated to all cloned instances
- you can select the output folder and the expected logging layout
- aSoftwareName will set the optional W3C-only software name string
- aRolloverSize will be used only when aRolloverType is hlrSize
procedure LogStop;
Disable HTTP API 2.0 logging
- this method won't do anything on the cloned instances, but the main instance logging state will be replicated to all cloned instances
procedure RegisterCompress(aFunction: THttpSocketCompress; aCompressMinSize: integer = 1024; aPriority: integer = 10); override;
Will register a compression algorithm
- overridden method which will handle any cloned instances
procedure SetAuthenticationSchemes(schemes: THttpApiRequestAuthentications; const DomainName: SynUnicode = ''; const Realm: SynUnicode = '');
Enable HTTP API 2.0 server-side authentication
- once enabled, the client sends an unauthenticated request: it is up to the server application to generate the initial 401 challenge with proper WWW-Authenticate headers; any further authentication steps will be perform in kernel mode, until the authentication handshake is finalized; later on, the application can check the AuthenticationStatus property of THttpServerRequest and its associated AuthenticatedUser value see https://msdn.microsoft.com/en-us/library/windows/desktop/aa364452
- will raise an EHttpApiServer exception if the old HTTP API 1.x is used so you should better test the availability of the method first:
if aServer.HasApi2 then SetAuthenticationSchemes(....);
- this method will work on the current group, for all instances
- see HTTPAPI_AUTH_ENABLE_ALL constant to set all available schemes
- optional Realm parameters can be used when haBasic scheme is defined
- optional DomainName and Realm parameters can be used for haDigest
procedure SetTimeOutLimits(aEntityBody, aDrainEntityBody, aRequestQueue, aIdleConnection, aHeaderWait, aMinSendRate: cardinal);
Enable HTTP API 2.0 advanced timeout settings
- all those settings are set for the current URL group
- will raise an EHttpApiServer exception if the old HTTP API 1.x is used so you should better test the availability of the method first:
if aServer.HasApi2 then SetTimeOutLimits(....);
- aEntityBody is the time, in seconds, allowed for the request entity body to arrive - default value is 2 minutes
- aDrainEntityBody is the time, in seconds, allowed for the HTTP Server API to drain the entity body on a Keep-Alive connection - default value is 2 minutes
- aRequestQueue is the time, in seconds, allowed for the request to remain in the request queue before the application picks it up - default value is 2 minutes
- aIdleConnection is the time, in seconds, allowed for an idle connection; is similar to THttpServer.ServerKeepAliveTimeOut - default value is 2 minutes
- aHeaderWait is the time, in seconds, allowed for the HTTP Server API to parse the request header - default value is 2 minutes
- aMinSendRate is the minimum send rate, in bytes-per-second, for the response - default value is 150 bytes-per-second
- any value set to 0 will set the HTTP Server API default value
property AuthenticationSchemes: THttpApiRequestAuthentications read fAuthenticationSchemes;
Read-only access to HTTP API 2.0 server-side enabled authentication schemes
property Cloned: boolean read GetCloned;
TRUE if this instance is in fact a cloned instance for the thread pool
property Clones: THttpApiServers read fClones;
Access to the internal THttpApiServer list cloned by this main instance
- as created by Clone() method
property Logging: boolean read GetLogging;
Read-only access to check if the HTTP API 2.0 logging is enabled
- use LogStart/LogStop methods to change this property value
property LoggingServiceName: RawUtf8 read fLoggingServiceName write SetLoggingServiceName;
The current HTTP API 2.0 logging Service name
- should be UTF-8 encoded, if LogStart(aFlags=[hlfUseUtf8Conversion])
- this value is dedicated to one instance, so the main instance won't propagate the change to all cloned instances
property MaxBandwidth: cardinal read GetMaxBandwidth write SetMaxBandwidth;
The maximum allowed bandwidth rate in bytes per second (via HTTP API 2.0)
- Setting this value to 0 allows an unlimited bandwidth
- by default Windows not limit bandwidth (actually limited to 4 Gbit/sec).
- will return 0 if the system does not support HTTP API 2.0 (i.e. under Windows XP or Server 2003)
property MaxConnections: cardinal read GetMaxConnections write SetMaxConnections;
The maximum number of HTTP connections allowed (via HTTP API 2.0)
- Setting this value to 0 allows an unlimited number of connections
- by default Windows does not limit number of allowed connections
- will return 0 if the system does not support HTTP API 2.0 (i.e. under Windows XP or Server 2003)
property ReceiveBufferSize: cardinal read fReceiveBufferSize write SetReceiveBufferSize;
How many bytes are retrieved in a single call to ReceiveRequestEntityBody
- set by default to 1048576, i.e. 1 MB - practical limit is around 20 MB
- you may customize this value if you encounter HTTP error HTTP_NOTACCEPTABLE (406) from client, corresponding to an ERROR_NO_SYSTEM_RESOURCES (1450) exception on server side, when uploading huge data content
property RegisteredUrl: SynUnicode read GetRegisteredUrl;
Return the list of registered URL on this server instance
property ServerSessionID: HTTP_SERVER_SESSION_ID read fServerSessionID;
Read-only access to the low-level HTTP API 2.0 Session ID
property UrlGroupID: HTTP_URL_GROUP_ID read fUrlGroupID;
Read-only access to the low-level HTTP API 2.0 URI Group ID
THttpApiWebSocketConnection = object(TObject)
Structure representing a single WebSocket connection
procedure Close(aStatus: WEB_SOCKET_CLOSE_STATUS; aBuffer: pointer; aBufferSize: ULONG);
Close connection
procedure Send(aBufferType: WEB_SOCKET_BUFFER_TYPE; aBuffer: pointer; aBufferSize: ULONG);
Send data to client
property Index: integer read fIndex;
Index of connection in protocol's connection list
property PrivateData: pointer read fPrivateData write fPrivateData;
Custom user data
property Protocol: THttpApiWebSocketServerProtocol read fProtocol;
Protocol of connection
property State: TWebSocketState read fState;
Access to the current state of this connection
THttpApiWebSocketServerProtocol = class(TObject)
Protocol Handler of websocket endpoints events
- maintains a list of all WebSockets clients for a given protocol
constructor Create(const aName: RawUtf8; aManualFragmentManagement: boolean; aServer: THttpApiWebSocketServer; const aOnAccept: TOnHttpApiWebSocketServerAcceptEvent; const aOnMessage: TOnHttpApiWebSocketServerMessageEvent; const aOnConnect: TOnHttpApiWebSocketServerConnectEvent; const aOnDisconnect: TOnHttpApiWebSocketServerDisconnectEvent; const aOnFragment: TOnHttpApiWebSocketServerMessageEvent = nil);
Initialize the WebSockets process
- if aManualFragmentManagement is true, onMessage will appear only for whole received messages, otherwise OnFragment handler must be passed (for video broadcast, for example)
destructor Destroy; override;
Finalize the process
function Broadcast(aBufferType: ULONG; aBuffer: pointer; aBufferSize: ULONG): boolean;
Send message to all connections of this protocol
function Close(index: integer; aStatus: WEB_SOCKET_CLOSE_STATUS; aBuffer: pointer; aBufferSize: ULONG): boolean;
Close WebSocket connection identified by its index
function Send(index: integer; aBufferType: ULONG; aBuffer: pointer; aBufferSize: ULONG): boolean;
Send message to the WebSocket connection identified by its index
property Index: integer read fIndex;
Identify the endpoint instance
property ManualFragmentManagement: boolean read fManualFragmentManagement;
OnFragment event will be called for each fragment
property Name: RawUtf8 read fName;
Text identifier
property OnAccept: TOnHttpApiWebSocketServerAcceptEvent read fOnAccept;
Event triggerred when a WebSockets client is initiated
property OnConnect: TOnHttpApiWebSocketServerConnectEvent read fOnConnect;
Event triggerred when a WebSockets client is connected
property OnDisconnect: TOnHttpApiWebSocketServerDisconnectEvent read fOnDisconnect;
Event triggerred when a WebSockets client is gracefully disconnected
property OnFragment: TOnHttpApiWebSocketServerMessageEvent read fOnFragment;
Event triggerred when a non complete frame is received
- required if ManualFragmentManagement is true
property OnMessage: TOnHttpApiWebSocketServerMessageEvent read fOnMessage;
Event triggerred when a WebSockets message is received
THttpApiWebSocketServer = class(THttpApiServer)
HTTP & WebSocket server using fast http.sys kernel-mode server
- can be used like simple THttpApiServer
- when AddUrlWebSocket is called WebSocket support are added in this case WebSocket will receiving the frames in asynchronous
constructor Create(aSocketThreadsCount: integer = 1; aPingTimeout: integer = 0; const QueueName: SynUnicode = ''; const aOnWSThreadStart: TOnNotifyThread = nil; const aOnWSThreadTerminate: TOnNotifyThread = nil; ProcessOptions: THttpServerOptions = []); reintroduce;
Initialize the HTTPAPI based Server with WebSocket support
- will raise an exception if http.sys or websocket.dll is not available (e.g. before Windows 8) or if the request queue creation failed
- for aPingTimeout explanation see PingTimeout property documentation
constructor CreateClone(From: THttpApiServer); override;
Create a WebSockets processing clone from the main thread
- do not use directly - is called during thread pool creation
function AddUrlWebSocket(const aRoot, aPort: RawUtf8; Https: boolean = false; const aDomainName: RawUtf8 = '*'; aRegisterUri: boolean = false): integer;
Register the URLs to Listen on using WebSocket
- aProtocols is an array of a recond with callbacks, server call during WebSocket activity
function Request(Ctxt: THttpServerRequestAbstract): cardinal; override;
procedure RegisterProtocol(const aName: RawUtf8; aManualFragmentManagement: boolean; const aOnAccept: TOnHttpApiWebSocketServerAcceptEvent; const aOnMessage: TOnHttpApiWebSocketServerMessageEvent; const aOnConnect: TOnHttpApiWebSocketServerConnectEvent; const aOnDisconnect: TOnHttpApiWebSocketServerDisconnectEvent; const aOnFragment: TOnHttpApiWebSocketServerMessageEvent = nil);
Prepare the process for a given THttpApiWebSocketServerProtocol
procedure SendServiceMessage;
Send a "service" message to a WebSocketServer to wake up a WebSocket thread
- can be called from any thread
- when a webSocket thread receives such a message it will call onServiceMessage in the thread context
property OnServiceMessage: TThreadMethod read fOnServiceMessage write fOnServiceMessage;
Event called when a service message is raised
property OnWSThreadStart: TOnNotifyThread read FOnWSThreadStart write SetOnWSThreadStart;
Event called when the processing thread starts
property OnWSThreadTerminate: TOnNotifyThread read FOnWSThreadTerminate write SetOnWSThreadTerminate;
Event called when the processing thread termintes
property PingTimeout: integer read fPingTimeout;
Ping timeout in seconds. 0 mean no ping.
- if connection not receive messages longer than this timeout TSynWebSocketGuard will send ping frame
- if connection not receive any messages longer than double of this timeout it will be closed
property Protocols[index: integer]: THttpApiWebSocketServerProtocol read GetProtocol;
Access to the associated endpoints
property ProtocolsCount: integer read getProtocolsCount;
Access to the associated endpoints count
TSynThreadPoolHttpApiWebSocketServer = class(TSynThreadPool)
A Thread Pool, used for fast handling of WebSocket requests
constructor Create(Server: THttpApiWebSocketServer; NumberOfThreads: integer = 1); reintroduce;
Initialize the thread pool
TSynWebSocketGuard = class(TThread)
Thread for closing deprecated WebSocket connections
- i.e. which have not responsed after PingTimeout interval
constructor Create(Server: THttpApiWebSocketServer); reintroduce;
Initialize the thread
PUdpFrame = ^TUdpFrame;
Pointer to a memory buffer of the maximum size of UDP frame
THttpPeerCacheMessageKind = ( pcfPing, pcfPong, pcfRequest, pcfResponseNone, pcfResponseOverloaded, pcfResponsePartial, pcfResponseFull, pcfBearer );
The content of a binary THttpPeerCacheMessage
- could eventually be extended in the future for frame versioning
THttpPeerCacheOption = ( pcoCacheTempSubFolders, pcoUseFirstResponse, pcoTryLastPeer, pcoBroadcastNotAlone, pcoNoServer, pcoNoBanIP, pcoSelfSignedHttps, pcoVerboseLog );
Each THttpPeerCacheSettings.Options item
- pcoCacheTempSubFolders will create 16 sub-folders (from first 0-9/a-z hash nibble) within CacheTempPath to reduce filesystem fragmentation
- pcoUseFirstResponse will accept the first positive response, and don't wait for the BroadcastTimeoutMS delay for all responses to be received
- pcoTryLastPeer will first check the latest peer with HTTP/TCP before making any broadcast - to be used if the files are likely to come in batch; can be forced by TWGetAlternateOptions from a given WGet() call
- pcoBroadcastNotAlone will disable broadcasting for up to one second if no response at all was received within BroadcastTimeoutMS delay
- pcoNoServer disable the local UDP/HTTP servers and acts as a pure client
- pcoNoBanIP disable the 4 seconds IP banishment mechanism at HTTP level; set RejectInstablePeersMin = 0 to disable banishment at UDP level
- pcoSelfSignedHttps enables HTTPS communication with a self-signed server (warning: this option should be set on all peers, clients and servers)
- pcoVerboseLog will log all details, e.g. raw UDP frames
THttpPeerCacheOptions = set of THttpPeerCacheOption;
THttpPeerCacheSettings.Options values
THttpServerExecuteState = ( esNotStarted, esBinding, esRunning, esFinished );
THttpServerSocketGeneric current state
THttpServerOption = ( hsoHeadersUnfiltered, hsoHeadersInterning, hsoNoXPoweredHeader, hsoNoStats, hsoCreateSuspended, hsoLogVerbose, hsoIncludeDateHeader, hsoEnableTls, hsoBan40xIP, hsoThreadCpuAffinity, hsoThreadSocketAffinity, hsoReusePort, hsoThreadSmooting, hsoEnablePipelining, hsoEnableLogging, hsoTelemetryCsv, hsoTelemetryJson );
Available HTTP server options
- some THttpServerGeneric classes may have only partial support of them
- hsoHeadersUnfiltered will store all headers, not only relevant (i.e. include raw Content-Length, Content-Type and Content-Encoding entries)
- hsoHeadersInterning triggers TRawUtf8Interning to reduce memory usage
- hsoNoStats will disable low-level statistic counters
- hsoNoXPoweredHeader excludes 'X-Powered-By: mORMot 2 synopse.info' header
- hsoCreateSuspended won't start the server thread immediately
- hsoLogVerbose could be used to debug a server in production
- hsoIncludeDateHeader will let all answers include a Date: ... HTTP header
- hsoEnableTls enables TLS support for THttpServer socket server, using Windows SChannel API or OpenSSL - call WaitStarted() to set the certificates
- hsoBan40xIP will reject any IP for a few seconds after a 4xx error code is returned (but 401/403) - only implemented by socket servers for now
- either hsoThreadCpuAffinity or hsoThreadSocketAffinity could be set: to force thread affinity to one CPU logic core, or CPU HW socket; see TNotifiedThread corresponding methods - not available on http.sys
- hsoReusePort will set SO_REUSEPORT on POSIX, allowing to bind several THttpServerGeneric on the same port, either within the same process, or as separated processes (e.g. to set process affinity to one CPU HW socket)
- hsoThreadSmooting will change the TAsyncConnections.ThreadPollingWakeup() algorithm to focus the process on the first threads of the pool - by design, this will disable both hsoThreadCpuAffinity and hsoThreadSocketAffinity
- hsoEnablePipelining enable HTTP pipelining (unsafe) on THttpAsyncServer
- hsoEnableLogging enable an associated THttpServerGeneric.Logger instance
- hsoTelemetryCsv and hsoTelemetryJson will enable CSV or JSON consolidated per-minute metrics logging via an associated THttpServerGeneric.Analyzer
THttpServerOptions = set of THttpServerOption;
How a THttpServerGeneric class is expected to process incoming requests
THttpServerRequestClass = class of THttpServerRequest;
Meta-class of HTTP server requests instances
THttpServerRespClass = class of THttpServerResp;
Metaclass of HTTP response Thread
THttpServerSocketClass = class of THttpServerSocket;
Meta-class of the THttpServerSocket process
- used to override THttpServerSocket.GetRequest for instance
THttpServerSocketGenericClass = class of THttpServerSocketGeneric;
Meta-class of our THttpServerSocketGeneric classes
- typically implemented by THttpServer, TWebSocketServer, TWebSocketServerRest or THttpAsyncServer classes
THttpServerSocketGetRequestResult = ( grClosed, grException, grOversizedPayload, grRejected, grIntercepted, grTimeout, grHeaderReceived, grBodyReceived, grWwwAuthenticate, grUpgraded, grBanned );
Results of THttpServerSocket.GetRequest virtual method
- grClosed is returned if the socket was disconnected/closed by the client
- grException is returned if any exception occurred during the process
- grOversizedPayload is returned when MaximumAllowedContentLength is reached
- grRejected on invalid input, or when OnBeforeBody returned not 200
- grIntercepted is returned e.g. from OnHeaderParsed as valid result
- grTimeout is returned when HeaderRetrieveAbortDelay is reached
- grHeaderReceived is returned for GetRequest({withbody=}false)
- grBodyReceived is returned for GetRequest({withbody=}true)
- grWwwAuthenticate is returned if GetRequest() did send a 401 response
- grUpgraded indicates that this connection was upgraded e.g. as WebSockets
- grBanned is triggered by the hsoBan40xIP option
TMacAddressFilter = set of ( mafEthernetOnly, mafLocalOnly, mafRequireBroadcast, mafIgnoreGateway, mafIgnoreKind, mafIgnoreSpeed);
Define how GetMacAddress() makes its sorting choices
- used e.g. for THttpPeerCacheSettings.InterfaceFilter property
- mafEthernetOnly will only select TMacAddress.Kind = makEthernet
- mafLocalOnly will only select makEthernet or makWifi adapters
- mafRequireBroadcast won't return any TMacAddress with Broadcast = ''
- mafIgnoreGateway won't put the TMacAddress.Gateway <> '' first
- mafIgnoreKind and mafIgnoreSpeed will ignore Kind or Speed properties
TOnHttpApiWebSocketServerAcceptEvent = function(Ctxt: THttpServerRequest; var Conn: THttpApiWebSocketConnection): boolean of object;
Event handler on THttpApiWebSocketServerProtocol Accepted connection
TOnHttpApiWebSocketServerConnectEvent = procedure( var Conn: THttpApiWebSocketConnection) of object;
Event handler on THttpApiWebSocketServerProtocol connection
TOnHttpApiWebSocketServerDisconnectEvent = procedure(var Conn: THttpApiWebSocketConnection; aStatus: WEB_SOCKET_CLOSE_STATUS; aBuffer: pointer; aBufferSize: ULONG) of object;
Event handler on THttpApiWebSocketServerProtocol disconnection
TOnHttpApiWebSocketServerMessageEvent = procedure(var Conn: THttpApiWebSocketConnection; aBufferType: WEB_SOCKET_BUFFER_TYPE; aBuffer: pointer; aBufferSize: ULONG) of object;
Event handler on THttpApiWebSocketServerProtocol Message received
TOnHttpServerBasicAuth = function(Sender: TObject; const aUser: RawUtf8; const aPassword: SpiUtf8): boolean of object;
Callback used by THttpServerSocketGeneric.SetAuthorizeBasic
- should return true if supplied aUser/aPassword pair is valid
TOnHttpServerHeaderParsed = function( ClientSock: THttpServerSocket): THttpServerSocketGetRequestResult of object;
Called from THttpServerSocket.GetRequest before OnBeforeBody
- this THttpServer-specific callback allow quick and dirty action on the raw socket, to bypass the whole THttpServer.Process high-level action
- should return grRejected/grIntercepted if the action has been handled as error or success, and response has been sent directly via ClientSock.SockSend/SockSendFlush (as HTTP/1.0) by this handler
- should return grHeaderReceived to continue as usual with THttpServer.Process
TOnHttpServerRequestAsyncResponse = procedure(Sender: THttpServerRequestAbstract; RespStatus: integer = HTTP_SUCCESS) of object;
Event signature for THttpServerRequest.AsyncResponse callback
TUdpFrame = array[word] of byte;
Work memory buffer of the maximum size of UDP frame (64KB)
TUriRouterMethod = ( urmGet, urmPost, urmPut, urmDelete, urmOptions, urmHead );
One HTTP method supported by TUriRouter
- only supports RESTful GET/POST/PUT/DELETE/OPTIONS/HEAD by default
- each method would have its dedicated TUriTree parser in TUriRouter
TUriRouterMethods = set of TUriRouterMethod;
The HTTP methods supported by TUriRouter
TUriRouterTree = array[urmGet .. high(TUriRouterMethod)] of TUriTree;
Store per-method URI multiplexing Radix Tree in TUriRouter
- each HTTP method would have its dedicated TUriTree parser in TUriRouter
TWebSocketState = ( wsConnecting, wsOpen, wsClosing, wsClosedByClient, wsClosedByServer, wsClosedByGuard, wsClosedByShutdown );
Current state of a THttpApiWebSocketConnection
HTTP_10_FLAGS: array[boolean] of THttpServerRequestFlags = ( [], [hsrHttp10]);
Used to compute the request ConnectionFlags from HTTP/1.0 command
HTTP_TLS_FLAGS: array[boolean] of THttpServerRequestFlags = ( [], [hsrHttps, hsrSecured]);
Used to compute the request ConnectionFlags from the socket TLS state
HTTP_UPG_FLAGS: array[boolean] of THttpServerRequestFlags = ( [], [hsrConnectionUpgrade]);
Used to compute the request ConnectionFlags from connection: upgrade header
THREADPOOL_BIGBODYSIZE = 16 * 1024 * 1024;
If HTTP body length is bigger than 16 MB, creates a dedicated THttpServerResp
- is the default value to TSynThreadPoolTHttpServer.BigBodySize
THREADPOOL_MAXWORKTHREADS = 512;
Kept-alive or big HTTP requests will create a dedicated THttpServerResp
- each thread reserves 2 MB of memory so it may break the server
- keep the value to a decent number, to let resources be constrained up to 1GB
- is the default value to TSynThreadPoolTHttpServer.MaxBodyThreadCount
UDP_SHUTDOWN: RawUtf8 = 'shutdown';
The UDP frame content as sent by TUdpServerThread.Destroy
URIROUTERMETHOD: array[TUriRouterMethod] of RawUtf8 = ( 'GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD');
Convert TUriRouterMethod into its standard HTTP text
- see UriMethod() function for the reverse conversion
| Functions or procedures | Description | |
|---|---|---|
| FavIconBinary | Used by THttpServerGeneric.SetFavIcon to return a nice /favicon.ico | |
| GetMainMacAddress | Get a network interface from its TMacAddress main fields | |
| GetMainMacAddress | Pickup the most suitable network according to some preferences | |
| InitNetTlsContextSelfSignedServer | Initialize a server-side TLS structure with a self-signed algorithm | |
| IsValidUriRoute | Check if the supplied text contains only valid characters for a root URI | |
| PrivKeyCertPfx | Some pre-computed CryptCertOpenSsl[caaRS256].New key for Windows | |
| UriMethod | UrmGet urmPost urmPut urmDelete urmOptions urmHead quickly recognize most HTTP text methods into a TUriRouterMethod enumeration |
function FavIconBinary: RawByteString;
Used by THttpServerGeneric.SetFavIcon to return a nice /favicon.ico
function GetMainMacAddress(out Mac: TMacAddress; Filter: TMacAddressFilter = []): boolean; overload;
Pickup the most suitable network according to some preferences
- will sort GetMacAddresses() results according to its Kind and Speed to select the most suitable local interface e.g. for THttpPeerCache
function GetMainMacAddress(out Mac: TMacAddress; const InterfaceNameAddressOrIP: RawUtf8; UpAndDown: boolean = false): boolean; overload;
Get a network interface from its TMacAddress main fields
- search is case insensitive for TMacAddress.Name and Address fields or as exact IP, and eventually as IP bitmask pattern (e.g. 192.168.1.255)
procedure InitNetTlsContextSelfSignedServer(var TLS: TNetTlsContext; Algo: TCryptAsymAlgo = caaRS256; UsePreComputed: boolean = false);
Initialize a server-side TLS structure with a self-signed algorithm
- as used e.g. by THttpServerSocketGeneric.WaitStartedHttps
- if OpenSSL is available and UsePreComputed is false, will generate a temporary pair of key files via Generate(CU_TLS_SERVER, '127.0.0.1', nil, 3650) with a random password
- if UsePreComputed=true or on pure SChannel, will use the PrivKeyCertPfx pre-computed constant
- you should eventually call DeleteFile(Utf8ToString(TLS.CertificateFile)) and DeleteFile(Utf8ToString(TLS.PrivateKeyFile)) to delete the two temp files
function IsValidUriRoute(p: PUtf8Char): boolean;
Check if the supplied text contains only valid characters for a root URI
- excluding the parameters, i.e. rejecting the ? and % characters
- but allowing <param> place holders as recognized by TUriRouter
function PrivKeyCertPfx: RawByteString;
Some pre-computed CryptCertOpenSsl[caaRS256].New key for Windows
- the associated password is 'pass'
- as used e.g. by THttpServerSocketGeneric.WaitStartedHttps
function UriMethod(const Text: RawUtf8; out Method: TUriRouterMethod): boolean;
UrmGet urmPost urmPut urmDelete urmOptions urmHead quickly recognize most HTTP text methods into a TUriRouterMethod enumeration
- may replace cascaded IsGet() IsPut() IsPost() IsDelete() function calls
- see URIROUTERMETHOD[] constant for the reverse conversion