# WebSocket Client

The IWebsocketClient interface is for working with WebSocket connections. While WebSocket is an update to an existing HTTP connection, there is no need to pass an existing HTTP connection to IWebsocketClient. Everything will handled from the interface itself. To establish a connection, the address of the WebSocket server need to be passed with the leading ws:// prefix. If the connection need to be encrypted, wss:// should be used instead. The server can be passed as TCP (v4 or v6) address or as name, the IWebsocketClient tries to resolve. Note that the IWebsocketClient only handles the client side of the connection. For implement a server-side WebSocket connection, see IWebserverPlugin - IWebsocket.

To create an IWebsocketClient instance, the static Create() function need to be called. Two existing ISocketProvider as well as an IDns instance must be passed to Create(). Those interfaces must not be deleted before releasing the IWebsocketClient instance!

## File information

File | common/interface/websocket_client.h
Classes | IWebsocketClient UWebsocketClient
Data types | IWebsocketClient::closereason_t WEBSOCKET_MAX_PAYLOAD_SIZE
Examples | IWebsocketClient

# Classes

## IWebsocketClient

    class IWebsocketClient {
    public:
        static IWebsocketClient * Create(class IIoMux * const iomux,
                                         class UWebsocketClient * const user,
                                         class ISocketProvider * const tcpSocketProvider,
                                         class ISocketProvider * const tlsSocketProvider,
                                         class IDns * const dns,
                                         class IInstanceLog * const log);

        virtual ~IWebsocketClient() {}
        virtual void Connect(const char * uri);
        virtual void Close();
        virtual void Send(const void * buf, size_t len, bool text = true);
        virtual void Recv(void * buf = nullptr, size_t len = 0);
    };

### Overview

This is the interface to establish a WebSocket connection. The interface is quite simple and straight forward to use.

### Logging

To enable logging for IWebsocketClient, the flag LOG_WEBSOCKET_CLIENT must be set in the managers diagnostic settings.

### Public functions

Create (static function)
  This static function creates the IWebsocketInstance. The returned instance must be release if no longer needed by using the C++ delete operator.

  #### Parameters

  class IIoMux * const iomux | The IIoMux instance the IWebsocketClient instance will be registered to.
  class UWebsocketClient * const user | The UWebsocketClient instance that will receive the callbacks of IWebsocketClient.
  class ISocketProvider * const tcpSocketProvider | A ISocketProvider instance that can create a TCP ISocket.
  class ISocketProvider * const tlsSocketProvider | A ISocketProvider instance that can create a TLS ISocket.
  class IDns * const dns | The IDns instance used to resolve the server name if needed.
  class IInstanceLog * const log | The IInstanceLog instance used for logging purposes.

  #### Return value

  The IWebsocketClient instance created. It must be freed by the C++ operator delete, if no longer be used.

  #### Remarks

  Note that the two ISocketProvider instances as well as the IDns instance must not be freed before the IWebsocketClient instance had been deleted.
Connect
  Tries to connect to the given address. The address must be given in the default WebSocket url format. That means "ws://<address>" for a normal connection, "wss://<address>" for an encrypted one. The IWebsocketClient can handle IPv4 and IPv6 addresses, as well as a server name which will be resolved using the IDns instance.

  #### Parameters

  const char * uri | The address to connect to.

  #### Callbacks

  On success, UWebsocketClient::WebsocketConnectComplete() will be called. If an error occurs, UWebsocketClient::WebsocketCloseComplete() will be called. Details of that error can be found inside the log, if logging for the WebSocket client is activated. Reason can be one of the following values:

  WSCR_CONNECTION_FAILED | An error occurred while trying to connect to the WebSocket server. Details of that error can be found inside the log, if logging for the WebSocket client is enabled.
  WSCR_URI_INVALID | The address, passed to IWebsocketClient::Connect(), has a wrong format.

Close
  Closes the WebSocket connection. If there is any data still cached and need to be sent, the data will be send before closing the connection.

  #### Callbacks

  After the connection had been closed, UWebsocketClient::WebsocketCloseComplete() will be called.
