SharkSSL™ Embedded SSL/TLS Stack
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
/* It is very important to seed the SharkSSL RNG generator (Ref-seed) */
sharkssl_entropy(baGetUnixTime() ^ (ptrdiff_t)&sharkSsl);
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);
/* Keep seeding (Make it more secure: Ref-seed) */
#ifndef WSC_NONSEC
sharkssl_entropy(baGetUnixTime() ^ (ptrdiff_t)&sharkSsl);
#endif
/* 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.
SHARKSSL_API U8 SharkSsl_setCAList(SharkSsl *o, SharkSslCAList caList)
Set a Certificate Authority (CA) list so the SharkSSL object can permform certificate validation on t...
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:641
U32 baGetUnixTime(void)
Platform dependent function returning seconds since Jan 1 1970.
@ SharkSsl_Client
Definition: SharkSSL.h:295
@ SharkSslConTrust_CertCnDate
This value is returned instead of SharkSslConTrust_CertCn when a certificate is trusted and the Shark...
Definition: SharkSslEx.h:148
@ 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:138
#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:261

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...
 

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...
 

Function Documentation

◆ wscClose()

int wscClose ( WscState wss,
int  statusCode 
)

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

Parameters
wssis the WebSocket state.
statusCodeis a WebSocket status code.

◆ 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
Zero success.

◆ 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.

◆ 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.

◆ 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