Secure connections
Any WebSocket traffic that crosses a network you do not control should travel
over TLS, which turns ws:// into wss://. Public services almost always
require it, browsers refuse plain WebSockets from secure pages, and the
WhatsApp integration demands it. TTMSFNCWebsocketClient supports TLS through
Indy's IO handler architecture: by default it creates an OpenSSL-based handler
for you, and when you need a different TLS library — most commonly to use
OpenSSL 3.x — it lets you supply your own handler. This chapter covers enabling
TLS, deploying the OpenSSL libraries the default handler needs, plugging in a
custom IO handler, and how the picture differs for browser applications.
Enabling TLS
Set UseSSL to True before
connecting whenever the server address starts with wss://, and set
Port to the server's TLS port
(usually 443). The TLS negotiation then runs inside Connect, before the
WebSocket handshake, and OnConnect still signals that the connection is
ready.
uses
IdSSLOpenSSL, IdSSLOpenSSLHeaders;
procedure TForm1.ConnectSecure;
begin
// wss://echo.example.com/ws (port 443)
TMSFNCWebsocketClient1.HostName := 'echo.example.com';
TMSFNCWebsocketClient1.Port := 443;
TMSFNCWebsocketClient1.PathName := 'ws';
TMSFNCWebsocketClient1.UseSSL := True;
try
TMSFNCWebsocketClient1.Connect;
except
on E: EIdOSSLCouldNotLoadSSLLibrary do
ShowMessage('OpenSSL libraries not found: deploy libeay32.dll and ssleay32.dll ' +
'next to the executable. ' + WhichFailedToLoad);
on E: Exception do
ShowMessage('Secure connection failed: ' + E.Message);
end;
end;
Deploying OpenSSL for the default handler
When UseSSL is True and no custom handler is supplied, the client creates
an Indy OpenSSL IO handler that negotiates TLS 1.0, 1.1, or 1.2. That handler
loads the OpenSSL 1.0.2 libraries at runtime, so they must be available to the
application — on Windows, libeay32.dll and ssleay32.dll next to the
executable, in the bitness (32-bit or 64-bit) of the application. Sources and
precompiled binaries are available from the
OpenSSL 1.0.2 source archive and the
Indy OpenSSL binaries repository.
When the libraries cannot be loaded, Connect raises an exception that names
the missing library. To keep the libraries in a subfolder, tell Indy where they
are before the first secure connection:
uses
IdSSLOpenSSLHeaders;
procedure TForm1.FormCreate(Sender: TObject);
begin
// Must run before the first secure connection loads the libraries.
// The folder contains libeay32.dll and ssleay32.dll in the application's bitness.
IdOpenSSLSetLibPath(TPath.Combine(ExtractFilePath(ParamStr(0)), 'openssl'));
end;
Using OpenSSL 3.x or another TLS library
OpenSSL 1.0.2 is no longer maintained. To use OpenSSL 3.x, or any other TLS
implementation, supply your own IO handler from
OnCreateIOHandler.
The event fires during Connect whenever UseSSL is True, before the
default handler would be created, and passes the handler by reference in
AIOHandler; whatever you assign there is used instead of the default. Pick a
library that builds on Indy's TLS support — for example
TaurusTLS, which adds
OpenSSL 3.x support to Indy — create its IO handler class, and configure it.
The example shows the pattern with Indy's own OpenSSL handler restricted to TLS 1.2; swap in the handler class of the library you use.
uses
IdIOHandler, IdSSLOpenSSL;
procedure TForm1.TMSFNCWebsocketClient1CreateIOHandler(Sender: TObject;
var AIOHandler: TIdIOHandler);
var
Handler: TIdSSLIOHandlerSocketOpenSSL;
begin
// The event fires on every Connect while UseSSL is True; keep the handler
// that was created for an earlier connection.
if Assigned(AIOHandler) then
Exit;
// No owner: the client frees the handler it received.
// For OpenSSL 3.x, create the IO handler class of an Indy TLS library
// such as TaurusTLS here instead, and configure it the same way.
Handler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
Handler.SSLOptions.Mode := sslmClient;
Handler.SSLOptions.SSLVersions := [sslvTLSv1_2];
Handler.PassThrough := False;
AIOHandler := Handler;
end;
Warning
A custom IO handler is used exactly as you configure it. The client does not
set any of its properties or hook any of its events, so configure everything
the connection needs — TLS versions, certificate verification, and library
paths — inside OnCreateIOHandler.
TLS in browser applications
TTMSFNCWebsocketClient is available for VCL and FMX. In TMS WEB Core browser
applications, use TMS WEB Core's own TWebSocketClient component, where the
browser performs TLS. A page served over http may open ws or wss
connections, but a page served over https may only open wss connections;
set UseSSL to True on the web client to force a secure connection from an
http page. The one component of this product that runs in the browser,
TTMSFNCWhatsAppReceiver, always connects
securely.
Combining a custom IO handler with a secure connect
A typical OpenSSL 3.x setup combines both examples in this chapter: assign
OnCreateIOHandler at design time or in FormCreate, then connect with
UseSSL enabled and handle a failing TLS negotiation separately from an
unreachable server. The handler from the previous example is created on the
first Connect and reused by every reconnect.
uses
IdSSLOpenSSLHeaders;
procedure TForm1.FormCreate(Sender: TObject);
begin
// Supplies the TLS handler (see TMSFNCWebsocketClient1CreateIOHandler).
TMSFNCWebsocketClient1.OnCreateIOHandler := TMSFNCWebsocketClient1CreateIOHandler;
TMSFNCWebsocketClient1.HostName := 'push.example.com';
TMSFNCWebsocketClient1.Port := 443;
TMSFNCWebsocketClient1.UseSSL := True;
end;
procedure TForm1.ConnectBtnClick(Sender: TObject);
begin
try
// The first Connect creates the handler; later connects reuse it.
TMSFNCWebsocketClient1.Connect;
if TMSFNCWebsocketClient1.Active then
StatusLabel.Text := 'Connected over TLS';
except
on E: EIdOpenSSLError do
StatusLabel.Text := 'TLS negotiation failed: ' + E.Message;
on E: Exception do
StatusLabel.Text := 'Server unreachable: ' + E.Message;
end;
end;
Pitfalls
- Missing libraries fail at connect time. The application compiles and
starts without OpenSSL; the error only appears on the first secure
Connect. Ship the libraries with the application. - Match the bitness. A 64-bit application cannot load 32-bit OpenSSL libraries, and vice versa.
- The handler is reused.
OnCreateIOHandlerfires on everyConnect, andAIOHandlerstill holds the handler from the previous connection; create a new one only when it isnil. - Do not give the handler an owner. The client frees the handler it received when it is destroyed; a handler that is also owned by the form is freed twice.
- Set
UseSSLbefore connecting. It cannot change while connected.
Related API
See also
- Connecting — addressing, handshake, and the connection lifecycle.
- Server secure connections — certificates and TLS on the server side.
- WhatsApp Receiver — a client that always connects securely.