Http Client
Sparkle provides classes to perform HTTP client requests from your application. Basic usage is simple:
Create a THttpClient instance (declared in unit Sparkle.Http.Client);
Call THttpClient.CreateRequest to create a THttpRequest object;
Fill the request object properties;
Call THttpClient.Send passing the request to receive a THttpResponse object;
Use the response object properties to inspect the response.
The THttpClient class is available in several platforms: Windows, Mac OS X, Android and iOS (iPad/iPhone). You can use either http or https addresses.
The following example sends a post request to address http://myserver/customers, passing a string as the request body, check if the response status code is 200, and if it does, get the response content body as string.
uses {...}, Sparkle.Http.Client;
var
Client: THttpClient;
Request: THttpRequest;
Response: THttpResponse;
ResponseBody: string;
begin
Request := nil;
Response := nil;
Client := THttpClient.Create;
try
Request := Client.CreateRequest;
Request.Uri := 'http://myserver/customers';
Request.Method := 'POST';
Request.SetContent(TEncoding.UTF8.GetBytes('Request content'));
Response := Client.Send(Request);
if Response.StatusCode = 200 then
ResponseBody := TEncoding.UTF8.GetString(Response.ContentAsBytes);
finally
Request.Free;
Response.Free;
Client.Free;
end;
The following topics describe more details about using the HTTP client class.
Configuring a Request
After you use the THttpClient to create a THttpRequest, there are several properties you can use to properly configure your request before sending it to the server.
Defining the URI
Use THttpRequest.Uri to specify the server URI to send the request to:
Request.Uri := 'http://myaddress.com';
Defining the request method
Use THttpRequest.Method to specify the request method:
Request.Method := 'DELETE';
Specifying request headers
Use the THttpRequest.Headers property to define custom headers for the HTTP request. It provides a THttpHeaders object with several methods to manipulate headers. The following example clears the headers and sets the value of the ETag header.
Request.Headers.Clear;
Request.Headers.SetValue('ETag', '"737060cd8c284d8af7ad3082f209582d"');
Defining request content body
Set THttpRequest.ContentStream to a valid TStream object. The content of the stream will be sent as the body of the request. The stream will not be destroyed by the client, it is up to you to destroy it. Example:
FileStream := TFileStream.Create('C:\myfile.dat', fmOpenRead);
try
Request.ContentStream := FileStream;
// send the request
finally
FileStream.Free;
end;
Alternatively, for simpler and smaller bodies, you can use THttpRequest.SetContent to set the content of the request to be sent to the server. It receives a byte array as parameter.
Request.SetContent(TEncoding.UTF8.GetBytes('content'));
Setting request timeout
Use THttpRequest.Timeout to specify the maximum time length (in milliseconds) the client will wait for a response from the server until an error is raised. Default value is 60000 (60 seconds).
Request.Timeout := 30000; // set timeout to 30 seconds
Examining the Response
After sending a request with THttpClient, you receive a THttpResponse object. You are responsible for destroying it after use. You can also assign it to a variable declared as IHttpResponse (which exposes the same properties) to benefit from interface automatic reference counting.
Status code
Read THttpResponse.StatusCode to examine the code returned by the server.
if Response.StatusCode = 200 then // Ok!
Response headers
Read THttpResponse.Headers to examine the headers returned in the response. It provides a THttpHeaders object with several methods to manipulate the headers.
Content
Read the whole response body at once with THttpResponse.ContentAsBytes (a byte array), and its media type with THttpResponse.ContentType.
THttpResponse.ContentLength gives the length of the returned content; if the content was gzip encoded, it reports the size of the content already decompressed.
Reading the content as a stream
THttpResponse.ContentAsStream returns a TStream over the
response body. If the response is chunked, the client might still be
receiving data from the server while you read from the stream: when you
reach the end of the received data but the transfer is not complete, a read
operation blocks until more data is available. Never rely on the stream's
Size property, since the stream grows dynamically as you keep reading. Use
THttpResponse.ContentLength to know the exact size; for a
chunked response (THttpResponse.Chunked is True),
ContentLength is 0 and you must keep reading until a read operation
returns zero bytes.
THttpHeaders object
THttpHeaders (declared in unit Sparkle.Http.Headers) provides the methods to read and set the headers of a request or a response. The same class is used by both the client and the server classes in Sparkle.
Header names are case-insensitive, so Get('Accept') is equivalent
to Get('accept') or Get('ACCEPT').
Set a header with THttpHeaders.SetValue (which replaces the value when the header already exists), and read it with THttpHeaders.Get. You do not need to check whether a header exists before reading it: THttpHeaders.Get returns an empty string for a header that is not present. When you must distinguish a missing header from an empty one, use THttpHeaders.Exists. Use THttpHeaders.Remove to remove a single header and THttpHeaders.Clear to remove them all.
You can enumerate every header through the THttpHeaders.AllHeaders property, which yields THttpHeaderInfo records exposing the header name and value:
uses {...}, Sparkle.Http.Headers;
var
Info: THttpHeaderInfo;
begin
for Info in Headers.AllHeaders do
// Use Info.Name and Info.Value to retrieve the header name and value
THttpClient Events
THttpClient class has events to help you control the client/server communication.
OnSendingRequest event
THttpClient.OnSendingRequest is called right before a request is sent to the server. It's an opportunity to inspect the THttpRequest object and do some last-minute modifications, like adding a header common to all requests, or logging the requests being sent.
MyHttpClient.OnSendingRequest :=
procedure(Req: THttpRequest)
begin
Req.Headers.SetValue('custom-header', 'customvalue');
end;
OnResponseReceived event
THttpClient.OnResponseReceived is called right after a
response is received from the server. It's an opportunity to inspect the
THttpResponse object and do some generic processing. The
AResponse object is passed by reference, meaning you can replace it with
another one in case you want to alter the response for further processing
by the framework. If you do this, you must destroy the previous
AResponse object.
Proxy configuration on Windows
When on Windows, you can configure the proxy used for connections. There are three modes for using proxies:
Default
This is the default mode. Sparkle http client on Windows is based on WinHttp library. When proxy is set to this mode, Sparkle will use the default proxy settings for WinHttp library. Note that this is not the default proxy used by Internet Explorer. The proxy for WinHttp is set using specific code, using netsh command-line (you can find an example here: Netsh Commands for WINHTTP).
Custom
In this mode, it's you that manually define the proxy address.
Auto
Proxy settings will be detected automatically based on current Windows settings (Internet Explorer and other global settings). Supported on Windows 8.1 and later only. If your application is running on a Windows version below 8.1, the mode will automatically switch to Default mode.
The following code illustrates how to use each mode, from an existing THttpClient instance (represented here by FClient variable):
uses {...}, Sparkle.WinHttp.Engine;
var
Engine: TWinHttpEngine;
begin
Engine := TWinHttpEngine(FClient.Engine);
// Option 1: Current behavior
Engine.ProxyMode = THttpProxyMode.Default;
// Option 2: Get proxy settings automatically (windows 8.1 and later only)
Engine.ProxyMode := THttpProxyMode.Auto;
// Option 3: Custom proxy settings
Engine.ProxyMode := THttpProxyMode.Custom;
Engine.ProxyName := 'localhost:8888';
// Force a new session to use new proxy settings
Engine.ResetSession;
end;
Bypassing Self-Signed Certificates on Windows
By default Windows HTTP client raises an error if you try to connect to a server that has a wrong certificate, like wrong date, domain name, etc. You can bypass this protection and allow the client to connect. This is usually useful when you want to test your client against a server with a self-signed certificate. But beware that this could create a security issue by allowing that!
For that, you need to use units Sparkle.WinHttp.Engine and
Sparkle.WinHttp.Api and then add an event handler to the WinHttp engine.
In the example below, FClient is of type THttpClient.
uses
{…}, Sparkle.WinHttp.Engine, Sparkle.WinHttp.Api;
// FClient is of type THttpClient
TWinHttpEngine(FClient.Engine).BeforeWinHttpSendRequest :=
procedure(Handle: HINTERNET)
var
dwFlags: DWORD;
begin
dwFlags := SECURITY_FLAG_IGNORE_UNKNOWN_CA or
SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE or
SECURITY_FLAG_IGNORE_CERT_CN_INVALID or
SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
WinHttpCheck(WinHttpSetOption(Handle, WINHTTP_OPTION_SECURITY_FLAGS, @dwFlags, SizeOf(dwFlags)));
end;
Using client certificates on Windows
You can make bind a client certificate when making requests, on Windows clients, using the following template code:
uses
{…}, Sparkle.WinHttp.Engine, Sparkle.WinHttp.Api;
var
Store: HCERTSTORE;
Cert: PCERT_CONTEXT;
begin
// Open the ''Personal'' SSL certificate store for the local machine and locate the required client-side certificate
Cert := nil;
Store := CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, PChar(''MY''));
if (Store <> nil) then
Cert := CertFindCertificateInStore(Store, X509_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR, PChar(''mycertsubject''), nil);
// If a valid certificate was found then OK to create and send the HTTP request
if (Cert <> nil) then
begin
// Use ''BeforeWinHttpSendRequest'' event to set any HTTP request properties such as client-side SSL certificate
TWinHttpEngine(FClient.Engine).BeforeWinHttpSendRequest :=
procedure (Req: HINTERNET)
begin
WinHttpCheck(WinHttpSetOption(Req, WINHTTP_OPTION_CLIENT_CERT_CONTEXT, Cert, SizeOf(CERT_CONTEXT)));
end;
end;
end;
// perform requests with FClient normally