Http Client
Sparkle provides classes to perform HTTP client requests from your application. Basic usage is simple:
Create an THttpClient class (declared in
Sparkle.Http.Client
unit);Call CreateRequest to create a THttpRequest object;
Fill the request object properties;
Call Send method 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 THttp
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
THttp
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;