A Secure WebSocket client example that shows how to use the WebSocket library in SharkSSL-Lite

Dependencies:   EthernetInterface SharkSSL-Lite mbed-rtos mbed

The example connects to an online echo service and requires that you connect a terminal via the ''mbed Serial Port'. Windows users must install the Windows serial port driver.

The following screenshot shows Putty connected to the mbed serial terminal and after entering a few lines of text.

/media/uploads/wini/websocket-example.png

WsEchoClient.cpp

Committer:
wini
Date:
2016-04-06
Revision:
0:3a00a9689a7e

File content as of revision 0:3a00a9689a7e:

/**
 *     ____             _________                __                _
 *    / __ \___  ____ _/ /_  __(_)___ ___  ___  / /   ____  ____ _(_)____
 *   / /_/ / _ \/ __ `/ / / / / / __ `__ \/ _ \/ /   / __ \/ __ `/ / ___/
 *  / _, _/  __/ /_/ / / / / / / / / / / /  __/ /___/ /_/ / /_/ / / /__
 * /_/ |_|\___/\__,_/_/ /_/ /_/_/ /_/ /_/\___/_____/\____/\__, /_/\___/
 *                                                       /____/
 *
 *                 SharkSSL Embedded SSL/TLS Stack
 ****************************************************************************
 *   PROGRAM MODULE
 *
 *   $Id: WsEchoClient.c 3616 2014-12-03 00:40:53Z wini $
 *
 *   COPYRIGHT:  Real Time Logic LLC, 2016
 *
 *   This software is copyrighted by and is the sole property of Real
 *   Time Logic LLC.  All rights, title, ownership, or other interests in
 *   the software remain the property of Real Time Logic LLC.  This
 *   software may only be used in accordance with the terms and
 *   conditions stipulated in the corresponding license agreement under
 *   which the software has been supplied.  Any unauthorized use,
 *   duplication, transmission, distribution, or disclosure of this
 *   software is expressly forbidden.
 *
 *   This Copyright notice may not be removed or modified without prior
 *   written consent of Real Time Logic LLC.
 *
 *   Real Time Logic LLC. reserves the right to modify this software
 *   without notice.
 *
 *               http://sharkssl.com
 ****************************************************************************


Secure WebSocket Example

WebSocket (WS) is a new standard enabling full duplex asynchronous
communication between a web server and a client and vice versa. WS can
be used as a base for M2M communication. See the following page for an
introduction to the protocol: http://en.wikipedia.org/wiki/WebSocket

The example is by default connecting to the WebSocket echo service at
realtimelogic.info. Compiling the code with ECHO_EX makes the example
connect to echo.websocket.org.

The WebSocket service at realtimelogic.info can respond to both RSA
and ECC clients, but the echo service echo.websocket.org will only
respond with an RSA certificate. For this reason, connecting to
echo.websocket.org will fail if you have compiled SharkSSL with the
option to exclude RSA and to only include support for ECC.

*/

#include "WsClientLib.h"
#include <mbed.h>
#include <stdarg.h>

/* 
    The #WSHOST and #WSURI macros must point to your WebSocket server.
*/
#ifdef ECHO_EX
#define WSHOST "echo.websocket.org"
#define WSURI "/"
#else
#define WSHOST "realtimelogic.info"
#define WSURI "/WS-ELIZA/"
#endif


/************************* Helper functions ******************************/

Serial pc(USBTX, USBRX); // tx, rx

/* Example code and selib.c use function xprintf.
 */
void _xprintf(const char* fmt, ...)
{
   va_list varg;
   va_start(varg, fmt);
   vprintf(fmt, varg);
   va_end(varg);
} 


/**************************************************************************
The following code is designed specifically for this example and
enables non blocking read from the console. The example's main loop is
single threaded and we most therefore use non blocking functions for
reading from the console.
***************************************************************************/


/* Function pollkb requires non blocking keyboard I/O.
 */

/* Platform specific function for non blocking keyboard read.
 */
static int
pollkb(void)
{
   if(pc.readable())
   {
      int c = pc.getc();
      return c=='\r' ? '\n' : c;
   }
   return 0;
}



/************ End non blocking console functions ***********************/



/*
  The main function connects to a WS echo server, by using the generic
  WS functions defined above.

  The function connects to the server defined by the macro #WSHOST. See
  WsClientLib.h for details.
*/
void
mainTask(SeCtx* ctx)
{
   /* Info printed to the console when the program starts
    */
   static const char info[] = {
      "SharkSSL Websocket client demo.\n"
      "Copyright (c) 2016 Real Time Logic.  All rights reserved.\n"
      "Connecting to wss://" WSHOST
   };

   SharkSsl sharkSsl;
   SharkSslCon* sharkSslCon;
   SOCKET sock;
   int rc,status;

   WscReadState wss={0};

   xprintf(("%s",info));
   xprintf(("y\n\n"));
   xprintf(("Connecting to " WSHOST "...\n"));
   /* Port 443 is the listen port for secure servers i.e. HTTPS */
   status=se_connect(&sock, WSHOST, 443);
   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 ".";
      }
      xprintf((
         "%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.
    */
   SharkSsl_constructor(&sharkSsl,
                        SharkSsl_Client, /* Two options: client or server */
                        0,      /* Not using SSL cache */
                        4000,   /* initial inBuf size: Can grow */
                        4000);   /* outBuf size: Fixed */
   
   /* It is very important to seed the SharkSSL RNG generator (Ref-seed) */
   sharkssl_entropy(baGetUnixTime() ^ (U32)&sharkSsl);

   if( (sharkSslCon = SharkSsl_createCon(&sharkSsl)) == 0)
      xprintf(("Cannot create SharkSslCon object.\n"));
   else /* We are now connected to the server. */
   {
      /* Keep seeding (Make it more secure: Ref-seed) */
      sharkssl_entropy(baGetUnixTime() ^ (U32)&sharkSsl);
      /* Establish a WS connection */
      if( ! wscProtocolHandshake(&wss, sharkSslCon, &sock,6000,WSHOST,WSURI,0))
      {
         U8 sbuf[255];
         int sbufIx=0; /* sbuf cursor */
         U8* rbuf; /* Receive buffer is managed by SharkSSL */
         int idleCounter=0;
#ifdef ECHO_EX
         xprintf(("\n------\nConnected\nEnter data and press the ENTER key\n"));
#endif
         while((rc = wscRead(&wss,sharkSslCon,&sock,&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,sharkSslCon,&sock,&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)
               {
                  xprintf(("%c",c));
                  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(sharkSslCon,&sock,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(
                     sharkSslCon,&sock,WSOP_Ping,msg,sizeof(msg)-1);
                  if(rc < 0) break;
               }
            }
         }
      }
      /* Release resources used by sharkSslCon */
      SharkSsl_terminateCon(&sharkSsl, sharkSslCon);
   }

   SharkSsl_destructor(&sharkSsl);
   se_close(&sock);
   /*! [inline doc] */
   xprintf(("\nServer connection closed!"));
}