Authentication supported from sgcWebSockets 4.3.0 version

Authentication can be enabled to associate a user with each connection and filter which users can access to resources. Authentication is implemented using Bearer Tokens, client provide an access token and server validates this token and uses it to identify then user.

In standard Web APIs, bearer tokens are sent in an HTTP Header, but when using websockets, token is transmitted as a query string parameter.

The following methods are supported:

srcaRequestToken

If Authentication is enabled, the flow is:

  1. First tries to get a valid token from server. Opens an HTTP connection against Authentication.RequestToken.URL and do a POST using User and Password data.

  2. If previous is successful, a token is returned. If not, an error is returned.

  3. If token is returned, then opens a new HTTP connection to negotiate a new connection. Here, token is passed as an HTTP Header.

  4. If previous is successful, opens a websocket connection and pass token as query string parameter.

 

Authentication.Enabled: if active, authorization will be used before a websocket connection is established.

Authentication.Username: the username provided to server to authenticate.

Authentication.Password: the secret word provided to server to authenticate.

Authentication.RequestToken.PostFieldUsername: name of field to transmit username (depends of configuration, check http javascript page to see which name is used).

Authentication.RequestToken.PostFieldPassword: name of field to transmit password (depends of configuration, check http javascript page to see which name is used).

Authentication.RequestToken.URL: url where token is requested.

Authentication.RequestToken.QueryFieldToken: name of query string parameter using in websocket connection.

 

srcaSetToken

Here, you pass token directly to SignalRCore server (because token has been obtained from another server).

Authentication.Enabled: if active, authorization will be used before a websocket connection is established.

Authentication.SetToken.Token: token value obtained.

 

TsgcWebSocketServer and TsgcWebSocketHTTPServer are based on Indy library, so every connection is handled by a thread, so if you have 1000 concurrent connections, you will have, at least, 1000 threads to handle these connections. When performance is important, you must do some "tweaks" to increase performance and improve server work.

 Use the following tips to increase server performance.

 

1. Set in Server component property NotifyEvents := neNoSync. This means that events are raised in the context of connection thread, so there is no synchronization mechanism. If you must access to VCL controls or shared objects, use your own synchronization mechanisms.

 

2. Set in Server component property Optimizations.Connections.Enabled := True. If you plan to have more than 1000 concurrent connections in your server, and you call Server.WriteData method a lot, enable this property. Basically it saves connections in a cache list where searches are faster than accessing to Indy connections list.

    2.1 CacheSize: is the number of connections stored in a fast cache.

    2.2 GroupLevel: creates internally several lists splitted by first character, so if you have lots of connections, searches are faster.

 

3.  Set in Server component property Optimizations.Channels.Enabled := True. Enabling this property, channels are saved in list where searches are faster than previous method.

 

4.  Set in Server component property Optimizations.ConnectionsFree.Enabled := True. If this property is enabled, every time there is a disconnection, instead of destroy TsgcWSConnection, object is stored in a List and every X seconds, all objects stored in this list are destroyed.

 

5. By default, sgcWebSockets uses Critical Sections to protect access to shared objects. But you can use TMonitor or SpinLocks instead of critical sections. Just compile your project with one of the following compiler defines

    3.1 {$DEFINE SGC_SPINLOCKS}

    3.2 {$DEFINE SGC_TMONITOR}

 

6. Use latest FastMM4, you can download from: https://github.com/pleriche/FastMM4

    FastMM4 is a very good memory manager, but sometimes doesn't scales well with multi threaded applications. Use the following compiler define in your application:

     {$DEFINE UseReleaseStack}

   Then, add FastMM4 as the first unit in your project uses and compile again. For a high concurrent server, you will note an increase in performance.

   This tweak does following: If a block cannot be released immediately during a FreeMem call the block will added to a list of blocks that will be freed later, either in the background cleanup thread or during the next call to FreeMem.

sgcWebSockets supports WebRTC using WebBrowser as client.

sgcWebSockets and WebRTC Demo

 

But there is another option if you don't want to use a WebBrowser as client, you can embed chrome browser in your application and connect to a WebRTC online Server.

This demo uses CEF4Delphi library to embed Chromium-based browsers in a client application to use AppRTC online demo without any webbrowser installed.

More info about CEF4Delphi: https://github.com/salvadordf/CEF4Delphi

Download Demo

By default, sgcWebSockets library use WebSockets as protocol, but from version sgcWebSockets 4.2.0 you can configure library to work as plain TCP Server and Client.

 

Client

There is a property in client component called: Specifications.RFC6455, by default is active. If you disable this property and try to connect to a server, client will use plain TCP protocol.

 Client.Specifications.RFC6455 := False;

 

Server

 

Every time a new connection is created in server, server tries to identify if it's a websocket connection, HTTP request... if a connection can't be identified, by default is closed. You can use OnUnknownProtocol event to accept none websocket connections

 procedure OnUnknownProtocol(Connection: TsgcWSConnection; var Accept: Boolean);

begin

  Accept := True;

end;

