NetBurner 3.5.6
PDF Version
websockets.h
1/*NB_REVISION*/
2
3/*NB_COPYRIGHT*/
4
35#ifndef __WEBSOCKETS_H
36#define __WEBSOCKETS_H
37
38// NB Definitions
39#include <predef.h>
40
41// NB Libs
42#include <buffers.h>
43#include <http.h>
44#include <iosys.h>
45#include <nbrtos.h>
46#include <nettypes.h>
47
48
58#define WS_BUFFER_SEGMENTS 4
59#define WS_MAX_SOCKS 4
60#define WS_BUFFER_MIN 10
61#define WS_ACCUM_DLY ((TICKS_PER_SECOND / 4) + 1)
62#define WS_FLUSH_TIMEOUT 0
63
64#define WS_FIN_BIT 0x80
65#define WS_OP_CONT 0x0
66#define WS_OP_TEXT 0x1
67#define WS_OP_BIN 0x2
68#define WS_OP_CLOSE 0x8
69#define WS_OP_PING 0x9
70#define WS_OP_PONG 0xA
71
72#define WS_SO_TEXT 0x8
73
78namespace NB
79{
80class WSCritObj; // WebSocket critical section object for thread synchronization
81
93{
94 /* Client-side short frame header (payload <=125 bytes, with mask) */
95 struct WS_Client_Short
96 {
97 uint8_t opAndFin; // Opcode (4 bits) and FIN bit (1 bit) combined
98 uint8_t maskAndLen; // Mask bit (1 bit) and payload length (7 bits)
99 uint32_t mask; // 32-bit masking key for payload
100 uint8_t pData[]; // Flexible array member for payload data
101 } __attribute__((packed));
102
103 /* Client-side medium frame header (payload 126-65535 bytes, with mask) */
104 struct WS_Client_Med
105 {
106 uint8_t opAndFin; // Opcode (4 bits) and FIN bit (1 bit) combined
107 uint8_t maskAndLen; // Mask bit (1 bit) and payload length indicator (7 bits = 126)
108 beuint16_t length; // 16-bit big-endian actual payload length
109 uint32_t mask; // 32-bit masking key for payload
110 uint8_t pData[]; // Flexible array member for payload data
111 } __attribute__((packed));
112
113 /* Client-side long frame header (payload >65535 bytes, with mask) */
114 struct WS_Client_Long
115 {
116 uint8_t opAndFin; // Opcode (4 bits) and FIN bit (1 bit) combined
117 uint8_t maskAndLen; // Mask bit (1 bit) and payload length indicator (7 bits = 127)
118 beuint32_t lengthHi; // Upper 32 bits of 64-bit big-endian payload length
119 beuint32_t lengthLo; // Lower 32 bits of 64-bit big-endian payload length
120 uint32_t mask; // 32-bit masking key for payload
121 uint8_t pData[]; // Flexible array member for payload data
122 } __attribute__((packed));
123
124 /* Server-side short frame header (payload <=125 bytes, no mask) */
125 struct WS_Server_Short
126 {
127 uint8_t opAndFin; // Opcode (4 bits) and FIN bit (1 bit) combined
128 uint8_t maskAndLen; // Mask bit (1 bit = 0) and payload length (7 bits)
129 uint8_t pData[]; // Flexible array member for payload data
130 } __attribute__((packed));
131
132 /* Server-side medium frame header (payload 126-65535 bytes, no mask) */
133 struct WS_Server_Med
134 {
135 uint8_t opAndFin; // Opcode (4 bits) and FIN bit (1 bit) combined
136 uint8_t maskAndLen; // Mask bit (1 bit = 0) and payload length indicator (7 bits = 126)
137 beuint16_t length; // 16-bit big-endian actual payload length
138 uint8_t pData[]; // Flexible array member for payload data
139 } __attribute__((packed));
140
141 /* Server-side long frame header (payload >65535 bytes, no mask) */
142 struct WS_Server_Long
143 {
144 uint8_t opAndFin; // Opcode (4 bits) and FIN bit (1 bit) combined
145 uint8_t maskAndLen; // Mask bit (1 bit = 0) and payload length indicator (7 bits = 127)
146 beuint32_t lengthHi; // Upper 32 bits of 64-bit big-endian payload length
147 beuint32_t lengthLo; // Lower 32 bits of 64-bit big-endian payload length
148 uint8_t pData[]; // Flexible array member for payload data
149 } __attribute__((packed));
150
151 /*
152 * WebSocket connection state machine enumeration
153 *
154 * Tracks the lifecycle of a WebSocket connection from initialization through
155 * establishment, closure handshake, and final cleanup.
156 */
157 enum WS_State
158 {
159 WS_INIT, // Initial state, connection not yet established
160 WS_CONNECTING, // Handshake in progress, transitioning to WebSocket protocol
161 WS_ESTABLISHED, // Connection fully established, ready for data exchange
162 WS_CLOSE_PENDING, // Close requested locally, waiting to send close frame
163 WS_CLOSE_SENT, // Close frame sent, awaiting peer's close response
164 WS_CLOSE_RECV, // Close frame received from peer, sending acknowledgment
165 WS_TCP_CLOSED, // Underlying TCP connection has been closed
166 WS_CLOSED, // WebSocket fully closed, cleanup complete
167 WS_ABORT, // Connection aborted due to error
168 WS_FAIL // Connection failed (handshake failure, protocol error)
169 };
170
171 static const char *WS_StateStr(WS_State state); // Convert WS_State enum to string for debugging
172
173 /*
174 * WebSocket close status codes (RFC 6455)
175 *
176 * Standard status codes sent in close frames to indicate the reason for
177 * connection closure. Some codes must not be sent on the wire.
178 */
179 enum WS_StatusCode
180 {
181 WS_STAT_NORM_CLOSE = 1000, // Normal closure, connection purpose fulfilled
182 WS_STAT_GOING_AWAY = 1001, // Endpoint going away (server shutdown, page navigation)
183 WS_STAT_PROT_ERROR = 1002, // Protocol error detected
184 WS_STAT_UNACCEPT_TYPE = 1003, // Unacceptable data type received
185 WS_STAT_NONE = 1005, // Reserved: No status code present (must not be sent)
186 WS_STAT_NONE_ABNORMAL = 1006, // Reserved: Abnormal closure, no close frame (must not be sent)
187 WS_STAT_NOT_CONSISTENT = 1007, // Data not consistent with message type (e.g., invalid UTF-8)
188 WS_STAT_POLICY_VIOLATION = 1008, // Policy violation detected
189 WS_STAT_MSG_TOO_BIG = 1009, // Message too large to process
190 WS_STAT_EXPECTED_EXTENSION = 1010,// Expected extension not negotiated
191 WS_STAT_UNEXPECTED_COND = 1011, // Unexpected condition prevented request fulfillment
192 WS_STAT_TLS_FAILURE = 1015, // Reserved: TLS handshake failure (must not be sent)
193 };
194
195 fifo_buffer_storage rxBuffer; // Receive buffer for incoming data from TCP connection
196 fifo_buffer_storage ctlBuffer; // Control frame buffer for ping/pong/close frames
197 fifo_buffer_storage txBuffer; // Transmit buffer for outgoing data to TCP connection
198
199public:
200
372
524
775 OS_CRIT socket_crit;
776
777private:
778 WS_State m_state; // Current connection state (INIT, CONNECTING, ESTABLISHED, etc.)
779 int m_options; // Socket options bitmap (e.g., WS_SO_TEXT for text mode)
780 uint32_t m_SendTime; // Timestamp of last send operation (in ticks)
781 uint32_t m_AccumDly; // Accumulation delay for batching writes (in ticks)
782 bool m_serverNotClient; // True if this is server-side connection, false for client
783
784 volatile bool m_CtlBlock; // Flag indicating control frame processing is blocked
785 uint32_t m_currentIndex; // Current buffer index for frame processing
786 uint32_t m_remLen; // Remaining length to process in current operation
787
788 uint8_t m_frameHeadBuf[14]; // Buffer for incoming frame header (max 14 bytes)
789 uint8_t m_frameHeadNext; // Next byte position in frame header buffer
790 bool m_gotLen; // True if complete length field has been received
791 uint32_t m_currentMask; // Current masking key for frame payload
792 uint32_t m_remainingLen; // Remaining payload bytes to receive in current frame
793
794 uint8_t m_txFrameHeadBuf[14];// Buffer for outgoing frame header (max 14 bytes)
795 uint8_t m_txFrameHeadNext; // Next byte position in TX frame header buffer
796 uint8_t m_txFrameHeadLen; // Total length of TX frame header
797 uint32_t m_txCurrMask; // Current masking key for outgoing payload
798 uint32_t m_txRemLen; // Remaining payload bytes to send in current frame
799 int m_txFlushInProgress; // Non-zero if flush operation is currently in progress
800 bool m_txCtlSendInProgress; // True if control frame transmission is in progress
801
802 /* Linked list node for tasks waiting on control frame operations */
803 struct PendingCtlWait {
804 OS_SEM sem; // Semaphore for blocking the waiting task
805 PendingCtlWait *pNext; // Pointer to next waiting task in linked list
806 };
807 PendingCtlWait *m_ctlWait; // Head of linked list for tasks waiting on control frames
808
809 uint32_t m_pingTick; // Tick count when last ping was sent
810 int m_pingLen; // Length of last ping payload
811 OS_SEM *m_pingWaitSem; // Semaphore for blocking on ping reply
812
813 uint32_t m_txBufferMin; // Minimum TX buffer space threshold
814
815 void ServerRxFromTCP(); // Process incoming data from TCP socket (server-side)
816 bool ReadRemainingHeader(); // Continue reading frame header bytes, return true when complete
817 void ProcessCtlFrame(); // Process received control frame (ping/pong/close)
818 bool ValidatePong(uint32_t seed, uint32_t len); // Validate pong payload matches ping seed
819 void Close(bool closedAfterSend, uint16_t code = 0, const char *reason = NULL, int len = 0); // Initiate WebSocket close handshake
820 void RemoteClose(); // Handle close frame received from peer
821 void CleanUp(); // Clean up resources after connection closure
822 void SendCtlFrame(); // Send queued control frame (ping/pong/close)
823 void Init(int fd_tcp, int fd_ws, bool serverNotClient); // Initialize WebSocket connection state
824 void Flush_Header(uint8_t *buf, uint32_t mask, uint32_t len); // Write frame header to buffer
825
826 void AddCtlWait(PendingCtlWait *pWait); // Add task to control frame wait queue
827 void PostToCtlWait(); // Wake up tasks waiting on control frame completion
828
829 static uint32_t s_flushTick; // Global tick counter for flush timing
830 static fd_set s_pendingCtlSocks; // Set of sockets with pending control frames
831 static OS_CRIT s_ws_crit_obj; // Global critical section for WebSocket operations
832 static bool s_bFinishedInit; // True if static initialization is complete
833
834 static void StaticInit(); // Initialize static/global WebSocket subsystem
835 static int GetNewSocket(int fd_tcp, bool serverNotClient); // Allocate new WebSocket structure
836
837 static WebSocket *GetRecordFromTCP(int fd); // Find WebSocket record by TCP file descriptor
838
839 // Core connection establishment logic
840 static int CoreConnect(IPADDR ip, const char *host, const char *resource, const char *protocol, int portnum, bool useSSL = false);
841
842 public:
843
1176 int WriteData(const char *buf, int nbytes);
1177
1543 int ReadData(char *buf, int nbytes);
1544
1793 void Flush();
1794
2040 void Pong(uint32_t len);
2041
2266 inline WS_State GetState() { return m_state; }
2267
2498 int Ping(uint32_t len, uint32_t *sentTick);
2499
2766 int WaitForPingReply(uint32_t timeout);
2767
3027 int GetPingReplyTick(uint32_t *replyTick);
3028
3203 int setoption(int option);
3204
3360 int clroption(int option);
3361
3618
3897 inline int GetWriteSpace() { return txBuffer.SpaceAvail(); }
3898
4021
4192
4458 static int ws_readwto(int fd, char *buf, int nbytes, int timeout);
4459
4679 static int ws_read(int fd, char *buf, int nbytes);
4680
4949 static int ws_write(int fd, const char *buf, int nbytes);
4950
5103 static int ws_externalclose(int fd);
5104
5279 static void ws_flush(int fd);
5280
5364 static int ws_setoption(int fd, int option);
5365
5451 static int ws_clroption(int fd, int option);
5452
5583 static int ws_getoption(int fd);
5584
5643
5751 static int promote_tcp_ws(int tcp_fd);
5752
5903 static void ws_read_notify(int fd);
5904
6066 static void ws_write_notify(int fd);
6067
6238 static void GetFlushTime();
6239
6338 static void RunSkippedCallBack(); // Process any skipped callbacks
6339
6667 static int Connect(const char * host, const char * resource, const char * protocol, int portnum = 80, bool useSSL = false);
6668
6692 static inline int Connect(const char * host, const char * resource, int portnum = 80, bool useSSL = false)
6693 { return Connect(host, resource, NULL, portnum, useSSL); }
6694
6910 static int Connect(IPADDR host, const char * resource, const char * protocol, int portnum = 80, bool useSSL = false);
6911
6912
6936 static inline int Connect(IPADDR host, const char * resource, int portnum = 80, bool useSSL = false)
6937 { return Connect(host, resource, NULL, portnum, useSSL); }
6938
7148 void DumpSock();
7149
7364 static void DumpSockets();
7365
7640
7641}; // class WebSocket
7642
7643/* } groupWebSocketClass */
7644
7645
7646} // namespace NB
7647
7648
7649
7822int WSPing(int fd, uint32_t len, uint32_t *sentTick);
7823
8071int WSGetPingReplyTick(int fd, uint32_t *replyTick);
8072
8321int WSWaitForPingReply(int fd, uint32_t timeout);
8322
8672int WSUpgrade(HTTP_Request *req, int sock);
8673
8674
8677#endif /* ----- #ifndef __WEBSOCKETS_H ----- */
8678
8679
8680
Used to hold and manipulate IPv4 and IPv6 addresses in dual stack mode.
Definition ipv6_addr.h:41
WebSocket connection management class.
Definition websockets.h:93
static int ws_readwto(int fd, char *buf, int nbytes, int timeout)
Read data from a WebSocket connection with timeout.
static int Connect(IPADDR host, const char *resource, int portnum=80, bool useSSL=false)
Connect to a WebSocket server using IP address without protocol negotiation.
Definition websockets.h:6936
static int Connect(IPADDR host, const char *resource, const char *protocol, int portnum=80, bool useSSL=false)
Connect to a WebSocket server using IP address with optional protocol negotiation.
int GetWriteSpace()
Get available transmit buffer space in bytes.
Definition websockets.h:3897
static void ws_write_notify(int fd)
TCP write notification callback invoked when buffer space becomes available.
static int GetFreeWebSocketCount()
Get the number of available WebSocket connection slots.
static WebSocket WSSockStructs[WS_MAX_SOCKS]
Static array of WebSocket connection structures.
Definition websockets.h:4191
static void ws_flush(int fd)
Flush the transmit buffer of a WebSocket connection.
int Ping(uint32_t len, uint32_t *sentTick)
Send a WebSocket ping frame and record the timestamp.
static void GetFlushTime()
Updates the global flush timing for WebSocket message delivery.
OS_CRIT socket_crit
Critical section for thread-safe socket operations.
Definition websockets.h:775
static int ws_read(int fd, char *buf, int nbytes)
Read data from a WebSocket connection.
static int promote_tcp_ws(int tcp_fd)
Promote a TCP socket to a WebSocket connection after successful handshake.
static int s_openSocketCount
Number of currently open WebSocket connections.
Definition websockets.h:4020
static int Connect(const char *host, const char *resource, int portnum=80, bool useSSL=false)
Connect to a WebSocket server using hostname without protocol negotiation.
Definition websockets.h:6692
static void RunSkippedCallBack()
Executes the callback function when a scheduled run is skipped.
WS_State GetState()
Get the current connection state of the WebSocket.
Definition websockets.h:2266
void Flush()
Flush buffered transmit data to the network.
static WebSocket * GetWebSocketRecord(int fd)
Find a WebSocket record by its file descriptor.
static int Connect(const char *host, const char *resource, const char *protocol, int portnum=80, bool useSSL=false)
Connect to a WebSocket server using hostname with optional protocol negotiation.
static void DumpSockets()
Dump detailed state information for all active WebSocket connections.
void DumpSock()
Dump detailed state information for this WebSocket connection.
int GetPingReplyTick(uint32_t *replyTick)
Get the tick count when the last pong was received.
static int ws_setoption(int fd, int option)
Set a socket option on a WebSocket connection.
static int ws_externalclose(int fd)
Close a WebSocket connection from an external caller.
static int ws_clroption(int fd, int option)
Clear a socket option on a WebSocket connection.
int WriteData(const char *buf, int nbytes)
Write data to the WebSocket connection.
void Pong(uint32_t len)
Send a pong frame in response to a ping.
int m_fd_ws
WebSocket file descriptor (application-level identifier)
Definition websockets.h:523
int WaitForPingReply(uint32_t timeout)
Block waiting for a pong reply to a previously sent ping.
int getoption()
Get the current socket options bitmap.
static int ws_getoption(int fd)
Get the current socket options for a WebSocket connection.
int ReadData(char *buf, int nbytes)
Read data from the WebSocket connection.
int setoption(int option)
Set a socket option on this WebSocket connection.
int m_fd_tcp
TCP socket file descriptor (underlying transport)
Definition websockets.h:371
static void ws_read_notify(int fd)
TCP read notification callback invoked when data is available on the WebSocket.
static int ws_write(int fd, const char *buf, int nbytes)
Write data to a WebSocket connection.
int clroption(int option)
Clear a socket option on this WebSocket connection.
FIFO buffer storage using linked pool buffers.
Definition buffers.h:443
uint16_t SpaceAvail()
Get number of bytes available for writing.
int WSUpgrade(HTTP_Request *req, int sock)
Upgrade an HTTP connection to a WebSocket connection.
int WSPing(int fd, uint32_t len, uint32_t *sentTick)
Send a WebSocket ping frame to test connection liveness and measure round-trip time.
int WSWaitForPingReply(int fd, uint32_t timeout)
Wait for a pong reply to a previously sent ping with a specified timeout.
int WSGetPingReplyTick(int fd, uint32_t *replyTick)
Retrieve the timestamp of the most recent pong reply received on a WebSocket connection.
#define WS_MAX_SOCKS
Maximum number of simultaneous WebSocket connections supported.
Definition websockets.h:59
HTTP Request Structure.
Definition http.h:87