SharkSSL C/C++ Reference
TLS, cryptography, certificates, and PSA integration
WebSocket Client Lib

Detailed Description

A compact WebSocket client library.

The client WebSocket library enables you to establish a persistent bi-directional communication channel with any WebSocket enabled application/web server.

See also
Minnow Server (WebSocket Server)

WebSocket is a frame based protocol requiring some logic for managing the connections and typically requires a full HTTPS client library. This client WebSocket library includes all logic required, including a minimalistic HTTPS client library, for establishing and maintaining a WebSocket connection in a very compact package. In order to keep the HTTPS client library at a minimum (only a few code lines), the logic in our WebSocket library makes a few assumptions. In particular, read the limitations in the documentation for the WebSocket handshake function (wscProtocolHandshake).

The WebSocket specification: http://tools.ietf.org/html/rfc6455

The following diagram shows the WebSocket frame header:

RFC6455: 5.2. Base Framing Protocol

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
      8 7 6 5 4 3 2 1 8 7 6 5 4 3 2 1 8 7 6 5 4 3 2 1 8 7 6 5 4 3 2 1
     +-+-+-+-+-------+-+-------------+-------------------------------+
     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
     | |1|2|3|       |K|             |                               |
     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
     |     Extended payload length continued, if payload len == 127  |
     + - - - - - - - - - - - - - - - +-------------------------------+
     |                               |Masking-key, if MASK set to 1  |
     +-------------------------------+-------------------------------+
     | Masking-key (continued)       |          Payload Data         |
     +-------------------------------- - - - - - - - - - - - - - - - +
     :                     Payload Data continued ...                :
     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
     |                     Payload Data continued ...                |
     +---------------------------------------------------------------+

Example:

The following code snippet is from WsEchoClient.c. The code example shows how to use the client WebSocket library for connecting to the echo server echo.websocket.org.