WebSocket protocol starts with an HTTP Handshake similar to normal HTTP Request, this handshake has an address request, origin, protocols supported... sometimes is useful to add some custom headers. sgcWebSockets supports customize HandShake Headers using OnHandshake event of Client WebSocket Component. There is a parameter called Headers which is a TStringList that contains a list of headers will be sent to Server, you can modify these headers or add your custom headers.

Example: add header "x-my-header" with value "test"

 

procedure OnClientHandshake(Connection: TsgcWSConnection; var Headers: TStringList);

begin

  Headers.Add('x-my-hader: test');

end;

TsgcWSHTTPWebBrokerBridgeServer use TIdHttpWebBrokerBridge as server base and is useful if you want to use a single server for DataSnap, HTTP and WebSocket connections.

TsgcWSHTTPWebBrokerBridgeServer inherits from TsgcWebSocketHTTPServer, so you can refer to this server.

Follow next steps to replace TIdHttpWebBrokerBridge for TsgcWSHTTPWebBrokerBridgeServer :

 1. Create a new instance of TsgcWSHTTPWebBrokerBridgeServer.

 2. Replace all calls to TIdHttpWebBrokerBridge for TsgcWSHTTPWebBrokerBridgeServer.

 3. To Handle WebSocket connections just refer to TsgcWebSocketHTTPServer.

Upload files to a websocket server is very easy, just send file as binary data. Example of HTML client:

 

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>Upload Files</title>

</head>

<body>

    <h2>File Upload</h2>

    Select file

    <input type="file" id="filename" />

    <br>

    <input type="button" value="Connect" onclick="connectChatServer()" />

    <br>

    <input type="button" value="Upload" onclick="sendFile()" />

    <script>

        var ws;

        function connectChatServer() {

            ws = new WebSocket("ws://127.0.0.1");

            ws.binaryType = "arraybuffer";

            ws.onopen = function() {

                alert("Connected.")

            };

            ws.onmessage = function(evt) {

                alert(evt.msg);

            };

            ws.onclose = function() {

                alert("Connection is closed...");

            };

            ws.onerror = function(e) {

                alert(e.msg);

            }

        }

        function sendFile() {

            var file = document.getElementById('filename').files[0];

            var reader = new FileReader();

            var rawData = new ArrayBuffer();            

            reader.loadend = function() {

            }

            reader.onload = function(e) {

                rawData = e.target.result;

                ws.send(rawData);

                alert("the File has been transferred.")

            }

            reader.readAsArrayBuffer(file);

        }

    </script>

</body>

</html>

Once binary message is received by server, just create a TFileStream and copy binary data. Example of Server:

procedure OnWSServerBinary(Connection: TsgcWSConnection; const Data: TMemoryStream);

var

  oFile: TFileStream;

begin

  oFile := TFileStream.Create('file.dat', fmCreate);

  Try

    oFile.CopyFrom(Data, Data.Size);

  Finally

    oFile.Free;

  End;

end;


 You can download a compiled application which shows how you can upload a file in your server.

 WebSocket Upload File Demo

 

 Read more about our sgcWebSockets components

 sgcWebSockets Components

From sgcWebSockets 4.1.6 default SGC Protocol supports wildcards in Subscribe, UnSubscribe and Publish methods, websocket protocol.

Channel is a string used by server to filter messages for each connected client. A channel can consists of one or more levels, usually each topic is separated by a forward slash. Example

  weather/usa/california/San Francisco/Silicon Valley

 

When a client subscribe to a channel it can use exact channel name or can subscribe to more channels at once by using wildcards. Example:

 

  weather/usa/california/*

  weather/usa/*

 

Wildcards only works for active channels, so you can create some persistent channels before server is started:

 

  WSPPROTOCOL.Subscriptions.Add(NewGuid + '_' + 'weather/usa/california';

  WSPPROTOCOL.Subscriptions.Add(NewGuid + '_' + 'weather/usa';

 

Implemented from sgcWebSockets 4.1.4

Supported by:

  TsgcWebSocketServer

  TsgcWebSocketHTTPServer

  TsgcWebSocketClient

 

WebSocket provides a simple subprotocol negotiation, basically adds a header with protocols name supported by request, this protocols are received and if receiver supports one of them, sends a response with subprotocol supported.

sgcWebSockets supports several SubProtocols: MQTT, WAMP... and more. You can implement your own subprotocols using a very easy method, just call RegisterProtocol and send SubProtocol Name as argument.

Example: you need to connect to a server which implements subprotocol "Test 1.0"

Client.Host := server host;

Client.Port := server.port;

Client.RegisterProtocol('Test 1.0');

Client.Active := True;

 

 

When a client sends a binary message to server, sometimes is useful set an ID to identify binary message when is received. There are 2 functions in sgcWebSocket_Helpers which can be used to set a short description of binary packet, basically adds a header to stream which is used to identify binary packet.

 

Before send a binary message, call sgcWSStreamWrite method to encode stream.

   vID := '00001';

   sgcWSStreamWrite(vID, oStream);

   Client.WriteData(oStream);

 

When binary message is received, call sgcWSStreamRead method to decode stream.

   sgcWSStreamRead(oStream, vID);

 

The only limitation is that text used to identify binary message, has a maximum length of 10 characters (this can be modified if you have access to source code).