Http Server
Sparkle provides classes for you to build an Http Server to process and respond to http requests. In this chapter we will explain how these classes work and how you can build your Http Server using Sparkle.
Overview
Sparkle Http Server provides three concepts, a server, a dispatcher and a server module (or simply "module").
The server is higher-level object that handles the communication process to you. Currently you can have five different server types: Http.Sys-based server (recommended for Windows), Socket server (recommended for Linux, also available on Windows), Apache-based server, Indy-based server and in-process server.
Each server is different from each other, but all them have a common object: the HTTP Dispatcher. The dispatcher is the object that holds information about all server modules and handles the requests.
Finally a module is a logical part of the whole server architecture that you register into the dispatcher to receive requests for a particular URI address. Each module has an URI associated with it, and the dispatcher is responsible to redirect the request to the proper module, based on the requested URI. A module receives requests for the URI he's associated with, and also for all other URI which parent is the associated URI.
For example, suppose you have registered two modules 1 and 2, and their associated URIs are "http://server/base/url1" and "http://server/base/url2", respectively.
Module 1 will receive requests and provide responses for URIs like
http://server/base/url1/
http://server/base/url1/test/
http://server/base/url1?q=1
While Module 2 will receive requests and provide responses for URIs like
http://server/base/url2/
http://server/base/url2/page.htm
http://server/base/url2/page2.html\#43
Note that the dispatcher will forward the request to the proper module based on the base URI, but then the module must be responsible to parse the URI to provide the correct response.
Wizard For New Sparkle Server
You can use the "New TMS Sparkle Server" wizard to create a new server with a few clicks. The wizard will only create Http.sys-based server. You can also freely create the server manually, the wizard is not mandatory and it's just a way to get started quickly. For Apache-based and in-process server, no wizard is available and you have to create it manually.
To create a new Sparkle Server:
chose File > New > Other and then look for "TMS Business" category under "Delphi Projects". Then double click "TMS Sparkle Server".
Chose the kind of applications you want to server to run on, then click Next.
Available options are VCL, FMX, Service and Console. You can choose multiple ones (for example, you can have a VCL project for quick test the server and a Service one to later install it in production, both sharing common code.Chose the Host Name, Port and Path for the server, then click Create.
Usually you don't need to change the host name, for more info check URL namespace and reservation. Port and Path form the rest of base URL where your server will be reached.
The new server will be create and ready to run. The Sparkle Server template just responds any request with a "Hello, World" message.
Using Design-Time Components
Another way to use TMS Sparkle is by using the design-time components. If you want the RAD, component dropping approach, this is the way.
There is a full chapter about the design-time components, but here is a short summary about how to use it:
Drop a dispatcher component in the form (for example, TSparkeHttpSysDispatcher);
Drop a server component in the form (TSparkleStaticServer, TXDataServer, etc.);
Associated the server to the dispatcher through the Dispatcher property of server component;
Specify the BaseUrl property of the server (for example, http://+:2001/tms/myserver);
Configure any specific property of the server;
Set Active property of dispatcher component to true.
Http.Sys-based Server
For servers running on Windows platform, Sparkle provides an HTTP server based on Microsoft HTTP Server API, which is an interface for the HTTP protocol stack (HTTP.sys). This makes Sparkle HTTP Server a full-featured, robust and fast server on Microsoft Windows. Many operations like routing HTTP requests, caching of responses, encryption and authentication runs in kernel-mode. It's the same stack used by Microsoft IIS.
It plays the role of the "HTTP Server" as described in the overview of Sparkle server architecture. It's a Delphi class that is a layer over the Microsoft API, receive the requests from operational system, and dispatch the requests to the proper server modules.
The following code illustrates how to create the server, add modules and start it.
uses {...}, Sparkle.HttpSys.Server;
var
Server: THttpSysServer;
begin
Server := THttpSysServer.Create;
Server.Dispatcher.AddModule(TMyServerModule.Create('http://host:2001/myapplication');
Server.Dispatcher.AddModule(TAnotherModule.Create('http://+:2001/anotherapp');
Server.Start;
// server runs in console mode until user hits Return
ReadLn;
Server.Stop;
end;
In the example above, any request to URIs beginning with "http://server.com/myapplication" will be dispatched to TMyServerModule. And any requests to address "http://host:2001/" will be dispatched to TAnotherModule.
By default, Sparkle will strip the host part of the url and replace with a + symbol. So even in the first example with "host" as the server, internally it will change to +:2001. This is desired for most cases. If you want Sparkle to keep the host name (for example, if you are listening to different IP address/host names in the same server, then you can set KeepHostInUrlPrefixes property to true:
Server.KeepHostInUrlPrefixes := True;
If you are using the TSparkleHttpSysDispatcher component, you can set its TSparkleHttpSysDispatcher.KeepHostInUrlPrefixes published property instead, either at design time or at runtime. It sets the property of the underlying server automatically.
Since THttpSysServer is based on the http.sys stack, the URI you want to listen (like the "http://+:2001/" in example) must be reserved in the Windows. Otherwise your application won't be able to respond to such requests. This is not Sparkle-related, but all requirements on the http.sys itself. There is plenty documentation over the internet about how to reserve and configure many details of the server, like configuring server-side certificates, for example. However, Sparkle also provides helpful tools for you to accomplish these tasks. The following topics will try to summarize the basic steps you need to perform such operations and how Sparkle can help you out with it.
URL namespace and reservation
Sparkle THttySysServer is based on kernel-mode http.sys which means all http requests are processed by the operational system. In this architecture, the kernel forwards the http requests based on the requested url. For this to work you need to first reserve an Url namespace. Namespace reservation assigns the rights for a portion of the HTTP URL namespace to a particular group of users. A reservation gives those users the right to create services that listen on that portion of the namespace.
So, if all your servers will run under the address http://server:2001/tms/, you can reserve that namespace to make sure Windows will listen HTTP requests to those addresses instead of refusing them.
To reserve an Url namespace, you can either:
TMSHttpConfig tool (to easily configure using GUI);
THttpSysServerConfig class (to configure from Delphi code);
Windows netsh command-line tool (to learn how to configure with Windows itself without using Sparkle).
The Url reservation just need to be done one time for the machine you are going to run the server. If you try a second time, an error will occur.
Using HTTP secure (HTTPS)
You can configure your server to work with HTTP secure. To do this, you need to previously bind an existing certificate to the port you are going to use for the HTTPS connection. This way, when the http.sys server receives an HTTPS request to a specified port, it will know which server certificate to send to the client.
Just as with URL reservation, there are several ways you can bind a certificate to a port:
TMSHttpConfig tool (to easily configure using GUI);
THttpSysServerConfig class (to configure from Delphi code);
Windows netsh command-line tool (to learn how to configure with Windows itself without using Sparkle).
One you have bound the certificate to the port in server, using HTTPS is pretty straightforward with Sparkle. You don't need any extra SSL libraries to be installed/deployed either in client or server. All SSL communication is done native by the underlying operational system.
Use "https" prefix in Delphi code
Once you have registered your certificate with the command above, your server is configured to use secure connections. Please note that you still need to reserve the url for the connection, and the url must begin with "https" (for example, you might want to reserve the url namespace "https://+:2002/tms/business".
When registering modules in the HTTP server, all you need to do is provide the correct base URI that matches the reserved namespace. Don't forget that you must prefix the URI with "https".
Using a certificate for testing
If you don't have a certificate, you can still generate a self-signed certificate for testing purposes. For that you will need makecert.exe tool, which is available when you install either Microsoft Visual Studio or Windows SDK. Generating a self-signed certificate is out of scope of this documentation, but the following links might help in doing such task. Once you have generated and installed a self-signed certificate, the process for using it is the same as described previously, all you need is bind the certificate to the HTTPS port using the certificate thumbprint.
How to: Create Temporary Certificates for Use During Development
Note
Self-signed certificates are only for testing/development purposes. For production environment, use a certificate signed by a certificate authority.
TMSHttpConfig Tool
Using the TMSHttpConfig tool is the easiest way to configure http.sys on your server. TMS Sparkle distribution includes a binary executable of TMSHttpConfig which runs stand-alone and don't need to be installed nor needs any extra files to be executed. Just run TMSHttpConfig.exe in the computer where your server will run and use it. Also full source code of this tool is avaliable in the demos distribution of TMS Sparkle.
URL Reservation
TMSHttpConfig shows you all the existing http.sys URL reservations in the "Url Acl" tab. You can browse them and if you select a row in list, it will show you to which accounts the URL is reserved to (use is permitted).
You can add a new URL reservation, or remove an existing one by using the Add and Remove buttons. To add a new reservation, you just need to type the URL prefix according to Microsoft rules. Usually just use the format "http://+:<port>/<basepath>". You must also choose to which Windows accounts the reservation will be added to. TMSHttpConfig predefines your account (for testing purposes and running stand-alone servers) and Network Service (to run from Windows services).
Server Certificate Configuration
It's also very easy to bind a server certificate to a port to use HTTPS with Sparkle servers. First, you must be sure your certificate is already installed/imported in the Windows Certificate Store. Also note that you must install it to Local Machine store, not the Current User. For more information about how to to this, follow this link. Usually your certificate provider will give you detailed instructions about how to do this.
For a review, TMSHttpConfig shows you all existing port-certificate binds in the "SSL" tab. If you select a row, it will show you a summary about the binding below the list. You can see information about the certificate bound to the port, and the app id used for the binding.
To add or remove a binding use "Add" and "Remove" buttons. When you click "Add", the following screen appears.
IP Address: You should just leave it with default "0.0.0.0" value. In the case you rarely need to bind the certificate to a specific IP only, just type the IP. The default value will work for all IP addresses.
Port: Type the port where the certificate will be bound to.
App ID: This field is just for information and is not needed for the server to function. It must be a GUID (enclosed by brackets) and you can just leave the default empty GUID provided by TMSHttpConfig.
Cert Store: Indicates which certificate store you will use to retrieve the certificate. Also, the default is "My" (Personal store) and that's where your certificate probably is so it's also unlikely you will need to change this field.
Cert Hash: This field should contain the thumbprint (hash) of the certificate to be bound to the part. You could just type it here, but it's way easier to click "Select Certificate" button to do that. When you click that button, TMSHttpConfig will show you a list of all available certificates in the chosen certificate store:
Just select your certificate, click "Ok", and the thumbprint will be filled automatically for you.
Once all fields are correct, just click "Ok" and the binding is done. Your server is now responding to HTTPS requests in the specified port using the specified certificate.
For later review, if you can select a binding in the list and click "View Certificate" to see more detailed information about the certificate bound to the port.
THttpSysServerConfig Class
To perform URL reservation and configure server certificate to use HTTPS, easier way is use the TMSHttpConfig tool. But it might be possible that you want to automate this registration procedures, or build your own tool to do that using Delphi. In this case, Sparkle provides the THttpSysServerConfig class (declared in unit Sparkle.HttpSys.Config) that allows you to do all those tasks from Delphi code. Just remember that your application must be running with administrative rights.
All the following examples assume you have a Config variable with an existing object instance of a THttpSysServerConfig class:
uses {...}, Sparkle.HttpSys.Config;
var
Config: THttpSysServerConfig;
begin
Config := THttpSysServerConfig.Create;
try
// Do your config code here using Config instance
finally
Config.Free;
end;
end;
URL Reservation
To reserve an URL from Delphi code using TMS Sparkle classes, use the following code.
if not Config.IsUrlReserved('http://+:2001/tms/business/') then
Config.ReserveUrl('http://+:2001/tms/business/', [TSidType.CurrentUser, TSidType.NetworkService]);
You can remove a reservation using RemoveURL:
Config.RemoveUrl('http://+:2001/tms/business/');
To get a list of all existing URL reservations, use the Reservations enumeration property, which will retrieve you object instances of TUrlAclInfo class.
var
Info: TUrlAclInfo;
begin
Config.RefreshReservations;
for Info in Config.Reservations do
ReservedPrefix := Info.UrlPrefix;
end;
Server Certificate Configuration
To add a bind a certificate to a port, use AddSslCert method:
Config.AddSslCert('0.0.0.0', 443, 'My', Thumbprint, MyAppGuid);
First three parameter are IP, Port and Certificate Store. Fourth parameter is the certificate thumbprint in binary format (TBytes). Finally, last parameter is a TGUID for the App Id (you can use an empty GUID here). For more information about those parameters, see the description in TMSHttpConfig tool topic.
To remove a binding, using RemoveSsl method just passing IP and port:
Config.RemoveSsl('0.0.0.0', 443);
To get a list of all existing certificate bindings, use SslCerts enumeration property, which will give you object instances of TSslCertInfo class.
var
Info: TSslCertInfo;
begin
Config.RefreshSslCerts;
for Info in FConfig.SslCerts do
ListBox1.AddItem(Format('%s:%d', [Info.Ip, Info.Port]), Info);
ListBox1.Sorted := true;
end;
The TMSHttpConfig tool uses THttpSysServerConfig under the hood to perform its actions. So for more detailed info about how to use these classes you can refer to its source code, available in Sparkle demos.
Windows netsh Command-Line
If you don't want to use neither TMSHttpConfig tool nor THttpSysServerConfig class, you can just use Windows command line tool netsh to do URL reservation and HTTPS config in your server. The following examples reserve the Url http://+:2001/tms/business (meaning we will be able to process requests to the port 2001, if the requested url starts with tms/business). Detailed info can be found in this Microsoft article.
URL Reservation
For this, just run windows command line tool (cmd.exe) under administrative rights and execute the following command:
netsh http add urlacl url=http://+:2001/tms/business/ user=%USERDOMAIN%\%USERNAME%
where %USERDOMAIN% and %USERNAME% are the domain and name of the user under which your server will run. For testing purposes, you can just give access to any user:
netsh http add urlacl url=http://+:2001/tms/business/ user=Everyone
Note that if the language of your Windows is not English, you must need to change "Everyone" by the name of the group that represents all users in Windows. Or, alternatively, provide the ssdl of the "everyone" group (or any other group you want to give permission to, for example replace "WD" by "NS" to provide access to network service.
netsh http add urlacl url=http://*:2001/tms/business/ sddl=D:(A;;GA;;;WD)
Server Certificate Configuration (binding to a port)
Run windows command line tool (cmd.exe) under administrative rights and use a command like this:
netsh http add sslcert ipport=0.0.0.0:2002 certhash=0000000000003ed9cd0c315bbb6dc1c08da5e6 appid={00112233-4455-6677-8899-AABBCCDDEEFF}
The above command will bind the proper certificate to port 2002. There are three parameters in the command above that you need to change for your own usage:
ipport: You must use the port number you use for HTTPS connections. In the example, it was 2002. The IP can still be 0.0.0.0 which means any IP.
certhash: You must provide the thumbprint of the certificate you want to use for your server. You can check the thumbprint by using Microsoft Management Console. Please refer to Microsoft article "How to: Retrieve the Thumbprint of a Certificate" for detailed information.
appid: This can be any arbitrary GUID. You just need to generate one and input it here. You can even use the GUID generator in Delphi code editor (while editing code, just press Shift+Ctrl+G, Delphi will generate a GUID for you). The GUID must be enclosed by brackets. For more information, please refer to "How to: Configure a Port with an SSL Certificate".
Socket Server
The socket server is a self-contained, high-performance HTTP server for Linux and Windows, implemented natively in Delphi over raw sockets, with support for HTTPS (OpenSSL 3, including SNI and zero-downtime certificate reload), WebSockets, static files and built-in hardening. It is the recommended way to deploy Sparkle-based servers on Linux - a single binary, with no external web server required - and, on Windows, an alternative to the Http.Sys-based server that needs no administrative URL reservations.
The socket server is covered in its own chapter: Socket Server.
Apache-based Server
Apache web server is another platform you can use to run your HTTP server, in addition to Http.Sys-based Server. If the Http.sys-based server is the recommended way to build Sparkle HTTP server on Microsoft Windows, on Linux the recommended option is the socket server; the Apache-based server remains available as an alternative (and can also be used on Windows).
Apache support on Sparkle is based on built-in Web Broker technology available in Delphi. You create an Apache module using Web Broker and then deploy the module to an Apache web server using standard procedures. Here are the steps:
1. Create an Apache module using Web Broker
Basically, go to File > New > Other, then Delphi Projects > WebBroker > Web Server Application and then choose "Apache dynamic link module". This will create the default Apache module library.
2. In WebModuleUnit1 unit, add the units Sparkle.WebBroker.Server and Sparkle.WebBroker.Adapter to the uses clause:
uses {...},
Sparkle.WebBroker.Server,
Sparkle.WebBroker.Adapter,
Sparkle.HttpServer.Module; // just for the anonymous module example
3. Declare a global TWebBrokerServer instance:
var
Server: TWebBrokerServer;
4. Create the instance in the initialization of the unit, and add your desired Sparkle modules (XData, RemoteDB, etc.) to its Dispatcher:
initialization
Server := TWebBrokerServer.Create;
// add modules you want to use. This example assumes a simple Sparkle module,
// but you can add your XData, RemoteDB or any other Sparkle module
Server.Dispatcher.AddModule(TAnonymousServerModule.Create(
'http://localhost/tms/hello',
procedure(const C: THttpServerContext)
begin
C.Response.StatusCode := 200;
C.Response.ContentType := 'text/html';
C.Response.Close(TEncoding.UTF8.GetBytes('<h1>Hello, World!</h1>'));
end
));
finalization
Server.Free;
end.
5. Replace the WebModule1DefaultHandlerAction event handler with the code below to dispatch the requests:
procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
Adapter: IWebBrokerAdapter;
begin
Adapter := TWebBrokerAdapter.Create(Request, Response);
Server.DispatchRequest(Adapter);
end;
That's it, your Apache module is done. You can now just build it and deploy to your Apache server. This is just WebBroker and Apache knowledege, here are some links to videos and/or articles about how to do it:
Building and Deploying a Linux Apache Module with Delphi WebBroker (Youtube video)
Delphi for Linux Database and Web Development (Blog post)
Indy-based Server
Alternatively to Http.Sys-based Server and Apache-based server, TMS Sparkle also offers an Indy-based server. Indy is an HTTP library for Delphi that fully implements HTTP server. While the http.sys-based server is the recommended option for Sparkle HTTP servers on Windows, and Apache is the option for Linux-based servers - both technologies are solid and proven - Indy-based server has its own usage.
With Indy-based server you can have a stand-alone HTTP server in any platform supported by Delphi, so it's an alternative for example to have a Sparkle HTTP server on Android. For Windows it's still recommended using http.sys, and for Linux the socket server, as you can also build stand-alone servers with them.
To use Indy-based server just instantiates the TIndySparkleHTTPServer
(declared in unit Sparkle.Indy.Server) and use the Dispatcher property
which is a Sparkle HTTP Dispatcher
for you to register Sparkle modules
(like XData, RemoteDB, etc.). Here is a code example:
uses
{...}, Sparkle.Indy.Server;
procedure TForm1.FormCreate(Sender: TObject);
var
Server: TIndySparkleHTTPServer;
begin
Server: = TIndySparkleHTTPServer.Create(Self);
Server.DefaultPort := 8080;
Server.Dispatcher.AddModule(TAnonymousServerModule.Create(
'http://localhost:8080/tms/hello',
procedure(const C: THttpServerContext)
begin
C.Response.StatusCode := 200;
C.Response.ContentType := 'text/html';
C.Response.Close(TEncoding.UTF8.GetBytes('<h1>Hello, World!</h1>'));
end
));
Server.Active := True;
end;
Some remarks:
TIndySparkleHttpServer is a TComponent descending from TIdHTTPServer. In the example above it's being created as owned by the Form, so it will be destroyed automatically. You can also simply create with no owner, and then you need to destroy the instance eventually.
Since it descends from TIdHTTPServer, it inherits all its properties and events. The properties DefaultPort and Active used above, for example, are from TIdHTTPServer. It's out of the scope of this documentation to explain full Indy HTTP server usage.
In-Process Server
Sparkle allows you to create an in-process "HTTP" server. This approach allows you to have a "server" in your application that responds to calls from the client inside the same application. This can serve many purposes:
Build a quick prototype without needing to setup an http.sys-based or Apache-based server.
Create a monolithic version of your application that works standalone, no needing a server, and without changing a single line of code.
Test your server modules.
You can have multiple in-process server in the same application. Each server is identified by a name.
Creating the in-process server
1. The server is created on-the-fly when you try to access it by its name using TInProcHttpServer (declared in unit Sparkle.InProc.Server):
uses {...}, Sparkle.InProc.Server;
{...}
var Server: TInProcHttpServer;
{...}
Server := TInProcHttpServer.Get('myserver');
2. Add your modules (XData, RemoteDB, etc.) to the server using the Dispatcher property:
Server.Dispatcher.AddModule(SomeModule);
That is enough to have the server working. You can even have it in a one-line code:
TInProcHttpServer.Get('myserver').Dispatcher.AddModule(SomeModule);
Accessing the server from the client
When using Sparkle HTTP Client, or any other class that uses it under the hood, you just need to use an URI with the local:// prefix, followed by the in-process server name. For example, to perform a request to the server root, just use this:
Client.Get('local://myserver');
to access another URL in the server, just build it as you would do with regular HTTP requests:
Client.Get('local://myserver/somepath?a=1');
Note that the server doesn't "run", i.e., it doesn't have a listener or a thread executing to respond to requests. It's the client that detects you are trying to access an in-process server (from the local:// scheme) and then just directly dispatch the request to the server dispatcher, in the same thread.
HTTP Dispatcher
TMS Sparkle provides five different types of server:
All of them have in common the HTTP Dispatcher (THttpDispatcher) which is the object that effectively handles the requests. When registering server modules, you register them in the dispatcher (provided by each server type through a property named Dispatcher). All the server does is receive the request and then pass it to the dispatcher.
One of the advantages of using dispatcher is because it's agnostic to the server type. Regardless if the server is http.sys or Apache, for example, you add modules to the dispatcher and don't care about what is the server type. This is also useful to convert your server from one type to another, or even to create an alternative in-process server to test your modules.
For example, you could have a generic method that adds modules to the dispatcher:
uses {...}, Sparkle.HttpServer.Dispatcher;
procedure AddModules(Dispatcher: THttpDispatcher);
begin
Dispatcher.AddModule(TXDataServerModule.Create(XDataUrl, Pool));
Dispatcher.AddModule(TRemoteDBModule.Create(RemoteDBUrl, Factory));
Dispatcher.AddModule(TMyCustomModule.Create(CustomUrl));
end;
Then you can call that method when creating an http.sys-based server:
var
Server: THttpSysServer;
begin
Server := THttpSysServer.Create;
AddModules(Server.Dispatcher);
Server.Start;
{...}
and you can reuse the same dispatcher setup with an in-process server, for testing purposes for example:
AddModules(TInProcHttpServer.Get('myserver').Dispatcher);
Server Modules
HTTP Server Modules are the main part of your Sparkle HTTP Server service/application. Those are the objects that will process HTTP requests, and where your logic will be implemented. For more info about how server modules fit into the whole server architecture, see the overview for Sparkle Http Servers.
The basic workflow for creating and using a server module is:
Create a new module class inheriting from THttpServerModule;
Override TBaseHttpServerModule.ProcessRequest and insert code to examine the HTTP request and build the HTTP response;
Create an instance of the module passing the base URI which it will respond to;
Register the module in the HTTP Dispatcher.
The following example creates a module that responds any request with a "Test123" plain text response.
uses {...}, Sparkle.HttpServer.Module, Sparkle.HttpServer.Context;
type
TSimpleModule = class(THttpServerModule)
public procedure ProcessRequest(const C: THttpServerContext); override;
end;
{...} implementation {...}
procedure TSimpleModule.ProcessRequest(const C: THttpServerContext);
begin
C.Response.StatusCode := 200;
C.Response.ContentType := 'text/plain';
C.Response.Close(TEncoding.UTF8.GetBytes('Test123'));
end;
// Add the module to a dispatcher
Dispatcher.AddModule(TSimpleModule.Create('http://host:2001/simple/');
The example above is just a simple, unreal example. In real applications, obviously, you will need to carefully examine the HTTP request and then properly build the HTTP response.
TAnonymousServerModule class
For very simple operations, use TAnonymousServerModule by passing an anonymous request procedure to process the request. This way you don't even need to create a new class derived from THttpServerModule. The following code creates and adds a minimal "Hello, World" module with one line of code:
Server.AddModule(TAnonymousServerModule.Create(
'http://+:80/tms/hello',
procedure(const C: THttpServerContext)
begin
C.Response.StatusCode := 200;
C.Response.ContentType := 'text/html';
C.Response.Close(TEncoding.UTF8.GetBytes('<h1>Hello, World!</h1>'));
end
));
Examining the Request
When processing a request in a server module, the first step is to examine the HTTP request. The THttpServerContext passed to TBaseHttpServerModule.ProcessRequest exposes the request through its THttpServerContext.Request property - a THttpServerRequest object with all the information sent by the client.
The example below responds with a "Method not allowed" error (405) when the client sends a PUT request:
procedure TMyServerModule.ProcessRequest(const C: THttpServerContext);
begin
if C.Request.MethodType = THttpMethod.Put then
C.Response.StatusCode := 405
else
// code for other methods
end;
Request URI
THttpServerRequest.Uri returns a TUri object whose properties give access to the individual parts of the URI:
RequestedPath := C.Request.Uri.Path;
QueryString := C.Request.Uri.Query;
Request method
Use THttpServerRequest.MethodType or
THttpServerRequest.Method to retrieve the request method.
THttpServerRequest.MethodType returns the
THttpMethod enumeration, convenient for case statements;
THttpServerRequest.Method returns the textual
representation, useful for methods not covered by the enumeration.
case C.Request.MethodType of
THttpMethod.Get: DoGetResponse;
THttpMethod.Put: DoPutResponse;
else
DoInvalidRequestMethod;
end;
{...}
if C.Request.Method = 'DELETE' then // delete method requested
Request headers
Request headers are available through THttpServerRequest.Headers, a THttpHeaders object (see THttpHeaders object for its methods):
Accept := C.Request.Headers.Get('Accept');
Request body (content)
The request body is available as a byte array through THttpServerRequest.Content. If the message has no content, the array length is zero.
ContentAsUTF8String := TEncoding.UTF8.GetString(C.Request.Content);
Authenticated user
If the request has been processed by an authentication middleware, the user identity and claims may be present in THttpServerRequest.User:
UserIdentity := C.Request.User;
if UserIdentity <> nil then // means request is authenticated
Remote IP
Read THttpServerRequest.RemoteIp to get the IP address of the client that sent the request.
Building the Response
When processing a request in a server module, you must build the HTTP response. The THttpServerContext exposes it through THttpServerContext.Response - a THttpServerResponse object with all the properties and methods you need.
Status code
Set THttpServerResponse.StatusCode to specify the HTTP status code. If you don't set it, Sparkle uses the default value, 400.
C.Response.StatusCode := 200; // OK
Response headers
Specify the response headers through THttpServerResponse.Headers, a THttpHeaders object (see THttpHeaders object for its methods). The following example clears the headers and sets the ETag header:
C.Response.Headers.Clear;
C.Response.Headers.SetValue('ETag', '"737060cd8c284d8af7ad3082f209582d"');
Content type and encoding
Use THttpServerResponse.ContentType and THttpServerResponse.ContentEncoding to set the content type and encoding:
C.Response.ContentType := 'application/json';
Sending a response with no content
After setting the response headers and properties, call THttpServerResponse.Close to send the response headers with no body and finish the response:
C.Response.Close;
You don't need to explicitly close the response: if you don't, Sparkle closes it later for you. Once a response is closed it is sent back to the client, so you can no longer change any property that affects the response (headers, status code, etc.) - doing so raises an exception.
Sending content as a byte array
You can close the response sending a byte array as the content:
C.Response.ContentType := 'text/plain';
C.Response.Close(TEncoding.UTF8.GetBytes('A plain text response.'));
This sets the content-length header with the correct value, sends the HTTP response headers, and then sends the bytes as the content body.
Sending content as a stream
Instead of a byte array, you can write the content body to the stream returned by THttpServerResponse.Content:
MyBitmap.SaveToStream(C.Response.Content);
// You can also use C.Response.Content.Write method
When the content stream is written for the first time, Sparkle sends the
response headers and starts sending the content body. From that point,
THttpServerResponse.HeadersSent is True and any change to
a property that affects headers (StatusCode, ContentType, Headers, etc.)
raises an exception. As you keep writing, Sparkle keeps sending the bytes to
the client. When you finish, call THttpServerResponse.Close
to finish the request; if you don't, Sparkle eventually calls it for you.
Setting ContentLength before sending content
If you know the size of the content in advance, set THttpServerResponse.ContentLength before writing to the stream:
C.Response.ContentLength := 65200;
This sends the Content-Length header before the content. If you then send the content as a byte array through THttpServerResponse.Close, the existing THttpServerResponse.ContentLength value is discarded and the byte array length is used instead. If you send the content through the THttpServerResponse.Content stream, the number of bytes written must equal THttpServerResponse.ContentLength; writing more or fewer bytes raises an exception.
Sending chunked responses
If you don't know the size of the response in advance, or you just want to
send a chunked response, set THttpServerResponse.Chunked to
True:
C.Response.Chunked := true;
// Now write to stream using Content property
When you start writing to the stream, the headers are sent without a content-length header and with a "transfer-encoding: chunked" header, and the content is sent using chunked encoding as you keep writing. Finish the response by calling THttpServerResponse.Close (or let Sparkle call it for you).
Checking the response status
Use THttpServerResponse.HeadersSent and
THttpServerResponse.Closed to check the current status of
the response. Both are False while nothing has been sent yet. Once the
headers have been sent, THttpServerResponse.HeadersSent is
True and you can no longer change any property that affects headers (doing
so raises an exception). Once the response is closed,
THttpServerResponse.Closed is True and any attempt to
send more data (for example, writing to the
THttpServerResponse.Content stream) raises an exception.
if not C.Response.HeadersSent then
C.Response.StatusCode := 200;
if not C.Response.Closed then
C.Response.Content.Write(Buffer, BufferSize);
Handling Multipart Content
Sparkle provides specific classes to handle multipart content sent by the client. More specifically, multipart/form-data content-type. That is usually the content-type sent by browsers when you use the HTML <form> tag to upload a file content (input control with type set to "file").
Key class is TMultipartFormDataReader, declared in unit
Sparkle.Multipart.FormDataReader. The following is an example of how to
use it.
uses {...}, Sparkle.Multipart.FormDataReader;
procedure ProcessRequest(const C: THttpServerContext);
var
Reader: TMultipartFormDataReader;
I: integer;
Part: TMultipartFormDataPart;
vres: string;
begin
Reader := TMultipartFormDataReader.Create(C.Request.ContentStream, C.Request.Headers.Get('content-type'));
try
vres := '';
while Reader.Next do
begin
Part := Reader.PartInfo;
vres := vres + 'Name: ' + Part.Name + #13#10;
if Part.FileName <> '' then
vres := vres + 'FileName: ' + Part.FileName + #13#10;
vres := vres + 'MediaType: ' + Part.MediaType + #13#10;
vres := vres + 'Charset: ' + Part.Charset + #13#10;
vres := vres + 'Content-Disposition: ' + Part.ContentDisposition + #13#10;
vres := vres + '=== Params ==='#13#10;
for I := 0 to Part.ParamCount - 1 do
vres := vres + Part.ParamName[I] + ': ' + Part.ParamValue[I] + #13#10;
if (Part.FileName <> '') and (Part.MediaType <> '') then
TFile.WriteAllBytes(
(TPath.Combine(ExtractFilePath(ParamStr(0)), TPath.GetFileName(Part.FileName))),
Reader.ContentAsBytes)
else
vres := vres + 'Content: ' + Reader.ContentAsString + #13#10;
vres := vres + #13#10;
end;
C.Response.StatusCode := 200;
C.Response.ContentType := 'text/plain;charset=UTF-8';
C.Response.Close(TEncoding.UTF8.GetBytes(vres));
finally
Reader.Free;
end;
end;
After creating TMultipartFormDataReader, use Reader.Next to iterate through the parts, and use Reader.PartInfo to get information about that specific part.
Besides the regular properties used in the example above (Name, FileName, MediaType, Charset, ParamCount, ParamName, ParamValue), you can read the part content in three different ways:
ContentAsBytes: Read the content as a byte array TArray<byte>.
ContentAsString: Read the content as string, using the specified charset or UTF8 if not specified.
ContentToStream: Save the part content to a stream. This can be used, for example, to transfer the content to a TFileStream.
It's worth noting that TMultpartFormDataReader is a sequential reader, which means you can start reading the content in chunks. This allows very low memory usage, as you don't need to load all the part content in memory. Using ContentToStream, for example, you can transfer the content sent by the client directly to a file stream keeping the memory usage low.