net: websocket: new receiving algorithm
websocket_recv_msg() is reworked with using fsm. Now the function return 0 when payload is empty, -ENOTCONN if socket close. Receiving empty ping and sending empty pong were added in tests. Fixes #52327 Signed-off-by: Grixa Yrev <grixayrev@yandex.ru>
This commit is contained in:
parent
932e57396f
commit
2a992c65c0
6 changed files with 390 additions and 281 deletions
|
@ -166,7 +166,10 @@ int websocket_send_msg(int ws_sock, const uint8_t *payload, size_t payload_len,
|
||||||
* The value is in milliseconds. Value SYS_FOREVER_MS means to wait
|
* The value is in milliseconds. Value SYS_FOREVER_MS means to wait
|
||||||
* forever.
|
* forever.
|
||||||
*
|
*
|
||||||
* @return <0 if error, >=0 amount of bytes received
|
* @retval >=0 amount of bytes received.
|
||||||
|
* @retval -EAGAIN on timeout.
|
||||||
|
* @retval -ENOTCONN on socket close.
|
||||||
|
* @retval -errno other negative errno value in case of failure.
|
||||||
*/
|
*/
|
||||||
int websocket_recv_msg(int ws_sock, uint8_t *buf, size_t buf_len,
|
int websocket_recv_msg(int ws_sock, uint8_t *buf, size_t buf_len,
|
||||||
uint32_t *message_type, uint64_t *remaining,
|
uint32_t *message_type, uint64_t *remaining,
|
||||||
|
|
|
@ -201,7 +201,7 @@ static void recv_data_wso_api(int sock, size_t amount, uint8_t *buf,
|
||||||
&message_type,
|
&message_type,
|
||||||
&remaining,
|
&remaining,
|
||||||
0);
|
0);
|
||||||
if (ret <= 0) {
|
if (ret < 0) {
|
||||||
if (ret == -EAGAIN) {
|
if (ret == -EAGAIN) {
|
||||||
k_sleep(K_MSEC(50));
|
k_sleep(K_MSEC(50));
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -150,15 +150,18 @@ int mqtt_client_websocket_read(struct mqtt_client *client, uint8_t *data,
|
||||||
|
|
||||||
ret = websocket_recv_msg(client->transport.websocket.sock,
|
ret = websocket_recv_msg(client->transport.websocket.sock,
|
||||||
data, buflen, &message_type, NULL, timeout);
|
data, buflen, &message_type, NULL, timeout);
|
||||||
if (ret > 0 && message_type > 0) {
|
if (ret >= 0 && message_type > 0) {
|
||||||
if (message_type & WEBSOCKET_FLAG_CLOSE) {
|
if (message_type & WEBSOCKET_FLAG_CLOSE) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(message_type & WEBSOCKET_FLAG_BINARY)) {
|
if ((ret == 0) || !(message_type & WEBSOCKET_FLAG_BINARY)) {
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ret == -ENOTCONN) {
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,8 +274,8 @@ int websocket_connect(int sock, struct websocket_request *wreq,
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->real_sock = sock;
|
ctx->real_sock = sock;
|
||||||
ctx->tmp_buf = wreq->tmp_buf;
|
ctx->recv_buf.buf = wreq->tmp_buf;
|
||||||
ctx->tmp_buf_len = wreq->tmp_buf_len;
|
ctx->recv_buf.size = wreq->tmp_buf_len;
|
||||||
ctx->sec_accept_key = sec_accept_key;
|
ctx->sec_accept_key = sec_accept_key;
|
||||||
ctx->http_cb = wreq->http_cb;
|
ctx->http_cb = wreq->http_cb;
|
||||||
|
|
||||||
|
@ -388,7 +388,10 @@ int websocket_connect(int sock, struct websocket_request *wreq,
|
||||||
/* We will re-use the temp buffer in receive function if needed but
|
/* We will re-use the temp buffer in receive function if needed but
|
||||||
* in order that to work the amount of data in buffer must be set to 0
|
* in order that to work the amount of data in buffer must be set to 0
|
||||||
*/
|
*/
|
||||||
ctx->tmp_buf_pos = 0;
|
ctx->recv_buf.count = 0;
|
||||||
|
|
||||||
|
/* Init parser FSM */
|
||||||
|
ctx->parser_state = WEBSOCKET_PARSER_STATE_OPCODE;
|
||||||
|
|
||||||
return fd;
|
return fd;
|
||||||
|
|
||||||
|
@ -583,7 +586,11 @@ static int websocket_prepare_and_send(struct websocket_context *ctx,
|
||||||
|
|
||||||
if (HEXDUMP_SENT_PACKETS) {
|
if (HEXDUMP_SENT_PACKETS) {
|
||||||
LOG_HEXDUMP_DBG(header, header_len, "Header");
|
LOG_HEXDUMP_DBG(header, header_len, "Header");
|
||||||
LOG_HEXDUMP_DBG(payload, payload_len, "Payload");
|
if ((payload != NULL) && (payload_len > 0)) {
|
||||||
|
LOG_HEXDUMP_DBG(payload, payload_len, "Payload");
|
||||||
|
} else {
|
||||||
|
LOG_DBG("No payload");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_NET_TEST)
|
#if defined(CONFIG_NET_TEST)
|
||||||
|
@ -682,16 +689,17 @@ int websocket_send_msg(int ws_sock, const uint8_t *payload, size_t payload_len,
|
||||||
header[hdr_len++] |= ctx->masking_value >> 8;
|
header[hdr_len++] |= ctx->masking_value >> 8;
|
||||||
header[hdr_len++] |= ctx->masking_value;
|
header[hdr_len++] |= ctx->masking_value;
|
||||||
|
|
||||||
data_to_send = k_malloc(payload_len);
|
if ((payload != NULL) && (payload_len > 0)) {
|
||||||
if (!data_to_send) {
|
data_to_send = k_malloc(payload_len);
|
||||||
return -ENOMEM;
|
if (!data_to_send) {
|
||||||
}
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(data_to_send, payload, payload_len);
|
memcpy(data_to_send, payload, payload_len);
|
||||||
|
|
||||||
for (i = 0; i < payload_len; i++) {
|
for (i = 0; i < payload_len; i++) {
|
||||||
data_to_send[i] ^=
|
data_to_send[i] ^= ctx->masking_value >> (8 * (3 - i % 4));
|
||||||
ctx->masking_value >> (8 * (3 - i % 4));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -715,94 +723,153 @@ quit:
|
||||||
return ret - hdr_len;
|
return ret - hdr_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool websocket_parse_header(uint8_t *buf, size_t buf_len, bool *masked,
|
static uint32_t websocket_opcode2flag(uint8_t data)
|
||||||
uint32_t *mask_value, uint64_t *message_length,
|
|
||||||
uint32_t *message_type_flag,
|
|
||||||
size_t *header_len)
|
|
||||||
{
|
{
|
||||||
uint8_t len_len; /* length of the length field in header */
|
switch (data & 0x0f) {
|
||||||
uint8_t len; /* message length byte */
|
case WEBSOCKET_OPCODE_DATA_TEXT:
|
||||||
uint16_t value;
|
return WEBSOCKET_FLAG_TEXT;
|
||||||
|
case WEBSOCKET_OPCODE_DATA_BINARY:
|
||||||
value = sys_get_be16(&buf[0]);
|
return WEBSOCKET_FLAG_BINARY;
|
||||||
if (value & 0x8000) {
|
case WEBSOCKET_OPCODE_CLOSE:
|
||||||
*message_type_flag |= WEBSOCKET_FLAG_FINAL;
|
return WEBSOCKET_FLAG_CLOSE;
|
||||||
}
|
case WEBSOCKET_OPCODE_PING:
|
||||||
|
return WEBSOCKET_FLAG_PING;
|
||||||
switch (value & 0x0f00) {
|
case WEBSOCKET_OPCODE_PONG:
|
||||||
case 0x0100:
|
return WEBSOCKET_FLAG_PONG;
|
||||||
*message_type_flag |= WEBSOCKET_FLAG_TEXT;
|
default:
|
||||||
break;
|
|
||||||
case 0x0200:
|
|
||||||
*message_type_flag |= WEBSOCKET_FLAG_BINARY;
|
|
||||||
break;
|
|
||||||
case 0x0800:
|
|
||||||
*message_type_flag |= WEBSOCKET_FLAG_CLOSE;
|
|
||||||
break;
|
|
||||||
case 0x0900:
|
|
||||||
*message_type_flag |= WEBSOCKET_FLAG_PING;
|
|
||||||
break;
|
|
||||||
case 0x0A00:
|
|
||||||
*message_type_flag |= WEBSOCKET_FLAG_PONG;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
len = value & 0x007f;
|
static int websocket_parse(struct websocket_context *ctx, struct websocket_buffer *payload)
|
||||||
if (len < 126) {
|
{
|
||||||
len_len = 0;
|
int len;
|
||||||
*message_length = len;
|
uint8_t data;
|
||||||
} else if (len == 126) {
|
size_t parsed_count = 0;
|
||||||
len_len = 2;
|
|
||||||
*message_length = sys_get_be16(&buf[2]);
|
|
||||||
} else {
|
|
||||||
len_len = 8;
|
|
||||||
*message_length = sys_get_be64(&buf[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Minimum websocket header is 2 bytes, header length might be
|
do {
|
||||||
* bigger depending on length field len.
|
if (parsed_count >= ctx->recv_buf.count) {
|
||||||
*/
|
return parsed_count;
|
||||||
*header_len = MIN_HEADER_LEN + len_len;
|
}
|
||||||
|
if (ctx->parser_state != WEBSOCKET_PARSER_STATE_PAYLOAD) {
|
||||||
|
data = ctx->recv_buf.buf[parsed_count++];
|
||||||
|
|
||||||
if (buf_len >= *header_len) {
|
switch (ctx->parser_state) {
|
||||||
if (value & 0x0080) {
|
case WEBSOCKET_PARSER_STATE_OPCODE:
|
||||||
*masked = true;
|
ctx->message_type = websocket_opcode2flag(data);
|
||||||
*mask_value = sys_get_be32(&buf[2 + len_len]);
|
if ((data & 0x80) != 0) {
|
||||||
*header_len += 4;
|
ctx->message_type |= WEBSOCKET_FLAG_FINAL;
|
||||||
|
}
|
||||||
|
ctx->parser_state = WEBSOCKET_PARSER_STATE_LENGTH;
|
||||||
|
break;
|
||||||
|
case WEBSOCKET_PARSER_STATE_LENGTH:
|
||||||
|
ctx->masked = (data & 0x80) != 0;
|
||||||
|
len = data & 0x7f;
|
||||||
|
if (len < 126) {
|
||||||
|
ctx->message_len = len;
|
||||||
|
if (ctx->masked) {
|
||||||
|
ctx->masking_value = 0;
|
||||||
|
ctx->parser_remaining = 4;
|
||||||
|
ctx->parser_state = WEBSOCKET_PARSER_STATE_MASK;
|
||||||
|
} else {
|
||||||
|
ctx->parser_remaining = ctx->message_len;
|
||||||
|
ctx->parser_state =
|
||||||
|
(ctx->parser_remaining == 0)
|
||||||
|
? WEBSOCKET_PARSER_STATE_OPCODE
|
||||||
|
: WEBSOCKET_PARSER_STATE_PAYLOAD;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx->message_len = 0;
|
||||||
|
ctx->parser_remaining = (len < 127) ? 2 : 8;
|
||||||
|
ctx->parser_state = WEBSOCKET_PARSER_STATE_EXT_LEN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WEBSOCKET_PARSER_STATE_EXT_LEN:
|
||||||
|
ctx->parser_remaining--;
|
||||||
|
ctx->message_len |= (data << (ctx->parser_remaining * 8));
|
||||||
|
if (ctx->parser_remaining == 0) {
|
||||||
|
if (ctx->masked) {
|
||||||
|
ctx->masking_value = 0;
|
||||||
|
ctx->parser_remaining = 4;
|
||||||
|
ctx->parser_state = WEBSOCKET_PARSER_STATE_MASK;
|
||||||
|
} else {
|
||||||
|
ctx->parser_remaining = ctx->message_len;
|
||||||
|
ctx->parser_state = WEBSOCKET_PARSER_STATE_PAYLOAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WEBSOCKET_PARSER_STATE_MASK:
|
||||||
|
ctx->parser_remaining--;
|
||||||
|
ctx->masking_value |= (data << (ctx->parser_remaining * 8));
|
||||||
|
if (ctx->parser_remaining == 0) {
|
||||||
|
if (ctx->message_len == 0) {
|
||||||
|
ctx->parser_remaining = 0;
|
||||||
|
ctx->parser_state = WEBSOCKET_PARSER_STATE_OPCODE;
|
||||||
|
} else {
|
||||||
|
ctx->parser_remaining = ctx->message_len;
|
||||||
|
ctx->parser_state = WEBSOCKET_PARSER_STATE_PAYLOAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (LOG_LEVEL >= LOG_LEVEL_DBG)
|
||||||
|
if ((ctx->parser_state == WEBSOCKET_PARSER_STATE_PAYLOAD) ||
|
||||||
|
((ctx->parser_state == WEBSOCKET_PARSER_STATE_OPCODE) &&
|
||||||
|
(ctx->message_len == 0))) {
|
||||||
|
NET_DBG("[%p] %smasked, mask 0x%08x, type 0x%02x, msg %zd", ctx,
|
||||||
|
ctx->masked ? "" : "un",
|
||||||
|
ctx->masked ? ctx->masking_value : 0, ctx->message_type,
|
||||||
|
(size_t)ctx->message_len);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
*masked = false;
|
size_t remaining_in_recv_buf = ctx->recv_buf.count - parsed_count;
|
||||||
|
size_t payload_in_recv_buf =
|
||||||
|
MIN(remaining_in_recv_buf, ctx->parser_remaining);
|
||||||
|
size_t free_in_payload_buf = payload->size - payload->count;
|
||||||
|
size_t ready_to_copy = MIN(payload_in_recv_buf, free_in_payload_buf);
|
||||||
|
|
||||||
|
if (free_in_payload_buf == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&payload->buf[payload->count], &ctx->recv_buf.buf[parsed_count],
|
||||||
|
ready_to_copy);
|
||||||
|
parsed_count += ready_to_copy;
|
||||||
|
payload->count += ready_to_copy;
|
||||||
|
ctx->parser_remaining -= ready_to_copy;
|
||||||
|
if (ctx->parser_remaining == 0) {
|
||||||
|
ctx->parser_remaining = 0;
|
||||||
|
ctx->parser_state = WEBSOCKET_PARSER_STATE_OPCODE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
} while (ctx->parser_state != WEBSOCKET_PARSER_STATE_OPCODE);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return parsed_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
int websocket_recv_msg(int ws_sock, uint8_t *buf, size_t buf_len,
|
int websocket_recv_msg(int ws_sock, uint8_t *buf, size_t buf_len,
|
||||||
uint32_t *message_type, uint64_t *remaining, int32_t timeout)
|
uint32_t *message_type, uint64_t *remaining, int32_t timeout)
|
||||||
{
|
{
|
||||||
struct websocket_context *ctx;
|
struct websocket_context *ctx;
|
||||||
size_t header_len = 0;
|
|
||||||
int recv_len = 0;
|
|
||||||
size_t can_copy, left;
|
|
||||||
int ret;
|
int ret;
|
||||||
k_timeout_t tout = K_FOREVER;
|
k_timeout_t tout = K_FOREVER;
|
||||||
|
struct websocket_buffer payload = {.buf = buf, .size = buf_len, .count = 0};
|
||||||
|
|
||||||
if (timeout != SYS_FOREVER_MS) {
|
if (timeout != SYS_FOREVER_MS) {
|
||||||
tout = K_MSEC(timeout);
|
tout = K_MSEC(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_NET_TEST)
|
if ((buf == NULL) || (buf_len == 0)) {
|
||||||
/* Websocket unit test does not use socket layer but feeds
|
return -EINVAL;
|
||||||
* the data directly here when testing this function.
|
}
|
||||||
*/
|
|
||||||
struct test_data {
|
|
||||||
uint8_t *input_buf;
|
|
||||||
size_t input_len;
|
|
||||||
struct websocket_context *ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
#if defined(CONFIG_NET_TEST)
|
||||||
struct test_data *test_data =
|
struct test_data *test_data =
|
||||||
UINT_TO_POINTER((unsigned int) ws_sock);
|
UINT_TO_POINTER((unsigned int) ws_sock);
|
||||||
|
|
||||||
|
@ -818,178 +885,90 @@ int websocket_recv_msg(int ws_sock, uint8_t *buf, size_t buf_len,
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_NET_TEST */
|
#endif /* CONFIG_NET_TEST */
|
||||||
|
|
||||||
/* If we have not received the websocket header yet, read it first */
|
do {
|
||||||
if (!ctx->header_received) {
|
size_t parsed_count;
|
||||||
|
|
||||||
|
if (ctx->recv_buf.count == 0) {
|
||||||
#if defined(CONFIG_NET_TEST)
|
#if defined(CONFIG_NET_TEST)
|
||||||
size_t input_len = MIN(ctx->tmp_buf_len - ctx->tmp_buf_pos,
|
size_t input_len = MIN(ctx->recv_buf.size,
|
||||||
test_data->input_len);
|
test_data->input_len - test_data->input_pos);
|
||||||
|
|
||||||
memcpy(&ctx->tmp_buf[ctx->tmp_buf_pos], test_data->input_buf,
|
if (input_len > 0) {
|
||||||
input_len);
|
memcpy(ctx->recv_buf.buf,
|
||||||
test_data->input_buf += input_len;
|
&test_data->input_buf[test_data->input_pos], input_len);
|
||||||
ret = input_len;
|
test_data->input_pos += input_len;
|
||||||
#else
|
ret = input_len;
|
||||||
ret = recv(ctx->real_sock, &ctx->tmp_buf[ctx->tmp_buf_pos],
|
|
||||||
ctx->tmp_buf_len - ctx->tmp_buf_pos,
|
|
||||||
K_TIMEOUT_EQ(tout, K_NO_WAIT) ? MSG_DONTWAIT : 0);
|
|
||||||
#endif /* CONFIG_NET_TEST */
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
return -errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret == 0) {
|
|
||||||
/* Socket closed */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->tmp_buf_pos += ret;
|
|
||||||
|
|
||||||
if (ctx->tmp_buf_pos >= MIN_HEADER_LEN) {
|
|
||||||
bool masked;
|
|
||||||
|
|
||||||
/* Now we will be able to figure out what is the
|
|
||||||
* actual size of the header.
|
|
||||||
*/
|
|
||||||
if (websocket_parse_header(&ctx->tmp_buf[0],
|
|
||||||
ctx->tmp_buf_pos,
|
|
||||||
&masked,
|
|
||||||
&ctx->masking_value,
|
|
||||||
&ctx->message_len,
|
|
||||||
&ctx->message_type,
|
|
||||||
&header_len)) {
|
|
||||||
ctx->masked = masked;
|
|
||||||
|
|
||||||
if (message_type) {
|
|
||||||
*message_type = ctx->message_type;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return -EAGAIN;
|
/* emulate timeout */
|
||||||
|
errno = EAGAIN;
|
||||||
|
ret = -1;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->tmp_buf_pos < header_len) {
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* All of the header is now received, we can read the payload
|
|
||||||
* data next.
|
|
||||||
*/
|
|
||||||
ctx->header_received = true;
|
|
||||||
|
|
||||||
if (HEXDUMP_RECV_PACKETS) {
|
|
||||||
LOG_HEXDUMP_DBG(&ctx->tmp_buf[0], header_len,
|
|
||||||
"Header");
|
|
||||||
NET_DBG("[%p] masked %d mask 0x%04x hdr %zd msg %zd",
|
|
||||||
ctx, ctx->masked,
|
|
||||||
ctx->masked ? ctx->masking_value : 0,
|
|
||||||
header_len, (size_t)ctx->message_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->total_read = 0;
|
|
||||||
|
|
||||||
memmove(ctx->tmp_buf, &ctx->tmp_buf[header_len],
|
|
||||||
ctx->tmp_buf_len - header_len);
|
|
||||||
ctx->tmp_buf_pos -= header_len;
|
|
||||||
|
|
||||||
if (ctx->tmp_buf_pos == 0) {
|
|
||||||
/* No data after the header, let the caller call
|
|
||||||
* this function again to get the payload.
|
|
||||||
*/
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
NET_DBG("There is %zd bytes of data", ctx->tmp_buf_pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now read the whole payload or parts of it */
|
|
||||||
|
|
||||||
if (ctx->tmp_buf_pos == 0) {
|
|
||||||
/* Read more data into temp buffer */
|
|
||||||
#if defined(CONFIG_NET_TEST)
|
|
||||||
size_t input_len = MIN(ctx->tmp_buf_len, test_data->input_len);
|
|
||||||
|
|
||||||
memcpy(ctx->tmp_buf, test_data->input_buf, input_len);
|
|
||||||
test_data->input_buf += input_len;
|
|
||||||
|
|
||||||
ret = input_len;
|
|
||||||
#else
|
#else
|
||||||
ret = recv(ctx->real_sock, ctx->tmp_buf, ctx->tmp_buf_len,
|
ret = recv(ctx->real_sock, ctx->recv_buf.buf, ctx->recv_buf.size,
|
||||||
K_TIMEOUT_EQ(tout, K_NO_WAIT) ? MSG_DONTWAIT : 0);
|
K_TIMEOUT_EQ(tout, K_NO_WAIT) ? MSG_DONTWAIT : 0);
|
||||||
#endif /* CONFIG_NET_TEST */
|
#endif /* CONFIG_NET_TEST */
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
ret = -errno;
|
||||||
|
if ((ret == -EAGAIN) && (payload.count > 0)) {
|
||||||
|
/* go to unmasking */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == 0) {
|
||||||
|
/* Socket closed */
|
||||||
|
return -ENOTCONN;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->recv_buf.count = ret;
|
||||||
|
|
||||||
|
NET_DBG("[%p] Received %d bytes", ctx, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = websocket_parse(ctx, &payload);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return -errno;
|
return ret;
|
||||||
|
}
|
||||||
|
parsed_count = ret;
|
||||||
|
|
||||||
|
if ((ctx->parser_state == WEBSOCKET_PARSER_STATE_OPCODE) ||
|
||||||
|
(payload.count >= payload.size)) {
|
||||||
|
if (remaining != NULL) {
|
||||||
|
*remaining = ctx->parser_remaining;
|
||||||
|
}
|
||||||
|
if (message_type != NULL) {
|
||||||
|
*message_type = ctx->message_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t left = ctx->recv_buf.count - parsed_count;
|
||||||
|
|
||||||
|
if (left > 0) {
|
||||||
|
memmove(ctx->recv_buf.buf, &ctx->recv_buf.buf[parsed_count], left);
|
||||||
|
}
|
||||||
|
ctx->recv_buf.count = left;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == 0) {
|
ctx->recv_buf.count -= parsed_count;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->tmp_buf_pos = ret;
|
} while (true);
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->tmp_buf_pos <= buf_len) {
|
|
||||||
/* Is there already any data in the temp buffer? If yes,
|
|
||||||
* just return it to the caller.
|
|
||||||
*/
|
|
||||||
can_copy = MIN(ctx->message_len - ctx->total_read,
|
|
||||||
ctx->tmp_buf_pos);
|
|
||||||
} else {
|
|
||||||
/* We have more data in tmp buffer that will fit into
|
|
||||||
* user buffer.
|
|
||||||
*/
|
|
||||||
can_copy = MIN(ctx->message_len - ctx->total_read, buf_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
left = ctx->tmp_buf_pos - can_copy;
|
|
||||||
|
|
||||||
NET_ASSERT(ctx->tmp_buf_pos >= can_copy);
|
|
||||||
|
|
||||||
memmove(buf, ctx->tmp_buf, can_copy);
|
|
||||||
recv_len = can_copy;
|
|
||||||
|
|
||||||
if (left > 0) {
|
|
||||||
memmove(ctx->tmp_buf, &ctx->tmp_buf[can_copy], left);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->tmp_buf_pos = left;
|
|
||||||
ctx->total_read += recv_len;
|
|
||||||
|
|
||||||
/* Unmask the data */
|
/* Unmask the data */
|
||||||
if (ctx->masked) {
|
if (ctx->masked) {
|
||||||
/* As we might have less than 4 received bytes, we must select
|
uint8_t *mask_as_bytes = (uint8_t *)&ctx->masking_value;
|
||||||
* which byte from masking value to take. The mask_shift will
|
size_t data_buf_offset = ctx->message_len - ctx->parser_remaining - payload.count;
|
||||||
* tell that.
|
|
||||||
*/
|
|
||||||
int mask_shift = (ctx->total_read - recv_len) % sizeof(uint32_t);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < recv_len; i++) {
|
for (size_t i = 0; i < payload.count; i++) {
|
||||||
buf[i] ^= ctx->masking_value >>
|
size_t m = data_buf_offset % 4;
|
||||||
(8 * (3 - (i + mask_shift) % 4));
|
|
||||||
|
payload.buf[i] ^= mask_as_bytes[3 - m];
|
||||||
|
data_buf_offset++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HEXDUMP_RECV_PACKETS
|
return payload.count;
|
||||||
LOG_HEXDUMP_DBG(buf, recv_len, "Payload");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (remaining) {
|
|
||||||
*remaining = ctx->message_len - ctx->total_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start to read the header again if all the data has been received */
|
|
||||||
if (ctx->message_len == ctx->total_read) {
|
|
||||||
ctx->header_received = false;
|
|
||||||
ctx->message_len = 0;
|
|
||||||
ctx->message_type = 0;
|
|
||||||
ctx->total_read = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return recv_len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int websocket_send(struct websocket_context *ctx, const uint8_t *buf,
|
static int websocket_send(struct websocket_context *ctx, const uint8_t *buf,
|
||||||
|
@ -1027,8 +1006,12 @@ static int websocket_recv(struct websocket_context *ctx, uint8_t *buf,
|
||||||
ret = websocket_recv_msg(ctx->sock, buf, buf_len, &message_type,
|
ret = websocket_recv_msg(ctx->sock, buf, buf_len, &message_type,
|
||||||
&remaining, timeout);
|
&remaining, timeout);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
errno = -ret;
|
if (ret == -ENOTCONN) {
|
||||||
return -1;
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
errno = -ret;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NET_DBG("[%p] Received %d bytes", ctx, ret);
|
NET_DBG("[%p] Received %d bytes", ctx, ret);
|
||||||
|
|
|
@ -23,6 +23,29 @@
|
||||||
/* From RFC 6455 chapter 4.2.2 */
|
/* From RFC 6455 chapter 4.2.2 */
|
||||||
#define WS_MAGIC "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
#define WS_MAGIC "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Websocket parser states
|
||||||
|
*/
|
||||||
|
enum websocket_parser_state {
|
||||||
|
WEBSOCKET_PARSER_STATE_OPCODE,
|
||||||
|
WEBSOCKET_PARSER_STATE_LENGTH,
|
||||||
|
WEBSOCKET_PARSER_STATE_EXT_LEN,
|
||||||
|
WEBSOCKET_PARSER_STATE_MASK,
|
||||||
|
WEBSOCKET_PARSER_STATE_PAYLOAD,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description of external buffers for payload and receiving
|
||||||
|
*/
|
||||||
|
struct websocket_buffer {
|
||||||
|
/* external buffer */
|
||||||
|
uint8_t *buf;
|
||||||
|
/* size of external buffer */
|
||||||
|
size_t size;
|
||||||
|
/* data length in external buffer */
|
||||||
|
size_t count;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Websocket connection information
|
* Websocket connection information
|
||||||
*/
|
*/
|
||||||
|
@ -59,19 +82,12 @@ __net_socket struct websocket_context {
|
||||||
int sock;
|
int sock;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Temporary buffers used for HTTP handshakes and Websocket protocol
|
/** Buffer for receiving from TCP socket.
|
||||||
* headers. User must provide the actual buffer where the headers are
|
* This buffer used for HTTP handshakes and Websocket packet parser.
|
||||||
|
* User must provide the actual buffer where the data are
|
||||||
* stored temporarily.
|
* stored temporarily.
|
||||||
*/
|
*/
|
||||||
uint8_t *tmp_buf;
|
struct websocket_buffer recv_buf;
|
||||||
|
|
||||||
/** Temporary buffer length.
|
|
||||||
*/
|
|
||||||
size_t tmp_buf_len;
|
|
||||||
|
|
||||||
/** Current reading position in the tmp_buf
|
|
||||||
*/
|
|
||||||
size_t tmp_buf_pos;
|
|
||||||
|
|
||||||
/** The real TCP socket to use when sending Websocket data to peer.
|
/** The real TCP socket to use when sending Websocket data to peer.
|
||||||
*/
|
*/
|
||||||
|
@ -80,15 +96,18 @@ __net_socket struct websocket_context {
|
||||||
/** Websocket connection masking value */
|
/** Websocket connection masking value */
|
||||||
uint32_t masking_value;
|
uint32_t masking_value;
|
||||||
|
|
||||||
/** Amount of data received. */
|
|
||||||
uint64_t total_read;
|
|
||||||
|
|
||||||
/** Message length */
|
/** Message length */
|
||||||
uint64_t message_len;
|
uint64_t message_len;
|
||||||
|
|
||||||
/** Message type */
|
/** Message type */
|
||||||
uint32_t message_type;
|
uint32_t message_type;
|
||||||
|
|
||||||
|
/** Parser remaining length in current state */
|
||||||
|
uint64_t parser_remaining;
|
||||||
|
|
||||||
|
/** Parser state */
|
||||||
|
enum websocket_parser_state parser_state;
|
||||||
|
|
||||||
/** Is the message masked */
|
/** Is the message masked */
|
||||||
uint8_t masked : 1;
|
uint8_t masked : 1;
|
||||||
|
|
||||||
|
@ -100,11 +119,28 @@ __net_socket struct websocket_context {
|
||||||
|
|
||||||
/** Did we receive all from peer during HTTP handshake */
|
/** Did we receive all from peer during HTTP handshake */
|
||||||
uint8_t all_received : 1;
|
uint8_t all_received : 1;
|
||||||
|
|
||||||
/** Header received */
|
|
||||||
uint8_t header_received : 1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if defined(CONFIG_NET_TEST)
|
||||||
|
/**
|
||||||
|
* Websocket unit test does not use socket layer but feeds
|
||||||
|
* the data directly here when testing this function.
|
||||||
|
*/
|
||||||
|
struct test_data {
|
||||||
|
/** pointer to data "tx" buffer */
|
||||||
|
uint8_t *input_buf;
|
||||||
|
|
||||||
|
/** "tx" buffer data length */
|
||||||
|
size_t input_len;
|
||||||
|
|
||||||
|
/** "tx" buffer read (recv) position */
|
||||||
|
size_t input_pos;
|
||||||
|
|
||||||
|
/** external test context */
|
||||||
|
struct websocket_context *ctx;
|
||||||
|
};
|
||||||
|
#endif /* CONFIG_NET_TEST */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Disconnect the Websocket.
|
* @brief Disconnect the Websocket.
|
||||||
*
|
*
|
||||||
|
|
|
@ -52,12 +52,6 @@ static uint8_t temp_recv_buf[MAX_RECV_BUF_LEN + EXTRA_BUF_SPACE];
|
||||||
static uint8_t feed_buf[MAX_RECV_BUF_LEN + EXTRA_BUF_SPACE];
|
static uint8_t feed_buf[MAX_RECV_BUF_LEN + EXTRA_BUF_SPACE];
|
||||||
static size_t test_msg_len;
|
static size_t test_msg_len;
|
||||||
|
|
||||||
struct test_data {
|
|
||||||
uint8_t *input_buf;
|
|
||||||
size_t input_len;
|
|
||||||
struct websocket_context *ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int test_recv_buf(uint8_t *feed_buf, size_t feed_len,
|
static int test_recv_buf(uint8_t *feed_buf, size_t feed_len,
|
||||||
struct websocket_context *ctx,
|
struct websocket_context *ctx,
|
||||||
uint32_t *msg_type, uint64_t *remaining,
|
uint32_t *msg_type, uint64_t *remaining,
|
||||||
|
@ -69,6 +63,7 @@ static int test_recv_buf(uint8_t *feed_buf, size_t feed_len,
|
||||||
test_data.ctx = ctx;
|
test_data.ctx = ctx;
|
||||||
test_data.input_buf = feed_buf;
|
test_data.input_buf = feed_buf;
|
||||||
test_data.input_len = feed_len;
|
test_data.input_len = feed_len;
|
||||||
|
test_data.input_pos = 0;
|
||||||
|
|
||||||
ctx_ptr = POINTER_TO_INT(&test_data);
|
ctx_ptr = POINTER_TO_INT(&test_data);
|
||||||
|
|
||||||
|
@ -103,6 +98,9 @@ static const unsigned char frame2[] = {
|
||||||
0xe9, 0xdc
|
0xe9, 0xdc
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Empty websocket frame, opcode is ping, without mask */
|
||||||
|
static const unsigned char ping[] = {0x89, 0x00};
|
||||||
|
|
||||||
#define FRAME1_HDR_SIZE (sizeof(frame1) - (sizeof(frame1_msg) - 1))
|
#define FRAME1_HDR_SIZE (sizeof(frame1) - (sizeof(frame1_msg) - 1))
|
||||||
|
|
||||||
static void test_recv(int count)
|
static void test_recv(int count)
|
||||||
|
@ -115,9 +113,9 @@ static void test_recv(int count)
|
||||||
|
|
||||||
memset(&ctx, 0, sizeof(ctx));
|
memset(&ctx, 0, sizeof(ctx));
|
||||||
|
|
||||||
ctx.tmp_buf = temp_recv_buf;
|
ctx.recv_buf.buf = temp_recv_buf;
|
||||||
ctx.tmp_buf_len = sizeof(temp_recv_buf);
|
ctx.recv_buf.size = sizeof(temp_recv_buf);
|
||||||
ctx.tmp_buf_pos = 0;
|
ctx.recv_buf.count = 0;
|
||||||
|
|
||||||
memcpy(feed_buf, &frame1, sizeof(frame1));
|
memcpy(feed_buf, &frame1, sizeof(frame1));
|
||||||
|
|
||||||
|
@ -173,6 +171,7 @@ static void test_recv(int count)
|
||||||
frame1_msg, recv_buf);
|
frame1_msg, recv_buf);
|
||||||
|
|
||||||
zassert_equal(remaining, 0, "Msg not empty");
|
zassert_equal(remaining, 0, "Msg not empty");
|
||||||
|
zassert_equal(msg_type & WEBSOCKET_FLAG_TEXT, WEBSOCKET_FLAG_TEXT, "Msg is not text");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_recv_1_byte(void)
|
static void test_recv_1_byte(void)
|
||||||
|
@ -225,6 +224,28 @@ static void test_recv_whole_msg(void)
|
||||||
test_recv(sizeof(frame1));
|
test_recv(sizeof(frame1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_recv_empty_ping(void)
|
||||||
|
{
|
||||||
|
struct websocket_context ctx;
|
||||||
|
int total_read = 0;
|
||||||
|
uint32_t msg_type = -1;
|
||||||
|
uint64_t remaining = -1;
|
||||||
|
|
||||||
|
memset(&ctx, 0, sizeof(ctx));
|
||||||
|
|
||||||
|
ctx.recv_buf.buf = temp_recv_buf;
|
||||||
|
ctx.recv_buf.size = sizeof(temp_recv_buf);
|
||||||
|
ctx.recv_buf.count = 0;
|
||||||
|
|
||||||
|
memcpy(feed_buf, &ping, sizeof(ping));
|
||||||
|
|
||||||
|
total_read = test_recv_buf(&feed_buf[0], sizeof(ping), &ctx, &msg_type, &remaining,
|
||||||
|
recv_buf, sizeof(recv_buf));
|
||||||
|
|
||||||
|
zassert_equal(total_read, 0, "Msg not empty (ret %d)", total_read);
|
||||||
|
zassert_equal(msg_type & WEBSOCKET_FLAG_PING, WEBSOCKET_FLAG_PING, "Msg is not ping");
|
||||||
|
}
|
||||||
|
|
||||||
static void test_recv_2(int count)
|
static void test_recv_2(int count)
|
||||||
{
|
{
|
||||||
struct websocket_context ctx;
|
struct websocket_context ctx;
|
||||||
|
@ -235,8 +256,8 @@ static void test_recv_2(int count)
|
||||||
|
|
||||||
memset(&ctx, 0, sizeof(ctx));
|
memset(&ctx, 0, sizeof(ctx));
|
||||||
|
|
||||||
ctx.tmp_buf = temp_recv_buf;
|
ctx.recv_buf.buf = temp_recv_buf;
|
||||||
ctx.tmp_buf_len = sizeof(temp_recv_buf);
|
ctx.recv_buf.size = sizeof(temp_recv_buf);
|
||||||
|
|
||||||
memcpy(feed_buf, &frame2, sizeof(frame2));
|
memcpy(feed_buf, &frame2, sizeof(frame2));
|
||||||
|
|
||||||
|
@ -251,17 +272,19 @@ static void test_recv_2(int count)
|
||||||
frame1_msg, recv_buf);
|
frame1_msg, recv_buf);
|
||||||
|
|
||||||
zassert_equal(remaining, 0, "Msg not empty");
|
zassert_equal(remaining, 0, "Msg not empty");
|
||||||
|
zassert_equal(msg_type & WEBSOCKET_FLAG_TEXT, WEBSOCKET_FLAG_TEXT, "Msg is not text");
|
||||||
|
|
||||||
/* Then read again, now we should get EAGAIN as the second message
|
/* Then read again. Take in account that part of second frame
|
||||||
* header is partially read.
|
* have read from tx buffer to rx buffer.
|
||||||
*/
|
*/
|
||||||
ret = test_recv_buf(&feed_buf[sizeof(frame1)], count, &ctx, &msg_type,
|
ret = test_recv_buf(&feed_buf[count], sizeof(frame2) - count, &ctx, &msg_type, &remaining,
|
||||||
&remaining, recv_buf, sizeof(recv_buf));
|
recv_buf, sizeof(recv_buf));
|
||||||
|
|
||||||
zassert_equal(ret, sizeof(frame1_msg) - 1,
|
zassert_mem_equal(recv_buf, frame1_msg, sizeof(frame1_msg) - 1,
|
||||||
"2nd header parse failed (ret %d)", ret);
|
"Invalid 2nd message, should be '%s' was '%s'", frame1_msg, recv_buf);
|
||||||
|
|
||||||
zassert_equal(remaining, 0, "Msg not empty");
|
zassert_equal(remaining, 0, "Msg not empty");
|
||||||
|
zassert_equal(msg_type & WEBSOCKET_FLAG_TEXT, WEBSOCKET_FLAG_TEXT, "Msg is not text");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_recv_two_msg(void)
|
static void test_recv_two_msg(void)
|
||||||
|
@ -279,15 +302,19 @@ int verify_sent_and_received_msg(struct msghdr *msg, bool split_msg)
|
||||||
|
|
||||||
memset(&ctx, 0, sizeof(ctx));
|
memset(&ctx, 0, sizeof(ctx));
|
||||||
|
|
||||||
ctx.tmp_buf = temp_recv_buf;
|
ctx.recv_buf.buf = temp_recv_buf;
|
||||||
ctx.tmp_buf_len = sizeof(temp_recv_buf);
|
ctx.recv_buf.size = sizeof(temp_recv_buf);
|
||||||
|
|
||||||
/* Read first the header */
|
/* Read first the header */
|
||||||
ret = test_recv_buf(msg->msg_iov[0].iov_base,
|
ret = test_recv_buf(msg->msg_iov[0].iov_base,
|
||||||
msg->msg_iov[0].iov_len,
|
msg->msg_iov[0].iov_len,
|
||||||
&ctx, &msg_type, &remaining,
|
&ctx, &msg_type, &remaining,
|
||||||
recv_buf, sizeof(recv_buf));
|
recv_buf, sizeof(recv_buf));
|
||||||
zassert_equal(ret, -EAGAIN, "Msg header not found");
|
if (remaining > 0) {
|
||||||
|
zassert_equal(ret, -EAGAIN, "Msg header not found");
|
||||||
|
} else {
|
||||||
|
zassert_equal(ret, 0, "Msg header read error (ret %d)", ret);
|
||||||
|
}
|
||||||
|
|
||||||
/* Then the first split if it is enabled */
|
/* Then the first split if it is enabled */
|
||||||
if (split_msg) {
|
if (split_msg) {
|
||||||
|
@ -339,8 +366,8 @@ static void test_send_and_recv_lorem_ipsum(void)
|
||||||
|
|
||||||
memset(&ctx, 0, sizeof(ctx));
|
memset(&ctx, 0, sizeof(ctx));
|
||||||
|
|
||||||
ctx.tmp_buf = temp_recv_buf;
|
ctx.recv_buf.buf = temp_recv_buf;
|
||||||
ctx.tmp_buf_len = sizeof(temp_recv_buf);
|
ctx.recv_buf.size = sizeof(temp_recv_buf);
|
||||||
|
|
||||||
test_msg_len = sizeof(lorem_ipsum) - 1;
|
test_msg_len = sizeof(lorem_ipsum) - 1;
|
||||||
|
|
||||||
|
@ -360,8 +387,8 @@ static void test_recv_two_large_split_msg(void)
|
||||||
|
|
||||||
memset(&ctx, 0, sizeof(ctx));
|
memset(&ctx, 0, sizeof(ctx));
|
||||||
|
|
||||||
ctx.tmp_buf = temp_recv_buf;
|
ctx.recv_buf.buf = temp_recv_buf;
|
||||||
ctx.tmp_buf_len = sizeof(temp_recv_buf);
|
ctx.recv_buf.size = sizeof(temp_recv_buf);
|
||||||
|
|
||||||
test_msg_len = sizeof(lorem_ipsum) - 1;
|
test_msg_len = sizeof(lorem_ipsum) - 1;
|
||||||
|
|
||||||
|
@ -373,12 +400,67 @@ static void test_recv_two_large_split_msg(void)
|
||||||
test_msg_len, ret);
|
test_msg_len, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_send_and_recv_empty_pong(void)
|
||||||
|
{
|
||||||
|
static struct websocket_context ctx;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
memset(&ctx, 0, sizeof(ctx));
|
||||||
|
|
||||||
|
ctx.recv_buf.buf = temp_recv_buf;
|
||||||
|
ctx.recv_buf.size = sizeof(temp_recv_buf);
|
||||||
|
|
||||||
|
test_msg_len = 0;
|
||||||
|
|
||||||
|
ret = websocket_send_msg(POINTER_TO_INT(&ctx), NULL, test_msg_len, WEBSOCKET_OPCODE_PING,
|
||||||
|
true, true, SYS_FOREVER_MS);
|
||||||
|
zassert_equal(ret, test_msg_len, "Should have sent %zd bytes but sent %d instead",
|
||||||
|
test_msg_len, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_recv_in_small_buffer(void)
|
||||||
|
{
|
||||||
|
struct websocket_context ctx;
|
||||||
|
uint32_t msg_type = -1;
|
||||||
|
uint64_t remaining = -1;
|
||||||
|
int total_read = 0;
|
||||||
|
int ret;
|
||||||
|
const size_t frame1_msg_size = sizeof(frame1_msg) - 1;
|
||||||
|
const size_t recv_buf_size = 7;
|
||||||
|
|
||||||
|
memset(&ctx, 0, sizeof(ctx));
|
||||||
|
|
||||||
|
ctx.recv_buf.buf = temp_recv_buf;
|
||||||
|
ctx.recv_buf.size = sizeof(temp_recv_buf);
|
||||||
|
|
||||||
|
memcpy(feed_buf, &frame1, sizeof(frame1));
|
||||||
|
|
||||||
|
/* Receive first part of message */
|
||||||
|
ret = test_recv_buf(&feed_buf[0], sizeof(frame1), &ctx, &msg_type, &remaining, recv_buf,
|
||||||
|
recv_buf_size);
|
||||||
|
zassert_equal(ret, recv_buf_size, "Should have received %zd bytes but ret %d",
|
||||||
|
recv_buf_size, ret);
|
||||||
|
total_read += ret;
|
||||||
|
|
||||||
|
/* Receive second part of message */
|
||||||
|
ret = test_recv_buf(&feed_buf[sizeof(frame1)], 0, &ctx, &msg_type, &remaining,
|
||||||
|
&recv_buf[recv_buf_size], recv_buf_size);
|
||||||
|
zassert_equal(ret, frame1_msg_size - recv_buf_size,
|
||||||
|
"Should have received %zd bytes but ret %d", frame1_msg_size - recv_buf_size,
|
||||||
|
ret);
|
||||||
|
total_read += ret;
|
||||||
|
|
||||||
|
/* Check receiving whole message */
|
||||||
|
zassert_equal(total_read, frame1_msg_size, "Received not whole message");
|
||||||
|
zassert_mem_equal(recv_buf, frame1_msg, frame1_msg_size,
|
||||||
|
"Invalid message, should be '%s' was '%s'", frame1_msg, recv_buf);
|
||||||
|
}
|
||||||
|
|
||||||
void test_main(void)
|
void test_main(void)
|
||||||
{
|
{
|
||||||
k_thread_system_pool_assign(k_current_get());
|
k_thread_system_pool_assign(k_current_get());
|
||||||
|
|
||||||
ztest_test_suite(websocket,
|
ztest_test_suite(websocket, ztest_unit_test(test_recv_1_byte),
|
||||||
ztest_unit_test(test_recv_1_byte),
|
|
||||||
ztest_unit_test(test_recv_2_byte),
|
ztest_unit_test(test_recv_2_byte),
|
||||||
ztest_unit_test(test_recv_3_byte),
|
ztest_unit_test(test_recv_3_byte),
|
||||||
ztest_unit_test(test_recv_6_byte),
|
ztest_unit_test(test_recv_6_byte),
|
||||||
|
@ -388,10 +470,12 @@ void test_main(void)
|
||||||
ztest_unit_test(test_recv_10_byte),
|
ztest_unit_test(test_recv_10_byte),
|
||||||
ztest_unit_test(test_recv_12_byte),
|
ztest_unit_test(test_recv_12_byte),
|
||||||
ztest_unit_test(test_recv_whole_msg),
|
ztest_unit_test(test_recv_whole_msg),
|
||||||
|
ztest_unit_test(test_recv_empty_ping),
|
||||||
ztest_unit_test(test_recv_two_msg),
|
ztest_unit_test(test_recv_two_msg),
|
||||||
ztest_unit_test(test_send_and_recv_lorem_ipsum),
|
ztest_unit_test(test_send_and_recv_lorem_ipsum),
|
||||||
ztest_unit_test(test_recv_two_large_split_msg)
|
ztest_unit_test(test_recv_two_large_split_msg),
|
||||||
);
|
ztest_unit_test(test_send_and_recv_empty_pong),
|
||||||
|
ztest_unit_test(test_recv_in_small_buffer));
|
||||||
|
|
||||||
ztest_run_test_suite(websocket);
|
ztest_run_test_suite(websocket);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue