Robotran C Documentation
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
Classes | Macros | Functions | Variables
ws.c File Reference

wsServer main routines. More...

#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <utf8.h>
#include <ws.h>

Classes

struct  ws_cli_conn_t
 Client socks. More...
 
struct  ws_frame_data
 WebSocket frame data. More...
 
struct  frame_state_data
 Frame state data. More...
 
struct  ws_accept_params
 Accept parameters. More...
 

Macros

#define _POSIX_C_SOURCE   200809L
 
#define MSG_NOSIGNAL   0
 
#define CLIENT_VALID(cli)
 Client validity macro. More...
 
#define panic(s)
 Issues an error message and aborts the program. More...
 

Functions

void * ws_get_server_context (ws_cli_conn_t *cli)
 Get server context. More...
 
void ws_set_connection_context (ws_cli_conn_t *cli, void *ptr)
 Set connection context. More...
 
void * ws_get_connection_context (ws_cli_conn_t *cli)
 Get connection context. More...
 
static void close_socket (int fd)
 Shutdown and close a given socket. More...
 
static int get_client_state (ws_cli_conn_t *client)
 Returns the current client state for a given client client. More...
 
static int set_client_state (ws_cli_conn_t *client, int state)
 Set a state state to the client index client. More...
 
static ssize_t send_all (ws_cli_conn_t *client, const void *buf, size_t len, int flags)
 Send a given message buf on a socket sockfd. More...
 
static void close_client (ws_cli_conn_t *client, int lock)
 Close client connection (no close handshake, this should be done earlier), set appropriate state and destroy mutexes. More...
 
static void * close_timeout (void *p)
 Close time-out thread. More...
 
static int start_close_timeout (ws_cli_conn_t *client)
 For a valid client index client, starts the timeout thread and set the current state to 'CLOSING'. More...
 
static void set_client_address (ws_cli_conn_t *client)
 Sets the IP address relative to a client connection opened by the server and save inside the client structure. More...
 
char * ws_getaddress (ws_cli_conn_t *client)
 Gets the IP address relative to a client connection opened by the server. More...
 
char * ws_getport (ws_cli_conn_t *client)
 Gets the IP port relative to a client connection opened by the server. More...
 
static int ws_sendframe_internal (ws_cli_conn_t *client, const char *msg, uint64_t size, int type, uint16_t port)
 Creates and send an WebSocket frame with some payload data. More...
 
int ws_sendframe (ws_cli_conn_t *client, const char *msg, uint64_t size, int type)
 Send an WebSocket frame with some payload data. More...
 
int ws_sendframe_bcast (uint16_t port, const char *msg, uint64_t size, int type)
 Send an WebSocket frame with some payload data to all clients connected into the same port. More...
 
static int32_t pong_msg_to_int32 (uint8_t *msg)
 Given a PONG message, decodes the content as a int32_t number that corresponds to our PONG id. More...
 
static void int32_to_ping_msg (int32_t ping_id, uint8_t *msg)
 Given a PING id, encodes the content to be sent as payload of a PING frame. More...
 
static void send_ping_close (ws_cli_conn_t *cli, int threshold, int lock)
 Send a ping message and close if the client surpasses the threshold imposed. More...
 
void ws_ping (ws_cli_conn_t *cli, int threshold)
 Sends a PING frame to the client cli with threshold threshold. More...
 
int ws_sendframe_txt (ws_cli_conn_t *client, const char *msg)
 Sends a WebSocket text frame. More...
 
int ws_sendframe_txt_bcast (uint16_t port, const char *msg)
 Sends a broadcast WebSocket text frame. More...
 
int ws_sendframe_bin (ws_cli_conn_t *client, const char *msg, uint64_t size)
 Sends a WebSocket binary frame. More...
 
int ws_sendframe_bin_bcast (uint16_t port, const char *msg, uint64_t size)
 Sends a broadcast WebSocket binary frame. More...
 
int ws_get_state (ws_cli_conn_t *client)
 For a given client, gets the current state for the connection, or -1 if invalid. More...
 
int ws_close_client (ws_cli_conn_t *client)
 Close the client connection for the given client with normal close code (1000) and no reason string. More...
 
static int is_control_frame (int frame)
 Checks is a given opcode frame belongs to a control frame or not. More...
 
static int is_valid_frame (int opcode)
 Checks is a given opcode opcode is valid or not. More...
 
static int do_handshake (struct ws_frame_data *wfd)
 Do the handshake process. More...
 
static int do_close (struct ws_frame_data *wfd, int close_code)
 Sends a close frame, accordingly with the close_code or the message inside wfd. More...
 
