Table of Contents

Http Client

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

  1. Create an THttpClient class (declared in Sparkle.Http.Client unit);

  2. Call CreateRequest to create a THttpRequest object;

  3. Fill the request object properties;

  4. Call Send method 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 Uri property to specify the server URI to send the request:

Request.Uri := 'http://myaddress.com';

Defining the request method

Use the Method property to specify the request method:

Request.Method := 'DELETE';

Specifying request headers

You can use Headers property to define custom headers for the HTTP request. The Headers property provides you with a THttpHeaders object. There are several methods you can use in this headers object to manipulate headers. The following example clears headers and set the value of ETag header.

Request.Headers.Clear;
Request.Headers.SetValue('ETag', '"737060cd8c284d8af7ad3082f209582d"');

Defining request content body

Set ContentStream property 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 SetContent method to set the content of the request to be sent to server. The SetContent method receives a byte array as parameter.

Request.SetContent(TEncoding.UTF8.GetBytes('content'));

Setting request timeout

Use Timeout property 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 to the server using the THttpClient, you will receive a THttpResponse object. There are several properties you can use to examine the response returned by the server. You are responsible for destroying the THttpResponse after you use it. You can also put the instance in a variable decalred as IHttpResponse (which has the same properties as THttpResponse) to benefit from interface automatic reference counting.

Status code

Use StatusCode property to examine the code returned by the server.

if Response.StatusCode = 200 then // Ok!

Response Headers

Use Headers property to examine the headers returned in the HTTP response. The Headers property provides you with a THttpHeaders object. There are several methods you can use in this headers object to manipulate the headers.

Content Length

Property ContentLength gives you the length of the content returned by the server. If the content was gzip encoded, this property will give you the value for the size of content already decompressed.

Content Type

Use ContentType property to examine the value of content-type returned by the server.

ContentAsBytes

To read all the response body at once, you can use ContentAsBytes property. This will return the whole body in a byte array.

ContentAsStream

ContentAsStream property will return a TStream object representing the content of response body. You can read from stream to receive content from server. If the response is chunked, the client might still be receiving data from the server while you read from the stream. If you reach the end of stream and data transfer is not completed yet, a read operation on the stream will wait until more data is available in the stream. Never use the Size property of the stream since the stream grows dynamically as you keep reading from it. Use ContentLength to know the exact size of the stream. If the response is chunked, ContentLength will be 0 and you must keep reading from the stream until the returned number of bytes in a Read operation is zero.

Chunked property

Indicates if the response content is sent in chunked encoding (true).

THttpHeaders object

The THttpHeaders object provides several methods for you to read and set headers of either a response or request object. It's used for both client and server classes in Sparkle. The following are a list of main properties and methods available. The THttpHeaders object is declared in unit Sparkle.Http.Headers.

For all methods, the header names are case-insensitive, so a call to Get('Accept') is equivalent to Get('accept') or Get('ACCEPT').

Setting a header value

Call SetValue method to set the value of a header. You must pass the header name, and header value. If the header name already exists, the current value will be replaced by the new one.

Headers.SetValue('ETag', '"737060cd8c284d8af7ad3082f209582d"');

Retrieving a header value

Use Get method to retrieve the value of a header. You don't need to check if a header exists before trying read its value. If you try to execute the Get method passing a header that doesn't exist in the request, an empty string will be returned. For a more precise way of checking if the header exists in a request or, you can use Exists method.

ContentType := Headers.Get('Content-Type');

Checking if a header exists

Use Exists method to verify if a header exists. This is useful to know if a header is present in a request or response message.

HasAcceptHeader := Headers.Exists('Accept');

Iterating through all headers

You can also iterate through all headers in the request/response by using AllHeaders property. The result type of this property is TEnumerable<THttpHeaderInfo>. The THttpHeaderInfo is just a record with both header name and value:

uses {...}, Sparkle.Http.Headers;
 
var
  Info: THttpHeaderInfo;
begin
  for Info in Headers.AllHeaders do
    // Use Info.HeaderName and Info.HeaderValue to retrieve header name and value, respectively

Removing a header

Call Remove method to remove a header from message:

Headers.Remove('Accept');

Clearing headers

You can use Clear method to clear all defined headers.

Headers.Clear;

THttpClient Events

THttpClient class has events to help you control the client/server communication.

OnSendingRequest event

THttpRequestProc = reference to procedure(ARequest: THttpRequest);
 
property OnSendingRequest: THttpRequestProc;

This event 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 for example adding a header common to all requests, or doing some logging of requests being sent.

MyHttpClient.OnSendingRequest :=
  procedure(Req: THttpRequest)
  begin
    Req.Headers.SetValue('custom-header', 'customvalue');
  end;

OnResponseReceived event

THttpResponseProc = reference to procedure(ARequest: THttpRequest; var AResponse: THttpResponse);
 
property OnResponseReceived: THttpResponseProc;

This event 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 by another one in case you want to alter the response for further processing of 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