151 lines
9.1 KiB
C
151 lines
9.1 KiB
C
/**
|
|
* @file kWebSocket.x.h
|
|
*
|
|
* @internal
|
|
* Copyright (C) 2017-2021 by LMI Technologies Inc.
|
|
* Licensed under the MIT License.
|
|
* Redistributed files must retain the above copyright notice.
|
|
*/
|
|
#ifndef K_API_WEBSOCKET_X_H
|
|
#define K_API_WEBSOCKET_X_H
|
|
|
|
#include <kApi/Io/kStream.h>
|
|
#include <kApi/Io/kTcpClient.h>
|
|
|
|
kDeclareEnumEx(k, kWebSocketDataType, kValue)
|
|
|
|
#define xkWEB_SOCKET_HANDSHAKE_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" // GUID used in HTTP upgrade handshake (defined by RFC 6455)
|
|
|
|
#define xkWEB_SOCKET_OP_CODE_CONTINUE (0x00) //frame is a continuation of previous data frame
|
|
#define xkWEB_SOCKET_OP_CODE_UTF8 (0x01) //first frame in a sequence of utf8 data frames
|
|
#define xkWEB_SOCKET_OP_CODE_BINARY (0x02) //first frame in a sequence of binary data frames
|
|
#define xkWEB_SOCKET_OP_CODE_CLOSE (0x08) //close request and/or response frame
|
|
#define xkWEB_SOCKET_OP_CODE_PING (0x09) //ping frame (expects pong response)
|
|
#define xkWEB_SOCKET_OP_CODE_PONG (0x0A) //pong frame (pong response, or unsolicted)
|
|
|
|
#define xkWEB_SOCKET_16_BIT_PAYLOAD (126) //payload code indicating 16-bit payload size (bits 6..0 in header byte 1)
|
|
#define xkWEB_SOCKET_64_BIT_PAYLOAD (127) //payload code indicating 64-bit payload size (bits 6..0 in header byte 1)
|
|
|
|
kInlineFx(k8u) xkWebSocket_SetFin(kBool fin) { return (k8u) (((fin) & 0x01) << 7); } //sets end-of-message (FIN) bit in header 0
|
|
kInlineFx(k8u) xkWebSocket_SetOpCode(k8u op) { return (k8u) (((op) & 0x0F) << 0); } //sets opcode field in header 0
|
|
kInlineFx(k8u) xkWebSocket_SetMaskEnabled(kBool en) { return (k8u) (((en) & 0x01) << 7); } //sets mask enabled bit in header 1
|
|
kInlineFx(k8u) xkWebSocket_SetPayloadCode(k8u pay) { return (k8u) (((pay) & 0x7F) << 0); } //sets payload code field in header 1
|
|
|
|
kInlineFx(kBool) xkWebSocket_Fin(k8u h0) { return (kBool) (((h0) >> 7) & 0x01); } //gets end-of-message (FIN) bit from header 0
|
|
kInlineFx(k8u) xkWebSocket_OpCode(k8u h0) { return (k8u) (((h0) >> 0) & 0x0F); } //gets opcode field from header 0
|
|
kInlineFx(kBool) xkWebSocket_MaskEnabled(k8u h1) { return (kBool) (((h1) >> 7) & 0x01); } //gets mask enabled from in header 1
|
|
kInlineFx(kSize) xkWebSocket_PayloadCode(k8u h1) { return (kSize) (((h1) >> 0) & 0x7F); } //gets payload code field from header 1
|
|
|
|
#define xkWEB_SOCKET_MAX_CONTROL_PAYLOAD (0x7D) //maximum payload size for control messages, as stipulated by RFC 6455
|
|
|
|
#define xkWEB_SOCKET_BACKGROUND_TIMER_PERIOD (200000) //period of background timer that polls for incoming control messages (e.g, ping, close), in microseconds
|
|
|
|
#define xkWEB_SOCKET_CLOSE_TIMEOUT (5000000) //maximum amount of time to wait for remote host to respond to close command, in microseconds
|
|
|
|
#define xkWEB_SOCKET_MAX_HTTP_RESPONSE_LENGTH (8*1024) //maximum expected length of HTTP websocket upgrade response, in bytes
|
|
|
|
#define xkWEB_SOCKET_MASK_BUFFER_SIZE (32*1024) //size of temp buffer used for masking outbound data
|
|
|
|
#define xkWEB_SOCKET_TCP_CLIENT_READ_BUFFER (16) //client read buffer for underlying kTcpClient object; just large enough for frame headers
|
|
#define xkWEB_SOCKET_TCP_CLIENT_WRITE_BUFFER (0) //client write buffer for underlying kTcpClient object; no buffer needed (kSerializer has built-in write buffer)
|
|
|
|
//connection state of kWebSocket
|
|
typedef k32s xkWebSocketState;
|
|
|
|
#define xkWEB_SOCKET_STATE_INITIALIZED (0) //created, not yet connected
|
|
#define xkWEB_SOCKET_STATE_CONNECTING (1) //TCP connection established, not yet upgraded
|
|
#define xkWEB_SOCKET_STATE_CONNECTED (2) //connected, upgraded, ready to use
|
|
#define xkWEB_SOCKET_STATE_CLOSING (3) //close request sent
|
|
#define xkWEB_SOCKET_STATE_CLOSED (4) //closed (close request received, or TCP client closed)
|
|
|
|
#define xkWEB_SOCKET_PROTOCOL_VERSION (13) //protocol version supported; correspoonds to RFC 6455
|
|
|
|
//receive state
|
|
typedef struct kWebSocketRecvInfo
|
|
{
|
|
k8u opCode; //last received opcode
|
|
kBool isMasked; //last received mask-enable field
|
|
kBool isLastFrame; //last received FIN bit
|
|
kSize payloadSize; //last received payload size
|
|
kByte maskKey[4]; //last received mask key
|
|
|
|
kBool inMessage; //are we currently in a data frame sequence?
|
|
kBool inDataFrame; //are we currently reading out a data frame?
|
|
kBool isFirstDataFrame; //is the current frame the first frame in a data frame sequence?
|
|
kSize dataRead; //amount of payload data that has been read out from the current frame (if a data frame)
|
|
kWebSocketDataType recvType; //data type of the current data frame sequence
|
|
kWebSocketDataType lastRecvType; //data type latched at most recent call to kWebSocket_Receive
|
|
} kWebSocketRecvInfo;
|
|
|
|
//send state
|
|
typedef struct kWebSocketSendInfo
|
|
{
|
|
kWebSocketDataType sendType; //data type of the current data frame sequence
|
|
kBool inMessage; //are we currently in a data frame sequence
|
|
} kWebSocketSendInfo;
|
|
|
|
typedef struct kWebSocketClass
|
|
{
|
|
kStreamClass base;
|
|
|
|
kBool isClient; //does this object represent the client or server side?
|
|
kBool shouldMaskSend; //does outbound data need to be masked?
|
|
xkWebSocketState state; //current state of web socket
|
|
|
|
kLock lock; //protects TCP client, send/recv info
|
|
|
|
kTcpClient tcpClient; //underlying TCP client
|
|
kSerializer serializer; //bound to TCP client; used to parse/format headers
|
|
|
|
kArray1 maskBuffer; //buffer used to mask data, when masking required
|
|
|
|
kWebSocketSendInfo sendInfo; //state information for outbound data
|
|
kWebSocketRecvInfo recvInfo; //state information for inbound data
|
|
|
|
k64u lastPongTime; //time at which last pong was detected
|
|
|
|
kPeriodic backgroundTimer; //periodic background timer to handle incoming control messages
|
|
|
|
} kWebSocketClass;
|
|
|
|
kDeclareClassEx(k, kWebSocket, kStream)
|
|
|
|
kFx(kStatus) xkWebSocket_ConstructFromRequest(kWebSocket* webSocket, kHttpServerChannel channel, kAlloc allocator);
|
|
|
|
kFx(kStatus) xkWebSocket_Init(kWebSocket webSocket, kType type, kHttpServerChannel channel, kAlloc allocator);
|
|
kFx(kStatus) xkWebSocket_VRelease(kWebSocket webSocket);
|
|
|
|
kFx(kStatus) xkWebSocket_VReadSomeImpl(kWebSocket webSocket, void* buffer, kSize minCount, kSize maxCount, kSize* bytesRead);
|
|
kFx(kStatus) xkWebSocket_VWriteImpl(kWebSocket webSocket, const void* buffer, kSize size);
|
|
kFx(kStatus) xkWebSocket_VFlush(kWebSocket webSocket);
|
|
kFx(kStatus) xkWebSocket_VFill(kWebSocket webSocket);
|
|
|
|
kFx(kStatus) xkWebSocket_SendHttpUpgradeRequest(kWebSocket webSocket, const kChar* host, k32u port, const kChar* uri, const kByte* key, kSize keyLength);
|
|
kFx(kStatus) xkWebSocket_ReceiveHttpUpgradeResponse(kWebSocket webSocket, const kByte* key, kSize keyLength);
|
|
kFx(kStatus) xkWebSocket_ProcessHttpUpgradeRequest(kWebSocket webSocket, kHttpServerChannel channel);
|
|
|
|
kFx(kStatus) xkWebSocket_WriteDataFrame(kWebSocket webSocket, const kByte* buffer, kSize count, kBool finalFrame);
|
|
kFx(kStatus) xkWebSocket_WriteFrame(kWebSocket webSocket, const kByte* buffer, kSize count, k8u opCode, kBool finalFrame);
|
|
kFx(kStatus) xkWebSocket_WriteHeader(kWebSocket webSocket, k8u opCode, kBool finalFrame, kBool shouldMask, const kByte* maskKey, kSize maskKeySize, kSize payloadSize);
|
|
|
|
kFx(kStatus) xkWebSocket_DiscardReceive(kWebSocket webSocket);
|
|
kFx(kStatus) xkWebSocket_BeginReceive(kWebSocket webSocket, k64u timeout);
|
|
kFx(kStatus) xkWebSocket_ProcessHeader(kWebSocket webSocket);
|
|
kFx(kStatus) xkWebSocket_ParseHeader(kWebSocket webSocket);
|
|
kFx(kStatus) xkWebSocket_InterpretHeader(kWebSocket webSocket);
|
|
|
|
kFx(kStatus) xkWebSocket_ProcessControlFrame(kWebSocket webSocket);
|
|
kFx(kStatus) xkWebSocket_ProcessPing(kWebSocket webSocket, const kByte* payload, kSize payloadSize);
|
|
kFx(kStatus) xkWebSocket_ProcessPong(kWebSocket webSocket, const kByte* payload, kSize payloadSize);
|
|
kFx(kStatus) xkWebSocket_ProcessClose(kWebSocket webSocket, const kByte* payload, kSize payloadSize);
|
|
|
|
kFx(kStatus) xkWebSocket_ReadAtLeast(kWebSocket webSocket, kByte* buffer, kSize minCount, kSize maxCount, kSize* bytesRead);
|
|
|
|
kFx(kStatus) xkWebSocket_ApplyMask(kWebSocket webSocket, const kByte* src, kByte* dest, kSize count, kByte* maskKey, kSize maskKeySize, kSize initialPosition);
|
|
|
|
kFx(kBool) xkWebSocket_IsValid(kWebSocket webSocket);
|
|
|
|
kFx(kStatus) xkWebSocket_OnBackgroundTimer(kWebSocket webSocket, kPeriodic timer);
|
|
|
|
#endif
|