static int do_pong (struct ws_frame_data *wfd, uint64_t frame_size)
 Send a pong frame in response to a ping frame. More...
 
static int next_byte (struct ws_frame_data *wfd)
 Read a chunk of bytes and return the next byte belonging to the frame. More...
 
static int skip_frame (struct ws_frame_data *wfd, uint64_t frame_size)
 Skips frame_size bytes of the current frame. More...
 
static int validate_utf8_txt (struct ws_frame_data *wfd, struct frame_state_data *fsd)
 Validates TXT frames if UTF8 validation is enabled. More...
 
static int handle_pong_frame (struct ws_frame_data *wfd, struct frame_state_data *fsd)
 Handle PONG frames in response to our PING (or not, unsolicited is possible too). More...
 
static int handle_ping_frame (struct ws_frame_data *wfd, struct frame_state_data *fsd)
 Handle PING frames sending a PONG response. More...
 
static int handle_close_frame (struct ws_frame_data *wfd, struct frame_state_data *fsd)
 Handle close frames while checking for UTF8 in the close reason. More...
 
static int read_single_frame (struct ws_frame_data *wfd, struct frame_state_data *fsd)
 Reads the current frame isolating data from control frames. More...
 
static int next_complete_frame (struct ws_frame_data *wfd)
 Reads the next frame, whether if a TXT/BIN/CLOSE of arbitrary size. More...
 
static void * ws_establishconnection (void *vclient)
 Establishes to connection with the client and trigger events when occurs one. More...
 
static void * ws_accept (void *data)
 Main loop that keeps accepting new connections. More...
 
static int do_bind_socket (struct ws_server *ws_srv)
 By using the server parameters provided in ws_srv, create a socket and bind it accordingly with the server configurations. More...
 
int ws_socket (struct ws_server *ws_srv)
 Main loop for the server. More...
 

Variables

static struct ws_connection client_socks [MAX_CLIENTS]
 Clients list. More...
 
static uint32_t timeout
 Timeout to a single send(). More...
 
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
 Global mutex. More...
 

Detailed Description

wsServer main routines.

Macro Definition Documentation

◆ _POSIX_C_SOURCE

#define _POSIX_C_SOURCE   200809L

◆ CLIENT_VALID

#define CLIENT_VALID (   cli)
Value:
((cli) != NULL && (cli) >= &client_socks[0] && \
(cli) <= &client_socks[MAX_CLIENTS - 1] && \
(cli)->client_sock > -1)

Client validity macro.

◆ MSG_NOSIGNAL

#define MSG_NOSIGNAL   0

◆ panic

#define panic (   s)
Value:
do \
{ \
perror(s); \
exit(-1); \
} while (0);

Issues an error message and aborts the program.

Parameters
sError message.

Function Documentation

◆ close_client()

static void close_client ( ws_cli_conn_t *  client,
int  lock 
)
static

Close client connection (no close handshake, this should be done earlier), set appropriate state and destroy mutexes.

Parameters
clientClient connection.
lockShould lock the global mutex?.
Attention
This is part of the internal API and is documented just for completeness.

◆ close_socket()

static void close_socket ( int  fd)
static

Shutdown and close a given socket.

Parameters
fdSocket file descriptor to be closed.
Attention
This is part of the internal API and is documented just for completeness.

◆ close_timeout()

static void* close_timeout ( void *  p)
static

Close time-out thread.

For a given client, this routine sleeps until TIMEOUT_MS and closes the connection or returns sooner if already closed connection.

Parameters
pws_connection/ws_cli_conn_t Structure Pointer.
Returns
Always NULL.
Attention
This is part of the internal API and is documented just for completeness.

◆ do_bind_socket()

static int do_bind_socket ( struct ws_server ws_srv)
static

By using the server parameters provided in ws_srv, create a socket and bind it accordingly with the server configurations.

Parameters
ws_srvWeb Socket configurations.
Returns
Returns the socket file descriptor.

◆ do_close()

static int do_close ( struct ws_frame_data wfd,
int  close_code 
)
static

Sends a close frame, accordingly with the close_code or the message inside wfd.

Parameters
wfdWebsocket Frame Data.
close_codeWebsocket close code.
Returns
Returns 0 if success, a negative number otherwise.
Attention
This is part of the internal API and is documented just for completeness.

◆ do_handshake()

static int do_handshake ( struct ws_frame_data wfd)
static

Do the handshake process.

Parameters
wfdWebsocket Frame Data.
Returns
Returns 0 if success, a negative number otherwise.
Attention
This is part of the internal API and is documented just for completeness.

◆ do_pong()