xprintf(("Connecting to " WSHOST "...\n"));
/* Port 443 is the listen port for secure servers i.e. HTTPS */
#ifdef WSC_NONSEC
status=se_connect(&sock, WSHOST, 80);
#else
status=se_connect(&sock, WSHOST, 443);
#endif
if(status)
{
const char* msg;
switch(status)
{
case -1: msg="Socket error!";
break;
case -2: msg="Cannot resolve IP address for " WSHOST ".";
break;
default: msg="Cannot connect to " WSHOST ".";
}
"%s\n%s",
msg,
status == -1 ? "" :
"Note: this example is not designed to connect via a HTTP proxy.\n"));
return ;
}
/* It is common to create one (or several) SharkSsl object(s) at
system start and to keep these objects for the lifetime of the
program/firmware.
*/
#ifndef WSC_NONSEC
SharkSsl_Client, /* Two options: client or server */
0, /* Not using SSL cache */
4000, /* initial inBuf size: Can grow */
4000); /* outBuf size: Fixed */
/* Enable server certificate validation. See Ref: CA-LIST
*/
#ifdef ECHO_EX
SharkSsl_setCAList(&sharkSsl, sharkSslCAList);
#else
SharkSsl_setCAList(&sharkSsl, sharkSSL_New_RTL_ECC_CA);
#endif
if( (sharkSslCon = SharkSsl_createCon(&sharkSsl)) == 0)
xprintf(("Cannot create SharkSslCon object.\n"));
else /* We are now connected to the server. */
#else /* WSC_NONSEC */
sharkSslCon=0;
#endif /* WSC_NONSEC */
{
wss.scon=sharkSslCon;
setChaChaCipher(sharkSslCon);
/* Establish a WS connection */
if( (status=wscProtocolHandshake(&wss,6000,WSHOST,WSURI,0)) > 0 )
{
U8 sbuf[255];
int sbufIx=0; /* sbuf cursor */
U8* rbuf; /* Receive buffer is managed by SharkSSL */
int idleCounter=0;
if(sharkSslCon && status !=
#else
#endif
)
{
/* See Ref: CA-LIST */
xprintf(("%cWARNING: certificate received from %s not trusted!\n",
7,WSHOST));
}
#ifdef ECHO_EX
xprintf(("\n------\nConnected\nEnter data and press the ENTER key\n"));
#endif
while((rc = wscRead(&wss,&rbuf,50)) >= 0)
{
if(rc) /* incomming data from server */
{
idleCounter=0;
#ifdef ECHO_EX
xprintf(("Received %d bytes from server:\n",wss.frameLen));
#endif
do
{
int len=rc;
while(len--)
xprintf(("%c", *rbuf++));
if(wss.bytesRead == wss.frameLen)
break; /* We are done receiving the current frame */
} while( (rc=wscRead(&wss,&rbuf,10000)) > 0 );
#ifdef ECHO_EX
xprintf(("\nEnd WS frame.\n"));
#endif
if(rc <= 0) break;
}
else /* 50 ms timeout */
{
int c;
/* Check if we have console data i.e. if user
* entered text into the console. */
while((c=pollkb())!=0)
{
#ifndef UNIXMODE
xprintf(("%c",c));
#endif
sbuf[sbufIx++] = (U8)c;
/* Flush on ENTER or if buffer is full */
if(c == '\n' || sbufIx == sizeof(sbuf))
{
/* Send console data to server */
rc = wscSendBin(&wss,sbuf,sbufIx);
sbufIx=0;
idleCounter=0;
if(c != '\n')
xprintf(("\n"));
break;
}
}
if(rc < 0) break;
}
if(rc == 0)
{
if(++idleCounter == 100) /* 50ms * 100: 5 sec */
{
static const U8 msg[]={"Are you still there?"};
idleCounter=0;
/* There are no WS requirements for sending
* pings. This is just an example. (Ref-Ping). Note,
* ping payload data is not required.
*/
rc=wscSendCtrl(&wss,WSOP_Ping,msg,sizeof(msg)-1);
if(rc < 0) break;
}
}
}
}
/* Release resources used by sharkSslCon */
#ifndef WSC_NONSEC
SharkSsl_terminateCon(&sharkSsl, sharkSslCon);
#endif
}
#ifndef WSC_NONSEC
SharkSsl_destructor(&sharkSsl);
#endif
se_close(&sock);
SharkSslCon * SharkSsl_createCon(SharkSsl *o)
Create a SharkSslCon object.
SHARKSSL_API void SharkSsl_destructor(SharkSsl *o)
Close the SharkSsl object.
#define SharkSsl_setCAList(o, caList)
Install an application-trusted CA list without validating its allocation extent.
Definition: SharkSSL.h:1552
void SharkSsl_terminateCon(const SharkSsl *o, SharkSslCon *con)
Terminate a SharkSslCon object created by function SharkSsl_createCon.
SHARKSSL_API void SharkSsl_constructor(SharkSsl *o, SharkSsl_Role role, U16 cacheSize, U16 inBufStartSize, U16 outBufSize)
A SharkSsl object is the coordinator for managing SharkSslCon objects.
#define SHARKSSL_CHECK_DATE
SharkSslCon_trusted also checks certificate expiration and returns SharkSslConTrust_CertCnDate if dat...
Definition: SharkSSL_cfg.h:988
@ SharkSsl_Client
Definition: SharkSSL.h:304
@ SharkSslConTrust_CertCnDate
This value is returned instead of SharkSslConTrust_CertCn when a certificate is trusted and the Shark...
Definition: SharkSslEx.h:151
@ SharkSslConTrust_CertCn
The peer's SSL certificate is trusted and the subject's common name matches the host name of the URL.
Definition: SharkSslEx.h:142
#define WSOP_Ping
RFC6455 5.5 - Control Frame - Ping.
Definition: WsClientLib.h:73
int wscProtocolHandshake(WscState *wss, U32 tmo, const char *host, const char *path, const char *origin)
Upgrades (morphs) an HTTPS request/response pair to a WebSocket connection.
int wscSendCtrl(WscState *wss, U8 opCode, const U8 *buf, int len)
Sends a WebSocket control frame.
int wscRead(WscState *wss, U8 **buf, U32 timeout)
Wait for WebSocket frames sent by the server.
int wscSendBin(WscState *wss, U8 *buf, int len)
Sends binary data to server.
void se_close(SOCKET *sock)
Close a connected socket connection.
int se_connect(SOCKET *sock, const char *address, U16 port)
Initializes a SOCKET object connected to a remote host/address at a given port.
#define xprintf(data)
The macro xprintf expands to function _xprintf if the code is compiled with XPRINTF set to 1.
Definition: selib.h:299

Modules

 WebSocket Opcodes
 WebSocket Opcodes.
 

Data Structures

struct  WscState
 WebSocket Client State Information, initialize using: WscState wss={0}; however, several members must be set. More...
 

Macros

#define WSC_TLS_AUTH_FAILED   -105
 TLS peer certificate or reference name was not fully authenticated. More...
 

Functions

int wscProtocolHandshake (WscState *wss, U32 tmo, const char *host, const char *path, const char *origin)
 Upgrades (morphs) an HTTPS request/response pair to a WebSocket connection. More...
 
int wscSendBin (WscState *wss, U8 *buf, int len)
 Sends binary data to server. More...
 
int wscSendCtrl (WscState *wss, U8 opCode, const U8 *buf, int len)
 Sends a WebSocket control frame. More...
 
int wscClose (WscState *wss, int statusCode)
 Sends a WebSocket close control frame to the server and closes the connection. More...
 
int wscRead (WscState *wss, U8 **buf, U32 timeout)
 Wait for WebSocket frames sent by the server. More...
 

Macro Definition Documentation

◆ WSC_TLS_AUTH_FAILED

#define WSC_TLS_AUTH_FAILED   -105

TLS peer certificate or reference name was not fully authenticated.

Function Documentation

◆ wscClose()

int wscClose ( WscState *  wss,
int  statusCode 
)

Sends a WebSocket close control frame to the server and closes the connection.

Attempts the close frame and closes the socket.

