Table of Contents

Http Client

Sparkle provides classes to perform HTTP client requests from your application. Basic usage is simple:

  1. Create a THttpClient instance (declared in unit Sparkle.Http.Client);

  2. Call THttpClient.​Create​Request to create a THttpRequest object;

  3. Fill the request object properties;

  4. Call THttpClient.Send passing the request to receive a THttpResponse object;

  5. 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.​Content​Stream 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.​Set​Content 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.​Status​Code 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.​Content​AsBytes (a byte array), and its media type with THttpResponse.​Content​Type.

THttpResponse.​Content​Length 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.​Content​AsStream 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.​Content​Length 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.​Set​Value (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.​All​Headers 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.​OnSending​Request 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.​OnResponse​Received 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