static int do_pong ( struct ws_frame_data wfd,
uint64_t  frame_size 
)
static

Send a pong frame in response to a ping frame.

Accordingly to the RFC, a pong frame must have the same data payload as the ping frame, so we just send a ordinary frame with PONG opcode.

Parameters
wfdWebsocket frame data.
frame_sizePong frame size.
Returns
Returns 0 if success and a negative number otherwise.
Attention
This is part of the internal API and is documented just for completeness.

◆ get_client_state()

static int get_client_state ( ws_cli_conn_t *  client)
static

Returns the current client state for a given client client.

Parameters
clientClient structure.
Returns
Returns the client state, -1 otherwise.
Attention
This is part of the internal API and is documented just for completeness.

◆ handle_close_frame()

static int handle_close_frame ( struct ws_frame_data wfd,
struct frame_state_data fsd 
)
static

Handle close frames while checking for UTF8 in the close reason.

Parameters
wfdWebSocket frame data.
fsdFrame state data.
Returns
Returns 0 if success, -1 otherwise.
Attention
This is part of the internal API and is documented just for completeness.

◆ handle_ping_frame()

static int handle_ping_frame ( struct ws_frame_data wfd,
struct frame_state_data fsd 
)
static

Handle PING frames sending a PONG response.

Parameters
wfdWebSocket frame data.
fsdFrame state data.
Returns
Returns 0 if success, -1 otherwise.
Attention
This is part of the internal API and is documented just for completeness.

◆ handle_pong_frame()

static int handle_pong_frame ( struct ws_frame_data wfd,
struct frame_state_data fsd 
)
static

Handle PONG frames in response to our PING (or not, unsolicited is possible too).

Parameters
wfdWebSocket frame data.
fsdFrame state data.
Returns
Always 0.
Attention
This is part of the internal API and is documented just for completeness.

◆ int32_to_ping_msg()

static void int32_to_ping_msg ( int32_t  ping_id,
uint8_t *  msg 
)
inlinestatic

Given a PING id, encodes the content to be sent as payload of a PING frame.

Parameters
ping_idPING id to be encoded.
msgTarget buffer.

◆ is_control_frame()

static int is_control_frame ( int  frame)
inlinestatic

Checks is a given opcode frame belongs to a control frame or not.

Parameters
frameFrame opcode to be checked.
Returns
Returns 1 if is a control frame, 0 otherwise.
Attention
This is part of the internal API and is documented just for completeness.

◆ is_valid_frame()

static int is_valid_frame ( int  opcode)
inlinestatic

Checks is a given opcode opcode is valid or not.

Parameters
opcodeFrame opcode to be checked.
Returns
Returns 1 if valid, 0 otherwise.
Attention
This is part of the internal API and is documented just for completeness.

◆ next_byte()

static int next_byte ( struct ws_frame_data wfd)
inlinestatic

Read a chunk of bytes and return the next byte belonging to the frame.

Parameters
wfdWebsocket Frame Data.
Returns
Returns the byte read, or -1 if error.
Attention
This is part of the internal API and is documented just for completeness.

◆ next_complete_frame()

static int next_complete_frame ( struct ws_frame_data wfd)
static

Reads the next frame, whether if a TXT/BIN/CLOSE of arbitrary size.

Parameters
wfdWebsocket Frame Data.
Returns
Returns 0 if success, a negative number otherwise.
Attention
This is part of the internal API and is documented just for completeness.

◆ pong_msg_to_int32()

static int32_t pong_msg_to_int32 ( uint8_t *  msg)
inlinestatic

Given a PONG message, decodes the content as a int32_t number that corresponds to our PONG id.

Parameters
msgContent to be decoded.
Returns
Returns the PONG id.

◆ read_single_frame()

static int read_single_frame ( struct ws_frame_data wfd,
struct frame_state_data fsd 
)
static

Reads the current frame isolating data from control frames.

The parameters are changed in order to reflect the current state.

Parameters
wfdWebsocket Frame Data.
fsdFrame state data.
Returns
Returns 0 if success, a negative number otherwise.
Attention
This is part of the internal API and is documented just for completeness.

◆ send_all()

static ssize_t send_all ( ws_cli_conn_t *  client,
const void *  buf,
size_t  len,
int  flags 
)
static

Send a given message buf on a socket sockfd.