Send
  Sends the given data to the WebSocket server, IWebsocketClient is connected to. If the data could not be send right now, it will be cached and send later. The UWebsocketClient instance will be informed if the data had been finally send.

  #### Parameters

  const void * buf | The buffer with the data to send. It must not be nullptr.
  size_t len | The number of bytes to send. The in buf given buffer must have at least a size of len bytes.
  bool text | (Default true) If set to false, the data will be send as binary data, else as text data.

  #### Callbacks

  After the data had been send, UWebsocketClient::WebsocketSendResult() will be called.
Recv
  Tells the IWebsocketClient instance to receive data. The data will be written to the given buffer. The WebSocket client will read data to the given buffer, until the number of bytes passed had been received. Although the IWebsocketClient won't support partial read, at least a nullptr (and a len of 0) can be passed to Recv(). This leads to a callback to ask for a buffer with the size needed. Doing this allows to make some kind of partial read and comes in handy, if the size of data to receive is unknown. See UWebsocketClient::WebsocketRecvBuffer for more information.

  #### Parameters

  void * buf | (Default nullptr) The buffer to read the data to.
  size_t len | (Default 0) The number of bytes to receive. The in buf given buffer must have at least a size of len bytes. This parameter will be ignored if buf = nullptr.

  #### Callbacks

  On success, UWebsocketClient::WebsocketRecvResult() will be called after data had been received. If an error occurs, UWebsocketClient::WebsocketCloseComplete() will be called. Details of that error can be found inside the log, if logging for the WebSocket client is activated. Reason can be one of the following values:

  WSCR_BUFFER_OVERFLOW | A WebSocket frame had been received, that exceeded the internal limit of frame size. See WEBSOCKET_MAX_PAYLOAD_SIZE for details.
  WSCR_PROTOCOL_ERROR | Something went wrong during the WebSocket communication. Details of that error can be found inside the log, if logging for the WebSocket client is enabled.

  #### Remarks

  When calling Recv with a buffer and a len, IWebsocketClient will wait until the requested amount of bytes had been received. So if the size of data to receive is unknown, it will be better to call Recv() with nullptr as buffer and use the UWebsocketClient::WebsocketRecvBuffer() mechanism to receive the data.

## UWebsocketClient

    class UWebsocketClient {
    public:
        virtual ~UWebsocketClient() {}
        virtual void WebsocketConnectComplete(IWebsocketClient * const websocket);
        virtual void WebsocketCloseComplete(IWebsocketClient * const websocket, closereason_t reason) ;
        virtual void WebsocketSendResult(IWebsocketClient * const websocket);
        virtual void WebsocketRecvResult(IWebsocketClient * const websocket, void * buf, size_t len, bool text, bool isFragment);
        virtual void * WebsocketRecvBuffer(size_t len) { return nullptr; }
    };

### Overview

The UWebsocketClient class is used to receive callbacks from an IWebsocketClient instance. An application must subclass UWebsocketClient, implement the functions that must be implemented and pass that class as user to IWebsocketClient::Create(). The instance of that subclass must not be freed before the IWebsocketClient instance assigned to. One UWebsocketClient instance can be assigned to multiple IWebsocketClient instances, because the calling IWebsocketClient will be passed as parameter to the callback functions.

### Public functions

WebsocketConnectComplete
  Will be called, after calling IWebsocketClient::Connect() and after the connection had been established. After receiving that callback, the IWebsocketClient instance can be used to send and receive data.

  #### Parameters

  class IWebsocketClient * const websocket | The calling IWebsocketClient instance.

WebsocketCloseComplete
  Will be called after the WebSocket connection had been closed. That can be because of a call to IWebsocketClient::Close(), because the other side closed the connection or because of an error. The reason for the close will be passed as parameter.

  #### Parameters

  class IWebsocketClient * const websocket | The calling IWebsocketClient instance.
  closereason_t reason | The reason why the WebSocket connection had been closed. See IWebsocketClient::closereason_t for more details.

WebsocketSendResult
  When calling IWebsocketClient::Send(), after sending the data UWebsocketClient::WebsocketSendResult() will be called. It is guaranteed, that the callback would not be called before returning from IWebsocketClient::Send().

  #### Parameters

  class IWebsocketClient * const websocket | The calling IWebsocketClient instance.