Parameters
wssWscState *; required connected WebSocket client state with its socket/send buffer initialized.
statusCodeint; WebSocket close status to encode when nonnegative; negative values represent a local failure.

An application-owned SharkSslCon must still be terminated separately.

Returns
int: statusCode if negative, otherwise -statusCode. Zero stays zero. This is not a report of whether sending the close frame succeeded.

◆ wscProtocolHandshake()

int wscProtocolHandshake ( WscState *  wss,
U32  tmo,
const char *  host,
const char *  path,
const char *  origin 
)

Upgrades (morphs) an HTTPS request/response pair to a WebSocket connection.

Sends the HTTP request header to the server and validates the server's HTTP response header – the function simulates a very basic HTTP client library. The function is designed to be as simple as possible and the code is, for this reason, making a few assumptions that could fail when used with a non traditional HTTP server. Read the comments in the source code file WsClientLib.c if you should experience problems.

Parameters
wssthe WebSocket Client State information is stored in this structure. All WscState members must be initially initialized to zero and then the following members must be set: WscState::scon, WscState::sock. When in non secure mode, the following members must be set: WscState::sock, WscState::recBuf, WscState::sendBuf, WscState::recBufLen, WscState::sendBufLen.
tmoin milliseconds. The timeout can be set to INFINITE_TMO.
hostis the server's host name
pathis the path component of the wss URL and the path must be to the server's WebSocket service.
originsome WebSocket server's may require an origin URL: http://tools.ietf.org/html/rfc6455#section-10.2. Set the parameter to NULL if it's not required by the server. The Origin header should only be required by a server when the request is sent from a browser.
Returns
Positive on success: full TLS trust, or 1 without TLS. Zero or a negative value means the handshake did not complete or the TLS peer was not authenticated (WSC_TLS_AUTH_FAILED). Configure a trusted CA list before calling this function in TLS mode; enable SHARKSSL_CHECK_DATE with a trusted clock to validate certificate dates. Status 101, Upgrade/Connection tokens, and the accept value are checked. Initialize the cryptographic RNG before sending any frames, including in plain-socket mode. Each frame obtains a fresh masking key from sharkssl_rng; RNG failure sends nothing. TinyMT is not supported by this client. The standalone client retains a fixed HTTP handshake nonce. It does not validate UTF-8 or support fragmented messages or payload lengths above 65535.

◆ wscRead()

int wscRead ( WscState *  wss,
U8 **  buf,
U32  timeout 
)

Wait for WebSocket frames sent by the server.

The function returns when data is available or on timeout. The function returns zero on timeout, but the peer can send zero length frames so you must verify that it is a timeout by checking the status of WscState::isTimeout.

The WebSocket protocol is frame based, but the function can return a fragment before the complete WebSocket frame is received if the frame sent by the peer is larger than the SharkSSL receive buffer. The frame length is returned in WscState::frameLen and the data consumed thus far is returned in WscState::bytesRead. The complete frame is consumed when frameLen == bytesRead.

Parameters
wssis the WebSocket state.
bufis a pointer set to the SharkSSL receive buffer offset to the start of the WebSocket payload data.
timeoutin milliseconds. The timeout can be set to INFINITE_TMO.
Returns
The payload data length or zero for zero length frames and timeout. The function returns a negative value on error. An incomplete control frame that reaches the read timeout closes with status 1001. Ordinary data frames may continue after a timeout.

◆ wscSendBin()

int wscSendBin ( WscState *  wss,
U8 *  buf,
int  len 
)

Sends binary data to server.

The function sets the WS frame header's opcode to binary. The WS protocol supports two payload frame types, UTF8 and binary (RFC6455: 5.6 Data Frames). We are assuming that you will be using the binary protocol for all data exchange.

Parameters
wssWscState *; required connected WebSocket client state with its socket/send buffer initialized.
bufU8 *; payload borrowed for this call; valid for len bytes. Requires an initialized cryptographic RNG. Each frame uses a fresh masking key; RNG failure sends nothing. An incomplete transport write closes the socket.
lenint; payload length from 0 to 65535; payload plus the 6-byte header (len<=125) or 8-byte header (larger payload) must fit the send buffer.
Returns
int: the complete framed byte count, or a negative error.

◆ wscSendCtrl()

int wscSendCtrl ( WscState *  wss,
U8  opCode,
const U8 *  buf,
int  len 
)

Sends a WebSocket control frame.

The code is used internally by the WebSocket functions. You can also use this function to send your own control frames such as WSOP_Ping.

See RFC6455: 5.5. Control Frames

Parameters
wssWscState *; required connected WebSocket client state with its socket/send buffer initialized.
opCodeU8; a control-frame opcode: WSOP_Close, WSOP_Ping, or WSOP_Pong.
bufconst U8 *; payload borrowed for this call; required when len is nonzero.
lenint; control payload byte count from 0 to 125; frame must fit the send buffer. Requires an initialized cryptographic RNG for a fresh masking key. RNG failure sends nothing; an incomplete transport write closes the socket.
Returns
int: len+6 after the complete frame is sent, or a negative error.