Parameters
clientTarget client.
bufMessage to be sent.
lenMessage length.
flagsSend flags.
Returns
If success (i.e: all message was sent), returns the amount of bytes sent. Otherwise, -1.
Note
Technically this shouldn't be necessary, since send() should block until all content is sent, since we don't use 'O_NONBLOCK'. However, it was reported (issue #22 on GitHub) that this was happening, so just to be cautious, I will keep using this routine.

◆ send_ping_close()

static void send_ping_close ( ws_cli_conn_t *  cli,
int  threshold,
int  lock 
)
static

Send a ping message and close if the client surpasses the threshold imposed.

Parameters
cliClient to be sent.
thresholdHow many pings can miss?.
lockShould lock global mutex or not?.
Attention
This is part of the internal API and is documented just for completeness.

◆ set_client_address()

static void set_client_address ( ws_cli_conn_t *  client)
static

Sets the IP address relative to a client connection opened by the server and save inside the client structure.

Parameters
clientClient connection.

◆ set_client_state()

static int set_client_state ( ws_cli_conn_t *  client,
int  state 
)
static

Set a state state to the client index client.

Parameters
clientClient structure.
stateState to be set.
Returns
Returns 0 if success, -1 otherwise.
Attention
This is part of the internal API and is documented just for completeness.

◆ skip_frame()

static int skip_frame ( struct ws_frame_data wfd,
uint64_t  frame_size 
)
static

Skips frame_size bytes of the current frame.

Parameters
wfdWebsocket Frame Data.
frame_sizeAmount of bytes to be skipped.
Returns
Returns 0 if success, a negative number otherwise.
Attention
This is part of the internal API and is documented just for completeness.

◆ start_close_timeout()

static int start_close_timeout ( ws_cli_conn_t *  client)
static

For a valid client index client, starts the timeout thread and set the current state to 'CLOSING'.

Parameters
clientClient connection.
Returns
Returns 0 if success, -1 otherwise.
Attention
This is part of the internal API and is documented just for completeness.

◆ validate_utf8_txt()

static int validate_utf8_txt ( struct ws_frame_data wfd,
struct frame_state_data fsd 
)
static

Validates TXT frames if UTF8 validation is enabled.

If the content is not valid, the connection is aborted.

Parameters
wfdWebSocket frame data.
fsdFrame state data.
Returns
Always 0.
Attention
This is part of the internal API and is documented just for completeness.

◆ ws_accept()

static void* ws_accept ( void *  data)
static

Main loop that keeps accepting new connections.

Parameters
dataServer socket.
Returns
Returns data.
Note
This may be run on a different thread.
Attention
This is part of the internal API and is documented just for completeness.

◆ ws_close_client()

int ws_close_client ( ws_cli_conn_t *  client)

Close the client connection for the given client with normal close code (1000) and no reason string.

Parameters
clientClient connection.
Returns
Returns 0 on success, -1 otherwise.
Note
If the client did not send a close frame in TIMEOUT_MS milliseconds, the server will close the connection with error code (1002).

◆ ws_establishconnection()

static void* ws_establishconnection ( void *  vclient)
static

Establishes to connection with the client and trigger events when occurs one.

Parameters
vclientClient connection.
Returns
Returns vclient.
Note
This will be run on a different thread.
Attention
This is part of the internal API and is documented just for completeness.

◆ ws_get_connection_context()

void* ws_get_connection_context ( ws_cli_conn_t *  cli)

Get connection context.

◆ ws_get_server_context()

void* ws_get_server_context ( ws_cli_conn_t *  cli)

Get server context.

Assumed to be set once, when initializing .context in struct ws_server.

◆ ws_get_state()

int ws_get_state ( ws_cli_conn_t *  client)

For a given client, gets the current state for the connection, or -1 if invalid.

Parameters
clientClient connection.
Returns
Returns the connection state or -1 if invalid client.
See also
WS_STATE_CONNECTING
WS_STATE_OPEN
WS_STATE_CLOSING
WS_STATE_CLOSED

◆ ws_getaddress()

char* ws_getaddress ( ws_cli_conn_t *  client)

Gets the IP address relative to a client connection opened by the server.

Parameters
clientClient connection.
Returns
Pointer the ip address, or NULL if fails.
Note
The returned string is static, no need to free up memory.

◆ ws_getport()

char* ws_getport ( ws_cli_conn_t *  client)

Gets the IP port relative to a client connection opened by the server.

Parameters
clientClient connection.
Returns
Pointer the port, or NULL if fails.
Note
The returned string is static, no need to free up memory.

◆ ws_ping()

void ws_ping ( ws_cli_conn_t *  cli,
int  threshold 
)

Sends a PING frame to the client cli with threshold threshold.

This routine sends a PING to a single client pointed to by cli or a broadcast PING if cli is NULL. If the specified client does not respond up to threshold PINGs, the connection is aborted.

ws_ping() is not automatic: the user who wants to send keep-alive PINGs must call this routine in a timely manner, whether on a different thread or inside an event.

See examples/ping/ping.c for a minimal example usage.

Parameters
cliClient to be sent, if NULL, broadcast.
thresholdHow many ignored PINGs should tolerate? (should be positive and greater than 0).
Note
It should be noted that the time between each call to ws_ping() is the timeout itself for receiving the PONG.

It is also important to note that for devices with unstable connections (such as a weak WiFi signal or 3/4/5G from a cell phone), a threshold greater than 1 is advisable.

◆ ws_sendframe()

int ws_sendframe ( ws_cli_conn_t *  client,
const char *  msg,
uint64_t  size,
int  type 
)

Send an WebSocket frame with some payload data.

Parameters
clientTarget to be send. If NULL, broadcast the message.
msgMessage to be send.
sizeBinary message size.
typeFrame type.
Returns
Returns the number of bytes written, -1 if error.
Note
If size is -1, it is assumed that a text frame is being sent, otherwise, a binary frame. In the later case, the size is used.

◆ ws_sendframe_bcast()

int ws_sendframe_bcast ( uint16_t  port,
const char *  msg,
uint64_t  size,
int  type 
)

Send an WebSocket frame with some payload data to all clients connected into the same port.

Parameters
portServer listen port to broadcast message.
msgMessage to be send.
sizeBinary message size.
typeFrame type.
Returns
Returns the number of bytes written, -1 if error.
Note
If size is -1, it is assumed that a text frame is being sent, otherwise, a binary frame. In the later case, the size is used.

◆ ws_sendframe_bin()

int ws_sendframe_bin ( ws_cli_conn_t *  client,
const char *  msg,
uint64_t  size 
)

Sends a WebSocket binary frame.

Parameters
clientTarget to be send.
msgMessage to be send.
sizeBinary message size.
Returns
Returns the number of bytes written, -1 if error.

◆ ws_sendframe_bin_bcast()

int ws_sendframe_bin_bcast ( uint16_t  port,
const char *  msg,
uint64_t  size 
)

Sends a broadcast WebSocket binary frame.

Parameters
portServer listen port to broadcast message.
msgMessage to be send.
sizeBinary message size.
Returns
Returns the number of bytes written, -1 if error.

◆ ws_sendframe_internal()

static int ws_sendframe_internal ( ws_cli_conn_t *  client,
const char *  msg,
uint64_t  size,
int  type,
uint16_t  port 
)
static

Creates and send an WebSocket frame with some payload data.

This routine is intended to be used to create a websocket frame for a given type e sending to the client. For higher level routines, please check ws_sendframe_txt and ws_sendframe_bin.

Parameters
clientTarget to be send. If NULL, broadcast the message.
msgMessage to be send.
sizeBinary message size.
typeFrame type.
portServer listen port to broadcast message (if any).
Returns
Returns the number of bytes written, -1 if error.
Note
If size is -1, it is assumed that a text frame is being sent, otherwise, a binary frame. In the later case, the size is used.
Attention
This is part of the internal API and is documented just for completeness.

◆ ws_sendframe_txt()

int ws_sendframe_txt ( ws_cli_conn_t *  client,
const char *  msg 
)

Sends a WebSocket text frame.

Parameters
clientTarget to be send.
msgMessage to be send, null terminated.
Returns
Returns the number of bytes written, -1 if error.

◆ ws_sendframe_txt_bcast()

int ws_sendframe_txt_bcast ( uint16_t  port,
const char *  msg 
)

Sends a broadcast WebSocket text frame.

Parameters
portServer listen port to broadcast message.
msgMessage to be send, null terminated.
Returns
Returns the number of bytes written, -1 if error.

◆ ws_set_connection_context()

void ws_set_connection_context ( ws_cli_conn_t *  cli,
void *  ptr 
)

Set connection context.

◆ ws_socket()

int ws_socket ( struct ws_server ws_srv)

Main loop for the server.

Parameters
ws_srvWeb Socket server parameters.
Returns
If thread_loop != 0, returns 0. Otherwise, never returns.

Variable Documentation

◆ client_socks

struct ws_connection client_socks[MAX_CLIENTS]
static

Clients list.

◆ mutex

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
static

Global mutex.

◆ timeout

uint32_t timeout
static

Timeout to a single send().

client_socks
static struct ws_connection client_socks[MAX_CLIENTS]
Clients list.
Definition: ws.c:128
ws_connection::client_sock
int client_sock
Client socket FD.
Definition: ws.c:72
MAX_CLIENTS
#define MAX_CLIENTS
Max clients connected simultaneously.
Definition: ws.h:44