in src/uws_client.c [1077:1657]
static void on_underlying_io_bytes_received(void* context, const unsigned char* buffer, size_t size)
{
/* Codes_SRS_UWS_CLIENT_01_415: [ If called with a NULL context argument, on_underlying_io_bytes_received shall do nothing. ]*/
if (context != NULL)
{
UWS_CLIENT_HANDLE uws_client = (UWS_CLIENT_HANDLE)context;
if ((buffer == NULL) ||
(size == 0))
{
/* Codes_SRS_UWS_CLIENT_01_416: [ If called with NULL buffer or zero size and the state of the iws is OPENING, uws shall report that the open failed by calling the on_ws_open_complete callback passed to uws_client_open_async with WS_OPEN_ERROR_INVALID_BYTES_RECEIVED_ARGUMENTS. ]*/
indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_INVALID_BYTES_RECEIVED_ARGUMENTS);
}
else
{
unsigned char decode_stream = 1;
switch (uws_client->uws_state)
{
default:
case UWS_STATE_CLOSED:
decode_stream = 0;
break;
case UWS_STATE_OPENING_UNDERLYING_IO:
/* Codes_SRS_UWS_CLIENT_01_417: [ When on_underlying_io_bytes_received is called while OPENING but before the on_underlying_io_open_complete has been called, uws shall report that the open failed by calling the on_ws_open_complete callback passed to uws_client_open_async with WS_OPEN_ERROR_BYTES_RECEIVED_BEFORE_UNDERLYING_OPEN. ]*/
indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_BYTES_RECEIVED_BEFORE_UNDERLYING_OPEN);
decode_stream = 0;
break;
case UWS_STATE_WAITING_FOR_UPGRADE_RESPONSE:
{
/* Codes_SRS_UWS_CLIENT_01_378: [ When on_underlying_io_bytes_received is called while the uws is OPENING, the received bytes shall be accumulated in order to attempt parsing the WebSocket Upgrade response. ]*/
unsigned char* new_received_bytes;
//size_t realloc_size = uws_client->stream_buffer_count + size + 1; **using safe int**
uws_client->stream_buffer_size = safe_add_size_t(safe_add_size_t(uws_client->stream_buffer_count, size), 1);
if (uws_client->stream_buffer_size == SIZE_MAX ||
(new_received_bytes = (unsigned char*)realloc(uws_client->stream_buffer, uws_client->stream_buffer_size)) == NULL)
{
/* Codes_SRS_UWS_CLIENT_01_379: [ If allocating memory for accumulating the bytes fails, uws shall report that the open failed by calling the on_ws_open_complete callback passed to uws_client_open_async with WS_OPEN_ERROR_NOT_ENOUGH_MEMORY. ]*/
indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_NOT_ENOUGH_MEMORY);
decode_stream = 0;
}
else
{
uws_client->stream_buffer = new_received_bytes;
(void)memcpy(uws_client->stream_buffer + uws_client->stream_buffer_count, buffer, size);
uws_client->stream_buffer_count += size;
decode_stream = 1;
}
break;
}
case UWS_STATE_OPEN:
case UWS_STATE_CLOSING_WAITING_FOR_CLOSE:
{
/* Codes_SRS_UWS_CLIENT_01_385: [ If the state of the uws instance is OPEN, the received bytes shall be used for decoding WebSocket frames. ]*/
unsigned char* new_received_bytes;
//size_t realloc_size = uws_client->stream_buffer_count + size + 1; **using safe int**
uws_client->stream_buffer_size = safe_add_size_t(safe_add_size_t(uws_client->stream_buffer_count, size), 1);
if (uws_client->stream_buffer_size == SIZE_MAX ||
(new_received_bytes = (unsigned char*)realloc(uws_client->stream_buffer, uws_client->stream_buffer_size)) == NULL)
{
/* Codes_SRS_UWS_CLIENT_01_418: [ If allocating memory for the bytes accumulated for decoding WebSocket frames fails, an error shall be indicated by calling the on_ws_error callback with WS_ERROR_NOT_ENOUGH_MEMORY. ]*/
LogError("Cannot allocate memory for received data");
indicate_ws_error(uws_client, WS_ERROR_NOT_ENOUGH_MEMORY);
decode_stream = 0;
}
else
{
uws_client->stream_buffer = new_received_bytes;
(void)memcpy(uws_client->stream_buffer + uws_client->stream_buffer_count, buffer, size);
uws_client->stream_buffer_count += size;
decode_stream = 1;
}
break;
}
}
while (decode_stream)
{
decode_stream = 0;
switch (uws_client->uws_state)
{
default:
case UWS_STATE_CLOSED:
break;
case UWS_STATE_OPENING_UNDERLYING_IO:
/* Codes_SRS_UWS_CLIENT_01_417: [ When on_underlying_io_bytes_received is called while OPENING but before the on_underlying_io_open_complete has been called, uws shall report that the open failed by calling the on_ws_open_complete callback passed to uws_client_open_async with WS_OPEN_ERROR_BYTES_RECEIVED_BEFORE_UNDERLYING_OPEN. ]*/
indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_BYTES_RECEIVED_BEFORE_UNDERLYING_OPEN);
break;
case UWS_STATE_WAITING_FOR_UPGRADE_RESPONSE:
{
const char* request_end_ptr;
/* Make sure it is zero terminated */
uws_client->stream_buffer[uws_client->stream_buffer_count] = '\0';
/* Codes_SRS_UWS_CLIENT_01_380: [ If an WebSocket Upgrade request can be parsed from the accumulated bytes, the status shall be read from the WebSocket upgrade response. ]*/
/* Codes_SRS_UWS_CLIENT_01_381: [ If the status is 101, uws shall be considered OPEN and this shall be indicated by calling the on_ws_open_complete callback passed to uws_client_open_async with IO_OPEN_OK. ]*/
if ((uws_client->stream_buffer_count >= 4) &&
((request_end_ptr = strstr((const char*)uws_client->stream_buffer, "\r\n\r\n")) != NULL))
{
int status_code;
/* This part should really be done with the HTTPAPI, but that has to be done as a separate step
as the HTTPAPI has to expose somehow the underlying IO and currently this would be a too big of a change. */
/* Codes_SRS_UWS_CLIENT_01_382: [ If a negative status is decoded from the WebSocket upgrade request, an error shall be indicated by calling the on_ws_open_complete callback passed to uws_client_open_async with WS_OPEN_ERROR_BAD_RESPONSE_STATUS. ]*/
/* Codes_SRS_UWS_CLIENT_01_478: [ A Status-Line with a 101 response code as per RFC 2616 [RFC2616]. ]*/
if (ParseHttpResponse((const char*)uws_client->stream_buffer, &status_code) != 0)
{
/* Codes_SRS_UWS_CLIENT_01_383: [ If the WebSocket upgrade request cannot be decoded an error shall be indicated by calling the on_ws_open_complete callback passed to uws_client_open_async with WS_OPEN_ERROR_BAD_UPGRADE_RESPONSE. ]*/
LogError("Cannot decode HTTP response");
indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_BAD_UPGRADE_RESPONSE);
}
else if (status_code != 101)
{
/* Codes_SRS_UWS_CLIENT_01_382: [ If a negative status is decoded from the WebSocket upgrade request, an error shall be indicated by calling the on_ws_open_complete callback passed to uws_client_open_async with WS_OPEN_ERROR_BAD_RESPONSE_STATUS. ]*/
LogError("Bad status (%d) received in WebSocket Upgrade response", status_code);
indicate_ws_open_complete_error_and_close(uws_client, WS_OPEN_ERROR_BAD_RESPONSE_STATUS);
}
else
{
/* Codes_SRS_UWS_CLIENT_01_384: [ Any extra bytes that are left unconsumed after decoding a succesfull WebSocket upgrade response shall be used for decoding WebSocket frames ]*/
consume_stream_buffer_bytes(uws_client, request_end_ptr - (char*)uws_client->stream_buffer + 4);
/* Codes_SRS_UWS_CLIENT_01_381: [ If the status is 101, uws shall be considered OPEN and this shall be indicated by calling the on_ws_open_complete callback passed to uws_client_open_async with IO_OPEN_OK. ]*/
uws_client->uws_state = UWS_STATE_OPEN;
/* Codes_SRS_UWS_CLIENT_01_065: [ When the client is to _Establish a WebSocket Connection_ given a set of (/host/, /port/, /resource name/, and /secure/ flag), along with a list of /protocols/ and /extensions/ to be used, and an /origin/ in the case of web browsers, it MUST open a connection, send an opening handshake, and read the server's handshake in response. ]*/
/* Codes_SRS_UWS_CLIENT_01_115: [ If the server's response is validated as provided for above, it is said that _The WebSocket Connection is Established_ and that the WebSocket Connection is in the OPEN state. ]*/
uws_client->on_ws_open_complete(uws_client->on_ws_open_complete_context, WS_OPEN_OK);
decode_stream = 1;
}
}
break;
}
case UWS_STATE_OPEN:
case UWS_STATE_CLOSING_WAITING_FOR_CLOSE:
{
size_t needed_bytes = 2;
size_t length;
/* Codes_SRS_UWS_CLIENT_01_277: [ To receive WebSocket data, an endpoint listens on the underlying network connection. ]*/
/* Codes_SRS_UWS_CLIENT_01_278: [ Incoming data MUST be parsed as WebSocket frames as defined in Section 5.2. ]*/
if (uws_client->stream_buffer_count >= needed_bytes &&
uws_client->stream_buffer_size > 1 // validate uws_client->stream_buffer[1] access
)
{
unsigned char has_error = 0;
/* Codes_SRS_UWS_CLIENT_01_160: [ Defines whether the "Payload data" is masked. ]*/
if ((uws_client->stream_buffer[1] & 0x80) != 0)
{
/* Codes_SRS_UWS_CLIENT_01_144: [ A client MUST close a connection if it detects a masked frame. ]*/
/* Codes_SRS_UWS_CLIENT_01_145: [ In this case, it MAY use the status code 1002 (protocol error) as defined in Section 7.4.1. (These rules might be relaxed in a future specification.) ]*/
LogError("Masked frame detected by WebSocket client");
indicate_ws_error_and_close(uws_client, WS_ERROR_BAD_FRAME_RECEIVED, 1002);
}
/* Codes_SRS_UWS_CLIENT_01_163: [ The length of the "Payload data", in bytes: ]*/
/* Codes_SRS_UWS_CLIENT_01_164: [ if 0-125, that is the payload length. ]*/
length = uws_client->stream_buffer[1];
if (length == 126)
{
/* Codes_SRS_UWS_CLIENT_01_165: [ If 126, the following 2 bytes interpreted as a 16-bit unsigned integer are the payload length. ]*/
needed_bytes += 2;
if (uws_client->stream_buffer_count >= needed_bytes &&
uws_client->stream_buffer_size > 3 // validate access upto stream_buffer[3]
)
{
/* Codes_SRS_UWS_CLIENT_01_167: [ Multibyte length quantities are expressed in network byte order. ]*/
length = ((size_t)(uws_client->stream_buffer[2]) << 8) + (size_t)uws_client->stream_buffer[3];
if (length < 126)
{
/* Codes_SRS_UWS_CLIENT_01_168: [ Note that in all cases, the minimal number of bytes MUST be used to encode the length, for example, the length of a 124-byte-long string can't be encoded as the sequence 126, 0, 124. ]*/
LogError("Bad frame: received a %u length on the 16 bit length", (unsigned int)length);
/* Codes_SRS_UWS_CLIENT_01_419: [ If there is an error decoding the WebSocket frame, an error shall be indicated by calling the on_ws_error callback with WS_ERROR_BAD_FRAME_RECEIVED. ]*/
indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED);
has_error = 1;
}
else
{
needed_bytes = safe_add_size_t(needed_bytes, length);
}
}
}
else if (length == 127)
{
/* Codes_SRS_UWS_CLIENT_01_166: [ If 127, the following 8 bytes interpreted as a 64-bit unsigned integer (the most significant bit MUST be 0) are the payload length. ]*/
needed_bytes += 8;
if (uws_client->stream_buffer_count >= needed_bytes)
{
if (uws_client->stream_buffer_size <= 2 || (uws_client->stream_buffer[2] & 0x80) != 0)
{
LogError("Bad frame: received a 64 bit length frame with the highest bit set");
/* Codes_SRS_UWS_CLIENT_01_419: [ If there is an error decoding the WebSocket frame, an error shall be indicated by calling the on_ws_error callback with WS_ERROR_BAD_FRAME_RECEIVED. ]*/
indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED);
has_error = 1;
}
else if (uws_client->stream_buffer_size <= 9) // validate access upto stream_buffer[9] below
{
indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED);
has_error = 1;
}
else
{
/* Codes_SRS_UWS_CLIENT_01_167: [ Multibyte length quantities are expressed in network byte order. ]*/
uint64_t length_uint64 = (((uint64_t)(uws_client->stream_buffer[2]) << 56) +
(((uint64_t)uws_client->stream_buffer[3]) << 48) +
(((uint64_t)uws_client->stream_buffer[4]) << 40) +
(((uint64_t)uws_client->stream_buffer[5]) << 32) +
(((uint64_t)uws_client->stream_buffer[6]) << 24) +
(((uint64_t)uws_client->stream_buffer[7]) << 16) +
(((uint64_t)uws_client->stream_buffer[8]) << 8) +
(uint64_t)(uws_client->stream_buffer[9]));
length = (size_t)(length_uint64);
needed_bytes = safe_add_size_t(needed_bytes, length);
if (length < 65536 ||
length_uint64 >= (SIZE_MAX/2) || // limit max pack size to 1/2 process memory
needed_bytes == SIZE_MAX)
{
/* Codes_SRS_UWS_CLIENT_01_168: [ Note that in all cases, the minimal number of bytes MUST be used to encode the length, for example, the length of a 124-byte-long string can't be encoded as the sequence 126, 0, 124. ]*/
LogError("Bad frame: received a %u length on the 64 bit length", (unsigned int)length);
/* Codes_SRS_UWS_CLIENT_01_419: [ If there is an error decoding the WebSocket frame, an error shall be indicated by calling the on_ws_error callback with WS_ERROR_BAD_FRAME_RECEIVED. ]*/
indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED);
has_error = 1;
}
}
}
}
else
{
needed_bytes = safe_add_size_t(needed_bytes, length);
}
if ((has_error == 0) &&
(uws_client->stream_buffer_count >= needed_bytes))
{
unsigned char opcode = uws_client->stream_buffer[0] & 0xF;
/* Codes_SRS_UWS_CLIENT_01_147: [ Indicates that this is the final fragment in a message. ]*/
bool is_final = (uws_client->stream_buffer[0] & 0x80) != 0;
switch (opcode)
{
default:
break;
/* Codes_SRS_UWS_CLIENT_01_152: [* * %x0 denotes a continuation frame *]*/
case (unsigned char)WS_CONTINUATION_FRAME:
{
/* Codes_SRS_UWS_CLIENT_01_213: [ A fragmented message consists of a single frame with the FIN bit clear and an opcode other than 0, followed by zero or more frames with the FIN bit clear and the opcode set to 0, and terminated by a single frame with the FIN bit set and an opcode of 0. ]*/
/* Codes_SRS_UWS_CLIENT_01_216: [ Message fragments MUST be delivered to the recipient in the order sent by the sender. ]*/
/* Codes_SRS_UWS_CLIENT_01_219: [ A sender MAY create fragments of any size for non-control messages. ]*/
if (process_frame_fragment(uws_client, length, needed_bytes) != 0)
{
break;
}
if (is_final)
{
/* Codes_SRS_UWS_CLIENT_01_225: [ As a consequence of these rules, all fragments of a message are of the same type, as set by the first fragment's opcode. ]*/
if (uws_client->fragmented_frame_type == WS_FRAME_TYPE_UNKNOWN)
{
LogError("Continuation fragment received without initial fragment specifying frame data type");
indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED);
decode_stream = 1;
break;
}
uws_client->on_ws_frame_received(uws_client->on_ws_frame_received_context, uws_client->fragmented_frame_type, uws_client->fragment_buffer, uws_client->fragment_buffer_count);
uws_client->fragment_buffer_count = 0;
uws_client->fragmented_frame_type = WS_FRAME_TYPE_UNKNOWN;
}
decode_stream = 1;
break;
}
/* Codes_SRS_UWS_CLIENT_01_153: [ * %x1 denotes a text frame ]*/
/* Codes_SRS_UWS_CLIENT_01_258: [** Currently defined opcodes for data frames include 0x1 (Text), 0x2 (Binary). ]*/
case (unsigned char)WS_TEXT_FRAME:
{
/* Codes_SRS_UWS_CLIENT_01_386: [ When a WebSocket data frame is decoded succesfully it shall be indicated via the callback on_ws_frame_received. ]*/
/* Codes_SRS_UWS_CLIENT_01_169: [ The payload length is the length of the "Extension data" + the length of the "Application data". ]*/
/* Codes_SRS_UWS_CLIENT_01_173: [ The "Payload data" is defined as "Extension data" concatenated with "Application data". ]*/
/* Codes_SRS_UWS_CLIENT_01_280: [ Upon receiving a data frame (Section 5.6), the endpoint MUST note the /type/ of the data as defined by the opcode (frame-opcode) from Section 5.2. ]*/
/* Codes_SRS_UWS_CLIENT_01_281: [ The "Application data" from this frame is defined as the /data/ of the message. ]*/
/* Codes_SRS_UWS_CLIENT_01_282: [ If the frame comprises an unfragmented message (Section 5.4), it is said that _A WebSocket Message Has Been Received_ with type /type/ and data /data/. ]*/
if (is_final)
{
uws_client->on_ws_frame_received(uws_client->on_ws_frame_received_context, WS_FRAME_TYPE_TEXT, uws_client->stream_buffer + needed_bytes - length, length);
}
else
{
/* Codes_SRS_UWS_CLIENT_01_217: [ The fragments of one message MUST NOT be interleaved between the fragments of another message unless an extension has been negotiated that can interpret the interleaving. ]*/
if (uws_client->fragmented_frame_type != WS_FRAME_TYPE_UNKNOWN)
{
LogError("Fragmented frame received interleaved between the fragments of another message");
indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED);
decode_stream = 1;
break;
}
/* Codes_SRS_UWS_CLIENT_01_213: [ A fragmented message consists of a single frame with the FIN bit clear and an opcode other than 0, followed by zero or more frames with the FIN bit clear and the opcode set to 0, and terminated by a single frame with the FIN bit set and an opcode of 0. ]*/
/* Codes_SRS_UWS_CLIENT_01_216: [ Message fragments MUST be delivered to the recipient in the order sent by the sender. ]*/
/* Codes_SRS_UWS_CLIENT_01_219: [ A sender MAY create fragments of any size for non-control messages. ]*/
if (process_frame_fragment(uws_client, length, needed_bytes) != 0)
{
break;
}
/* Codes_SRS_UWS_CLIENT_01_225: [ As a consequence of these rules, all fragments of a message are of the same type, as set by the first fragment's opcode. ]*/
/* Codes_SRS_UWS_CLIENT_01_226: [ Since control frames cannot be fragmented, the type for all fragments in a message MUST be either text, binary, or one of the reserved opcodes. ]*/
uws_client->fragmented_frame_type = WS_FRAME_TYPE_TEXT;
}
decode_stream = 1;
break;
}
/* Codes_SRS_UWS_CLIENT_01_154: [ * %x2 denotes a binary frame ]*/
case (unsigned char)WS_BINARY_FRAME:
{
/* Codes_SRS_UWS_CLIENT_01_386: [ When a WebSocket data frame is decoded succesfully it shall be indicated via the callback on_ws_frame_received. ]*/
/* Codes_SRS_UWS_CLIENT_01_169: [ The payload length is the length of the "Extension data" + the length of the "Application data". ]*/
/* Codes_SRS_UWS_CLIENT_01_173: [ The "Payload data" is defined as "Extension data" concatenated with "Application data". ]*/
/* Codes_SRS_UWS_CLIENT_01_264: [ The "Payload data" is arbitrary binary data whose interpretation is solely up to the application layer. ]*/
/* Codes_SRS_UWS_CLIENT_01_280: [ Upon receiving a data frame (Section 5.6), the endpoint MUST note the /type/ of the data as defined by the opcode (frame-opcode) from Section 5.2. ]*/
/* Codes_SRS_UWS_CLIENT_01_281: [ The "Application data" from this frame is defined as the /data/ of the message. ]*/
/* Codes_SRS_UWS_CLIENT_01_282: [ If the frame comprises an unfragmented message (Section 5.4), it is said that _A WebSocket Message Has Been Received_ with type /type/ and data /data/. ]*/
if (is_final)
{
size_t stream_buffer_idx = safe_add_size_t(uws_client->stream_buffer, needed_bytes);
stream_buffer_idx = safe_subtract_size_t(stream_buffer_idx, length);
if (stream_buffer_idx != SIZE_MAX)
{
uws_client->on_ws_frame_received(uws_client->on_ws_frame_received_context, WS_FRAME_TYPE_BINARY, (const unsigned char*)stream_buffer_idx, length);
}
else
{
LogError("Invalid packet length, stream_buffer_idx=%zu", stream_buffer_idx);
indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED);
decode_stream = 1;
break;
}
}
else
{
/* Codes_SRS_UWS_CLIENT_01_217: [ The fragments of one message MUST NOT be interleaved between the fragments of another message unless an extension has been negotiated that can interpret the interleaving. ]*/
if (uws_client->fragmented_frame_type != WS_FRAME_TYPE_UNKNOWN)
{
LogError("Fragmented frame received interleaved between the fragments of another message");
indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED);
decode_stream = 1;
break;
}
/* Codes_SRS_UWS_CLIENT_01_213: [ A fragmented message consists of a single frame with the FIN bit clear and an opcode other than 0, followed by zero or more frames with the FIN bit clear and the opcode set to 0, and terminated by a single frame with the FIN bit set and an opcode of 0. ]*/
/* Codes_SRS_UWS_CLIENT_01_216: [ Message fragments MUST be delivered to the recipient in the order sent by the sender. ]*/
/* Codes_SRS_UWS_CLIENT_01_219: [ A sender MAY create fragments of any size for non-control messages. ]*/
if (process_frame_fragment(uws_client, length, needed_bytes) != 0)
{
break;
}
/* Codes_SRS_UWS_CLIENT_01_225: [ As a consequence of these rules, all fragments of a message are of the same type, as set by the first fragment's opcode. ]*/
/* Codes_SRS_UWS_CLIENT_01_226: [ Since control frames cannot be fragmented, the type for all fragments in a message MUST be either text, binary, or one of the reserved opcodes. ]*/
uws_client->fragmented_frame_type = WS_FRAME_TYPE_BINARY;
}
decode_stream = 1;
break;
}
/* Codes_SRS_UWS_CLIENT_01_156: [ * %x8 denotes a connection close ]*/
/* Codes_SRS_UWS_CLIENT_01_234: [ The Close frame contains an opcode of 0x8. ]*/
/* Codes_SRS_UWS_CLIENT_01_214: [ Control frames (see Section 5.5) MAY be injected in the middle of a fragmented message. ]*/
case (unsigned char)WS_CLOSE_FRAME:
{
uint16_t close_code;
uint16_t* close_code_ptr;
const unsigned char* data_ptr = uws_client->stream_buffer + needed_bytes - length;
const unsigned char* extra_data_ptr;
size_t extra_data_length;
unsigned char* close_frame_bytes;
size_t close_frame_length;
bool utf8_error = false;
/* Codes_SRS_UWS_CLIENT_01_215: [ Control frames themselves MUST NOT be fragmented. ]*/
if (!is_final)
{
LogError("Fragmented control frame received.");
indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED);
break;
}
/* Codes_SRS_UWS_CLIENT_01_235: [ The Close frame MAY contain a body (the "Application data" portion of the frame) that indicates a reason for closing, such as an endpoint shutting down, an endpoint having received a frame too large, or an endpoint having received a frame that does not conform to the format expected by the endpoint. ]*/
if (length >= 2)
{
/* Codes_SRS_UWS_CLIENT_01_236: [ If there is a body, the first two bytes of the body MUST be a 2-byte unsigned integer (in network byte order) representing a status code with value /code/ defined in Section 7.4. ]*/
close_code = (data_ptr[0] << 8) + data_ptr[1];
/* Codes_SRS_UWS_CLIENT_01_461: [ The argument close_code shall be set to point to the code extracted from the CLOSE frame. ]*/
close_code_ptr = &close_code;
}
else
{
/* Codes_SRS_UWS_CLIENT_01_462: [ If no code can be extracted then close_code shall be NULL. ]*/
close_code_ptr = NULL;
}
if (length > 2)
{
/* Codes_SRS_UWS_CLIENT_01_463: [ The extra bytes (besides the close code) shall be passed to the on_ws_peer_closed callback by using extra_data and extra_data_length. ]*/
extra_data_ptr = data_ptr + 2;
extra_data_length = length - 2;
/* Codes_SRS_UWS_CLIENT_01_238: [ As the data is not guaranteed to be human readable, clients MUST NOT show it to end users. ]*/
/* Codes_SRS_UWS_CLIENT_01_237: [ Following the 2-byte integer, the body MAY contain UTF-8-encoded data with value /reason/, the interpretation of which is not defined by this specification. ]*/
if (utf8_checker_is_valid_utf8(extra_data_ptr, extra_data_length) != true)
{
LogError("Reason in CLOSE frame is not UTF-8.");
extra_data_ptr = NULL;
extra_data_length = 0;
utf8_error = true;
}
}
else
{
extra_data_ptr = NULL;
extra_data_length = 0;
}
if (utf8_error)
{
uws_client->uws_state = UWS_STATE_CLOSING_UNDERLYING_IO;
if (xio_close(uws_client->underlying_io, on_underlying_io_close_complete, uws_client) != 0)
{
LogError("Could not close underlying IO");
indicate_ws_error(uws_client, WS_ERROR_CANNOT_CLOSE_UNDERLYING_IO);
uws_client->uws_state = UWS_STATE_CLOSED;
}
}
else
{
BUFFER_HANDLE close_frame_buffer;
if (uws_client->uws_state == UWS_STATE_CLOSING_WAITING_FOR_CLOSE)
{
uws_client->uws_state = UWS_STATE_CLOSING_UNDERLYING_IO;
if (xio_close(uws_client->underlying_io, on_underlying_io_close_complete, uws_client) != 0)
{
indicate_ws_close_complete(uws_client);
uws_client->uws_state = UWS_STATE_CLOSED;
}
}
else
{
/* Codes_SRS_UWS_CLIENT_01_296: [ Upon either sending or receiving a Close control frame, it is said that _The WebSocket Closing Handshake is Started_ and that the WebSocket connection is in the CLOSING state. ]*/
/* Codes_SRS_UWS_CLIENT_01_240: [ The application MUST NOT send any more data frames after sending a Close frame. ]*/
uws_client->uws_state = UWS_STATE_CLOSING_SENDING_CLOSE;
}
/* Codes_SRS_UWS_CLIENT_01_241: [ If an endpoint receives a Close frame and did not previously send a Close frame, the endpoint MUST send a Close frame in response. ]*/
/* Codes_SRS_UWS_CLIENT_01_242: [ It SHOULD do so as soon as practical. ]*/
/* Codes_SRS_UWS_CLIENT_01_239: [ Close frames sent from client to server must be masked as per Section 5.3. ]*/
/* Codes_SRS_UWS_CLIENT_01_140: [ To avoid confusing network intermediaries (such as intercepting proxies) and for security reasons that are further discussed in Section 10.3, a client MUST mask all frames that it sends to the server (see Section 5.3 for further details). ]*/
close_frame_buffer = uws_frame_encoder_encode(WS_CLOSE_FRAME, NULL, 0, true, true, 0);
if (close_frame_buffer == NULL)
{
LogError("Cannot encode the response CLOSE frame");
/* Codes_SRS_UWS_CLIENT_01_288: [ To _Close the WebSocket Connection_, an endpoint closes the underlying TCP connection. ]*/
/* Codes_SRS_UWS_CLIENT_01_290: [ An endpoint MAY close the connection via any means available when necessary, such as when under attack. ]*/
uws_client->uws_state = UWS_STATE_CLOSING_UNDERLYING_IO;
if (xio_close(uws_client->underlying_io, on_underlying_io_close_complete, uws_client) != 0)
{
indicate_ws_error(uws_client, WS_ERROR_CANNOT_CLOSE_UNDERLYING_IO);
uws_client->uws_state = UWS_STATE_CLOSED;
}
}
else
{
close_frame_bytes = BUFFER_u_char(close_frame_buffer);
close_frame_length = BUFFER_length(close_frame_buffer);
if (xio_send(uws_client->underlying_io, close_frame_bytes, close_frame_length, on_underlying_io_close_sent, uws_client) != 0)
{
LogError("Cannot send the response CLOSE frame");
/* Codes_SRS_UWS_CLIENT_01_288: [ To _Close the WebSocket Connection_, an endpoint closes the underlying TCP connection. ]*/
/* Codes_SRS_UWS_CLIENT_01_290: [ An endpoint MAY close the connection via any means available when necessary, such as when under attack. ]*/
uws_client->uws_state = UWS_STATE_CLOSING_UNDERLYING_IO;
if (xio_close(uws_client->underlying_io, on_underlying_io_close_complete, uws_client) != 0)
{
indicate_ws_error(uws_client, WS_ERROR_CANNOT_CLOSE_UNDERLYING_IO);
uws_client->uws_state = UWS_STATE_CLOSED;
}
}
BUFFER_delete(close_frame_buffer);
}
}
/* Codes_SRS_UWS_CLIENT_01_460: [ When a CLOSE frame is received the callback on_ws_peer_closed passed to uws_client_open_async shall be called, while passing to it the argument on_ws_peer_closed_context. ]*/
uws_client->on_ws_peer_closed(uws_client->on_ws_peer_closed_context, close_code_ptr, extra_data_ptr, extra_data_length);
break;
}
/* Codes_SRS_UWS_CLIENT_01_157: [ * %x9 denotes a ping ]*/
/* Codes_SRS_UWS_CLIENT_01_247: [ The Ping frame contains an opcode of 0x9. ]*/
/* Codes_SRS_UWS_CLIENT_01_251: [ An endpoint MAY send a Ping frame any time after the connection is established and before the connection is closed. ]*/
/* Codes_SRS_UWS_CLIENT_01_214: [ Control frames (see Section 5.5) MAY be injected in the middle of a fragmented message. ]*/
case (unsigned char)WS_PING_FRAME:
{
/* Codes_SRS_UWS_CLIENT_01_249: [ Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in response ]*/
/* Codes_SRS_UWS_CLIENT_01_250: [ It SHOULD respond with Pong frame as soon as is practical. ]*/
unsigned char* pong_frame;
size_t pong_frame_length;
BUFFER_HANDLE pong_frame_buffer;
/* Codes_SRS_UWS_CLIENT_01_215: [ Control frames themselves MUST NOT be fragmented. ]*/
if (!is_final)
{
LogError("Fragmented control frame received.");
indicate_ws_error(uws_client, WS_ERROR_BAD_FRAME_RECEIVED);
break;
}
/* Codes_SRS_UWS_CLIENT_01_140: [ To avoid confusing network intermediaries (such as intercepting proxies) and for security reasons that are further discussed in Section 10.3, a client MUST mask all frames that it sends to the server (see Section 5.3 for further details). ]*/
pong_frame_buffer = uws_frame_encoder_encode(WS_PONG_FRAME, uws_client->stream_buffer + needed_bytes - length, length, true, true, 0);
if (pong_frame_buffer == NULL)
{
LogError("Encoding of PONG failed.");
}
else
{
/* Codes_SRS_UWS_CLIENT_01_248: [ A Ping frame MAY include "Application data". ]*/
pong_frame = BUFFER_u_char(pong_frame_buffer);
pong_frame_length = BUFFER_length(pong_frame_buffer);
if (xio_send(uws_client->underlying_io, pong_frame, pong_frame_length, unchecked_on_send_complete, NULL) != 0)
{
LogError("Sending PONG frame failed.");
}
BUFFER_delete(pong_frame_buffer);
}
break;
}
/* Codes_SRS_UWS_CLIENT_01_252: [ The Pong frame contains an opcode of 0xA. ]*/
case (unsigned char)WS_PONG_FRAME:
break;
}
consume_stream_buffer_bytes(uws_client, needed_bytes);
}
}
break;
}
}
}
}
}
}