WebsocketRecvResult
  After calling IWebsocketClient::Recv(), UWebsocketClient::WebsocketRecvResult() will be called when data had been received. Because WebSocket messages can be fragmented, that information will be passed to the callback function by the isFragmented flag. So to make sure that the whole message had been received, the application should call IWebsocketClient::Recv() until UWebsocketClient::WebsocketRecvResult() will be called with isFragmented set to false.

  #### Parameters

  class IWebsocketClient * const websocket | The calling IWebsocketClient instance.
  void * buf | The buffer holding the data received. Will be the same as passed to IWebsocketClient::Recv() or returned by UWebsocketClient::WebsocketRecvResult().
  size_t len | The length of the data inside the buffer.
  bool isFragemented | If true, the data is fragmented. That means, that there are still other parts of the data to receive. So isFragmented set to false can also interpreted as "all data received".

# Data types

## UWebsocketClient::closereason_t

### Overview

When the web socket connection had been closed, UWebsocketClient::WebsocketCloseComplete() will be called with the reason for closing the connection. Note that the datatype is embedded in the UWebsocketClient class.

#### Values

WSCR_NORMAL_CLOSE | The connection had been closed because of a call to IWebsocketClient::Close().
WSCR_CONNECTION_FAILED | An error occurred while trying to connect to the WebSocket server. Details of that error can be found inside the log, if logging for the WebSocket client is enabled.
WSCR_URI_INVALID | The address, passed to IWebsocketClient::Connect(), has a wrong format.
WSCR_BUFFER_OVERFLOW | A WebSocket frame had been received, that exceeded the internal limit of frame size. See WEBSOCKET_MAX_PAYLOAD_SIZE for details.
WSCR_PROTOCOL_ERROR | Something went wrong during the WebSocket communication. Details of that error can be found inside the log, if logging for the WebSocket client is enabled.
WSCR_SOCKET_LOST | The socket got lost unexpected. Details of that error can be found inside the log, if logging for the WebSocket client is enabled.
WSCR_SERVER_CLOSED | The socket connection had been close from the server side.

## WEBSOCKET_MAX_PAYLOAD_SIZE (define)

### Overview

While the IWebsocketClient supports the full WebSocket protocol, the size of one WebSocket frame is limited. This limit is defined by WEBSOCKET_MAX_PAYLOAD_SIZE. If a frame comes in with a data size larger than WEBSOCKET_MAX_PAYLOAD_SIZE, UWebsocketClose::WebsocketCloseComplete() will be called with reason set to WSCR_BUFFER_OVERFLOW.

#### Values

WEBSOCKET_MAX_PAYLOAD_SIZE | Value in bytes, defined to 16384

# Code Example

## IWebsocketClient

    app::app(class IIoMux * iomux,
             ISocketProvider * tcpSocketProvider,
             ISocketProvider * tlsSocketProvider,
             IDns * dns)
        : iomux(iomux)
    {
        this->websocketClient = IWebsocketClient::Create(iomux, this, tcpSocketProvider, tlsSocketProvider, dns);
        this->websocketClient->Connect("wss://myserver.com/websocket");
    }

    void app::WebsocketConnectComplete(IWebsocketClient * const websocket)
    {
        printf("Websocket connected\n");
    	this->websocketClient->Recv();
    }

    void app::WebsocketCloseComplete(IWebsocketClient * const websocket, closereason_t reason)
    {
        printf("Websocket connection closed\n");
        delete this->websocketClient;
        this->websocketClient = NULL;
        iomux->Terminate();
    }

    void app::WebsocketSendResult(IWebsocketClient * const websocket)
    {
        this->websocketClient->Close();
    }

    void * app::WebsocketRecvBuffer(size_t len)
    {
        return malloc(len);
    }

    void app::WebsocketRecvResult(IWebsocketClient * const websocket, void * buf, size_t len, bool text)
    {
        printf("Websocket client received data (text = %s)\n", (test ? "true" : "false"));
        debug->HexDump(buf, len);
        this->websocketClient->Send(buf, len);
    	free(buf);
    }