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 three different server types: Http.Sys-based server, 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;
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".
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, Apache is the way to go if you want to run it on Linux (although you can also use it on Windows as well).
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, or full stand-alone HTTP server on Linux (without installing Apache). For Windows it's still recommended using http.sys as you can also build stand-alone server with it.
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 four different types of server:
All of them have in common the HTTP Dispatcher (THTTPDispatcher class) 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 class (declared in Sparkle.HttpServer.Module unit);
Override the method 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, there is TAnonymousServerModule that you can use just 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 examine the HTTP request. The only parameter provided
by the ProcessRequest method of the server module is a THttpServerContext
object. This object has two key properties: Request and Response.
The Request property provides a THttpServerRequest object with all the
info about the HTTP request sent by the client. THttpServerRequest class
is declared in unit Sparkle.HttpServer.Context
.
A quick example of how to read information from request is showed below. The server module response with a "Method not allowed" error (405) if 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
You can check the URI requested by the client using property Uri, which returns a TUri object. You can use several properties of TUri object to retrieve information about parts of the URI.
RequestedPath := C.Request.Uri.Path;
QueryString := C.Request.Uri.Query;
Request Method
Use properties MethodType or Method to retrieve the method in the http request. MethodType property is just an enumerated type with the most commonly used methods to help you with case statements or similar language constructs. The Method property provides you with the textual representation of the method.
THttpMethod = (Other, Get, Post, Put, Delete, Head);
{...}
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 Headers property of the Request object. You can use methods Exists and Get to check if a header is included in request, or get the header value.
Accept := C.Request.Headers.Get('Accept');
The Headers property is actually a THttp
Request body (content)
The request body, or content, is available as a byte array in property Content. If the message has no content, the length of byte array will be zero.
ContentAsUTF8String := TEncoding.UTF8.GetString(C.Request.Content);
Authenticated user
If the request has been processed by an authentication middleware, it's possible that the user identity and claims are present in the request. To access such info, just read the User property:
UserIdentity := C.Request.User;
if UserIdentity <> nil then // means request is authenticated
Remote IP
You can check the IP address of the client which sent the request by reading the RemoteIP property.
Building the Response
When processing a request in a server module, you must create the HTTP response. The only parameter provided by the ProcessRequest method of the server module is a THttpServerContext object. This object has two key properties: Request and Response. The Response property provides a THttpServerResponse object with all the properties and methods you need to build the HTTP response.
A quick example is showed below. The server module responses with a "Method not allowed" error (405) if 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;
Status Code
Use the StatusCode property to specify the HTTP status code response. If you don't set the status code, Sparkle will use the default value, which is 400.
C.Response.StatusCode := 200; // OK
Response Headers
You can specify the response headers using Headers property of the
Response object. The Headers property provides you with a
THttp
C.Response.Headers.Clear;
C.Response.Headers.SetValue('ETag', '"737060cd8c284d8af7ad3082f209582d"');
ContentEncoding and ContentType
You can use ContentEncoding and ContentType to set content encoding and type:
C.Response.ContentType := 'application/json';
Sending response with no content
After you set response headers and specific properties (like status code), you can call Close method to send response headers without content body, and finish the response:
C.Response.Close;
You don't need to explicitly close the response. If you don't close it, Sparkle will close it later for you. Once a response is closed, it's send back to client, thus you can't change any other property that affect the response anymore (headers, status code, etc.). If you do, an exception will be raised.
Sending content using byte array
You can also close the response sending an array of bytes as content.
C.Response.ContentType := 'text/plain';
C.Response.Close(TEncoding.UTF8.GetBytes('A plain text response.'));
This will set the content-length header with the correct value, send HTTP response headers, and then send the bytes as the content body of the message.
Sending content using stream
Instead of a byte array, you can use streams to send the content body of response message. You do that by using Content property, which returns a TStream object you can use to write data:
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 will send the response headers to the client, and start sending the content body. From this point, the HeadersSent property will be set to true, and any change to any property that affects headers (StatusCode, ContentType, Headers, etc.) will raise an exception. As you keep writing the string, Sparkle will keep sending the bytes to the client. After you finished writing to the stream, call Close method to finish the request. If you don't call Close method, Sparkle will eventually call it at a later time, closing the response.
Setting ContentLength before sending content
If you know the size of the content in advance, you can set ContentLength property before writing to the stream:
C.Response.ContentLength := 65200;
This has the advance of sending the Content-Length header before sending the content using the stream. If after you set ContentLength property you send the content using byte array by calling Close method (see above), the existing value of ContentLength will be discarded and the length of byte array will be used.
If you send the content body using the Content stream, then the number bytes written to the stream must be equal to the value of ContentLength property. If you write more or less bytes, an exception will be raised.
Sending chunked responses
If you don't know the size of the response in advance, or if you just want to send chunked responsed, you can set Chunked property to true:
C.Response.Chunked := true;
// Now write to stream using Content property
When you start writing to the stream, headers will be sent without content-length header, and with "transfer-encoding: chunked" header. The content will be sent using chunked encoding, while you keep writing to the stream. You can finish the response by calling Close method. As already mentioned above, Sparkle you later call Close method if you don't.
Checking response status
You can use HeadersSent and Closed property to check the current status of HTTP response message. If no response was sent to client yet, both properties will be false. If the HTTP response message headers were already sent, HeadersSent property will be true and you can't change any property of response that affects headers. If you do, an exception will be raised. If you have called Close method and the response is closed (meaning no more data can be sent to client), Closed property will be true. If you try to send any info to client (e.g., writing to Content stream), an exception will be raised.
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.