lock_lfvx/bk_idk/components/bk_websocket/bk_websocket_client.c
2025-10-10 16:07:00 +08:00

1448 lines
43 KiB
C
Executable File

// Copyright 2021-2022 Beken
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include <stddef.h>
#include <stdio.h>
#include <common/bk_include.h>
#include <os/mem.h>
#include <os/str.h>
#include <components/log.h>
#include <os/os.h>
#ifdef CONFIG_WEBSOCKET
#include <bk_websocket_client.h>
#include <http_parser.h>
#include <mbedtls/sha1.h>
#include "mbedtls/base64.h"
#include <netdb.h>
#define WEBSOCKET_TCP_DEFAULT_PORT 80
#define WEBSOCKET_BUFFER_SIZE_BYTE (4*1024)
#define WS_BUFFER_SIZE 1600
#define MAX_WEBSOCKET_HEADER_SIZE 16
#define WS_SIZE64 127
#define WS_MASK 0x80
#define WS_SIZE16 126
#define WEBSOCKET_TASK_PRIORITY (4)
#define WEBSOCKET_TASK_STACK (4*1024)
#define WEBSOCKET_NETWORK_TIMEOUT_MS (10*1000)
#define WEBSOCKET_PINGPONG_TIMEOUT_SEC (120)
#define WEBSOCKET_PING_INTERVAL_SEC (10)
#define WEBSOCKET_RECONNECT_TIMEOUT_MS (5*1000)
#define WEBSOCKET_RX_RETRY_COUNT (10)
websocket_event_cb websocket_cb = NULL;
void bk_websocket_register_cb(websocket_event_cb cb)
{
websocket_cb = cb;
}
void bk_websocket_push_cb(int32_t event_id, char *event_data, int data_len)
{
if(websocket_cb)
(*websocket_cb)(event_id, event_data, data_len);
}
#define TAG "WEBSOCKET"
static uint64_t bk_tick_get_ms(void);
struct timeval* utils_ms_to_timeval(int timeout_ms, struct timeval *tv);
void fill_random(void *buf, size_t len);
#define CO_BIT(pos) (1U<<(pos))
const static int PING_SENT_BIT = CO_BIT(1);
const static int TEXT_SENT_BIT = CO_BIT(2);
const static int CLOSE_SENT_BIT = CO_BIT(3);
int status_bits =0;
static char *trimwhitespace(const char *str);
static char *get_http_header(const char *buffer, const char *key);
static int ws_tcp_close(transport client);
static int ws_tcp_poll_read(int *sockfd, int timeout_ms);
static int ws_tcp_poll_write(int *sockfd, int timeout_ms);
static int ws_tcp_read(int *sockfd, char *buffer, int len, int timeout_ms);
static int ws_tcp_write(int *sockfd, const char *buffer, int len, int timeout_ms);
static int ws_read_payload(transport client, char *buffer, int len, int timeout_ms);
static int ws_read_header(transport client, char *buffer, int len, int timeout_ms);
static int ws_write(transport client, int opcode, int mask_flag, const char *b, int len, int timeout_ms);
static int ws_read(transport client, char *buffer, int len, int timeout_ms);
static int ws_poll_connection_closed(int *sockfd, int timeout_ms);
static int ws_client_recv(transport client);
static bk_err_t set_socket_non_blocking(int fd, bool non_blocking);
static bk_err_t hostname_to_fd(const char *host, size_t hostlen, int port, struct sockaddr_storage *address, int* fd);
static bk_err_t _tcp_connect(int *sockfd, const char *host, int hostlen, int port, int timeout_ms);
static int ws_tcp_connect(int *sockfd, const char *host, int port, int timeout_ms);
static bk_err_t ws_disconnect(transport client);
static int ws_connect(transport client, const char *host, int port, int timeout_ms);
static bk_err_t websocket_client_destory_config(transport client);
static char *trimwhitespace(const char *str)
{
char *end;
// Trim leading space
while (isspace((unsigned char)*str)) str++;
if (*str == 0) {
return (char *)str;
}
// Trim trailing space
end = (char *)(str + strlen(str) - 1);
while (end > str && isspace((unsigned char)*end)) end--;
// Write new null terminator
*(end + 1) = 0;
return (char *)str;
}
static char *get_http_header(const char *buffer, const char *key)
{
char *found = strcasestr(buffer, key);
if (found) {
found += strlen(key);
char *found_end = strstr(found, "\r\n");
if (found_end) {
found_end[0] = 0;
return trimwhitespace(found);
}
}
return NULL;
}
static void bk_hex_dump(char *s, int length)
{
//os_printf("bk begin dump:\r\n");
for (int i = 0; i < length; i++)
os_printf("%c", *(u8 *)(s+i));
os_printf("\r\n");
}
static int ws_tcp_close(transport client)
{
int ret = -1;
if (client->sockfd >= 0) {
BK_LOGE(TAG, "%s, sockfd used, closing\r\n", __func__);
ret = close(client->sockfd);
client->sockfd = -1;
}
return ret;
}
static int ws_tcp_poll_read(int *sockfd, int timeout_ms)
{
int ret = -1;
struct timeval timeout;
fd_set readset;
fd_set errset;
FD_ZERO(&readset);
FD_ZERO(&errset);
FD_SET(*sockfd, &readset);
FD_SET(*sockfd, &errset);
ret = select((*sockfd) + 1, &readset, NULL, &errset, utils_ms_to_timeval(timeout_ms, &timeout));
if (ret > 0 && FD_ISSET(*sockfd, &errset)) {
int sock_errno = 0;
uint32_t optlen = sizeof(sock_errno);
getsockopt(*sockfd, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen);
BK_LOGE(TAG, "poll_read select error %d, errno = %s, fd = %d", sock_errno, strerror(sock_errno), sockfd);
ret = BK_FAIL;
}
//BK_LOGD(TAG, "%s, ret = %d\r\n", __func__, ret);
return ret;
}
static int ws_tcp_poll_write(int *sockfd, int timeout_ms)
{
int ret = -1;
struct timeval timeout;
fd_set writeset;
fd_set errset;
FD_ZERO(&writeset);
FD_ZERO(&errset);
FD_SET(*sockfd, &writeset);
FD_SET(*sockfd, &errset);
ret = select((*sockfd) + 1, NULL, &writeset, &errset, utils_ms_to_timeval(timeout_ms, &timeout));
if (ret > 0 && FD_ISSET(*sockfd, &errset)) {
int sock_errno = 0;
uint32_t optlen = sizeof(sock_errno);
getsockopt(*sockfd, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen);
BK_LOGE(TAG, "poll_write select error %d, errno = %s, fd = %d\r\n", sock_errno, strerror(sock_errno), *sockfd);
ret = BK_FAIL;
}
//BK_LOGE(TAG, "%s [sockfd:%d] poll wirte ret:%d\r\n", __func__, *sockfd, ret);
return ret;
}
static int ws_tcp_read(int *sockfd, char *buffer, int len, int timeout_ms)
{
int poll;
if ((poll = ws_tcp_poll_read(sockfd, timeout_ms)) <= 0) {
return poll;
}
int ret = recv(*sockfd, (unsigned char *)buffer, len, 0);
if (ret < 0) {
BK_LOGE(TAG, "tcp_read error, errno=%s", strerror(errno));
}
if (ret == 0) {
if (poll > 0) {
// no error
BK_LOGE(TAG, "%s select > 0, ret = 0 \r\n", __func__);
}
ret = BK_FAIL;
}
//BK_LOGE(TAG, "%s, ret:%d\r\n", __func__, ret);
return ret;
}
static int ws_tcp_write(int *sockfd, const char *buffer, int len, int timeout_ms)
{
int poll;
if ((poll = ws_tcp_poll_write(sockfd, timeout_ms)) <= 0) {
BK_LOGE(TAG, "Poll timeout or error, errno=%s, fd=%d, timeout_ms=%d\r\n", strerror(errno), sockfd, timeout_ms);
return poll;
}
int ret = send((*sockfd),(const unsigned char *) buffer, len, 0);
if (ret < 0) {
BK_LOGE(TAG, "tcp_write error, errno=%s", strerror(errno));
}
//BK_LOGE(TAG, "%s, write send len:%d\r\n", __func__, ret);
return ret;
}
static int ws_read_payload(transport client, char *buffer, int len, int timeout_ms)
{
int bytes_to_read;
int rlen = 0;
transport_ws_t *ws = client->ws_transport;
if (ws->frame_state.bytes_remaining > len) {
BK_LOGE(TAG, "Actual data to receive (%d) are longer than ws buffer (%d)\r\n", ws->frame_state.bytes_remaining, len);
bytes_to_read = len;
} else {
bytes_to_read = ws->frame_state.bytes_remaining;
}
if (bytes_to_read != 0 && (rlen = ws_tcp_read(&(client->sockfd), buffer, bytes_to_read, timeout_ms)) <= 0) {
BK_LOGE(TAG, "Error read payload data\r\n");
return rlen;
}
ws->frame_state.bytes_remaining -= rlen;
if (ws->frame_state.mask_key) {
for (int i = 0; i < bytes_to_read; i++) {
buffer[i] = (buffer[i] ^ ws->frame_state.mask_key[i % 4]);
}
}
return rlen;
}
/* Read and parse the WS header, determine length of payload */
static int ws_read_header(transport client, char *buffer, int len, int timeout_ms)
{
int payload_len;
transport_ws_t *ws = client->ws_transport;
char ws_header[MAX_WEBSOCKET_HEADER_SIZE];
char *data_ptr = ws_header, mask;
int rlen;
int poll_read;
ws->frame_state.header_received = false;
if ((poll_read = ws_tcp_poll_read(&(client->sockfd), timeout_ms)) <= 0) {
BK_LOGE(TAG, "error poll read data\r\n");
return poll_read;
}
int header = 2;
int mask_len = 4;
if ((rlen = ws_tcp_read(&(client->sockfd), data_ptr, header, timeout_ms)) <= 0) {
BK_LOGE(TAG, "first header, Error read data\r\n");
return rlen;
}
ws->frame_state.header_received = true;
ws->frame_state.opcode = (*data_ptr & 0x0F);
data_ptr ++;
mask = ((*data_ptr >> 7) & 0x01);
payload_len = (*data_ptr & 0x7F);
data_ptr++;
// BK_LOGE(TAG, "%s, Opcode: %d, mask: %d, payload len: %d\r\n", __func__, ws->frame_state.opcode, mask, payload_len);
if (payload_len == 126) {
if ((rlen = ws_tcp_read(&(client->sockfd), data_ptr, header, timeout_ms)) <= 0) {
BK_LOGE(TAG, "126 read: Error read data\r\n");
return rlen;
}
payload_len = ((data_ptr[0] << 8) & 0xFF00l) | (data_ptr[1] & 0xFFl);
//BK_LOGE(TAG, "%s, 126, payload_len:%c\r\n", __func__, payload_len);
} else if (payload_len == 127) {
header = 8;
if ((rlen = ws_tcp_read(&(client->sockfd), data_ptr, header, timeout_ms)) <= 0) {
BK_LOGE(TAG, "127 read: Error read data\r\n");
return rlen;
}
if (data_ptr[0] != 0 || data_ptr[1] != 0 || data_ptr[2] != 0 || data_ptr[3] != 0) {
// really too big!
payload_len = 0xFFFFFFFF;
} else {
payload_len = data_ptr[4] << 24 | data_ptr[5] << 16 | data_ptr[6] << 8 | data_ptr[7];
}
//BK_LOGD(TAG, "%s, 127, payload_len:%d\r\n", __func__, payload_len);
}
if (mask) {
// Read and store mask
if (payload_len != 0 && (rlen = ws_tcp_read(&(client->sockfd), buffer, mask_len, timeout_ms)) <= 0) {
BK_LOGE(TAG, "mask error read data\r\n");
return rlen;
}
memcpy(ws->frame_state.mask_key, buffer, mask_len);
} else {
memset(ws->frame_state.mask_key, 0, mask_len);
}
ws->frame_state.payload_len = payload_len;
ws->frame_state.bytes_remaining = payload_len;
return payload_len;
}
static int ws_write(transport client, int opcode, int mask_flag, const char *b, int len, int timeout_ms)
{
char *buffer = (char *)b;
char ws_header[MAX_WEBSOCKET_HEADER_SIZE];
char *mask;
int header_len = 0, i;
int poll_write;
if ((poll_write = ws_tcp_poll_write(&(client->sockfd), timeout_ms)) <= 0) {
BK_LOGE(TAG, "Error ws_tcp_poll_write");
return poll_write;
}
ws_header[header_len++] = opcode;
if (len <= 125) {
ws_header[header_len++] = (uint8_t)(len | mask_flag);
} else if (len < 65536) {
ws_header[header_len++] = WS_SIZE16 | mask_flag;
ws_header[header_len++] = (uint8_t)(len >> 8);
ws_header[header_len++] = (uint8_t)(len & 0xFF);
} else {
ws_header[header_len++] = WS_SIZE64 | mask_flag;
/* Support maximum 4 bytes length */
ws_header[header_len++] = 0; //(uint8_t)((len >> 56) & 0xFF);
ws_header[header_len++] = 0; //(uint8_t)((len >> 48) & 0xFF);
ws_header[header_len++] = 0; //(uint8_t)((len >> 40) & 0xFF);
ws_header[header_len++] = 0; //(uint8_t)((len >> 32) & 0xFF);
ws_header[header_len++] = (uint8_t)((len >> 24) & 0xFF);
ws_header[header_len++] = (uint8_t)((len >> 16) & 0xFF);
ws_header[header_len++] = (uint8_t)((len >> 8) & 0xFF);
ws_header[header_len++] = (uint8_t)((len >> 0) & 0xFF);
}
if (mask_flag) {
mask = &ws_header[header_len];
fill_random(ws_header + header_len, 4);
header_len += 4;
for (i = 0; i < len; ++i) {
buffer[i] = (buffer[i] ^ mask[i % 4]);
}
}
BK_LOGE(TAG, "%s, ws header len:%d\r\n", __func__, header_len);
if (ws_tcp_write(&(client->sockfd), ws_header, header_len, timeout_ms) != header_len) {
BK_LOGE(TAG, "Error write header\r\n");
return BK_FAIL;
}
if (len == 0) {
return 0;
}
BK_LOGE(TAG, "%s, payload buffer len:%d\r\n", __func__, len);
int ret = ws_tcp_write(&(client->sockfd), buffer, len, timeout_ms);
if (mask_flag) {
mask = &ws_header[header_len-4];
for (i = 0; i < len; ++i) {
buffer[i] = (buffer[i] ^ mask[i % 4]);
}
}
return ret;;
}
static int ws_read(transport client, char *buffer, int len, int timeout_ms)
{
int rlen = 0;
transport_ws_t *ws = client->ws_transport;
if (ws->frame_state.bytes_remaining <= 0) {
if ((rlen = ws_read_header(client, buffer, len, timeout_ms)) < 0) {
BK_LOGE(TAG, "Error reading header %d\r\n", rlen);
ws->frame_state.bytes_remaining = 0;
return rlen;
}
if (rlen == 0) {
ws->frame_state.bytes_remaining = 0;
return BK_OK;
}
}
if (ws->frame_state.payload_len) {
if ((rlen = ws_read_payload(client, buffer, len, timeout_ms)) <= 0) {
BK_LOGE(TAG, "Error reading payload data\r\n");
ws->frame_state.bytes_remaining = 0;
return rlen;
}
BK_LOGE(TAG, "%s, payload len:%d\r\n", __func__, rlen);
}
return rlen;
}
static uint64_t bk_tick_get_ms(void)
{
return rtos_get_time();
}
struct timeval* utils_ms_to_timeval(int timeout_ms, struct timeval *tv)
{
if (timeout_ms == -1) {
return NULL;
}
tv->tv_sec = timeout_ms / 1000;
tv->tv_usec = (timeout_ms - (tv->tv_sec * 1000)) * 1000;
return tv;
}
static void ms_to_timeval(int timeout_ms, struct timeval *tv)
{
tv->tv_sec = timeout_ms / 1000;
tv->tv_usec = (timeout_ms % 1000) * 1000;
}
static bk_err_t set_socket_non_blocking(int fd, bool non_blocking)
{
int flags;
if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
BK_LOGE(TAG, "[sock=%d] get file flags error: %s", fd, strerror(errno));
return BK_FAIL;
}
if (non_blocking) {
flags |= O_NONBLOCK;
} else {
flags &= ~O_NONBLOCK;
}
if (fcntl(fd, F_SETFL, flags) < 0) {
BK_LOGE(TAG, "[sock=%d] set blocking/nonblocking error: %s", fd, strerror(errno));
return BK_FAIL;
}
BK_LOGE(TAG, "[sock=%d] %s, non_block:%d\r\n", fd, __func__, non_blocking);
return BK_OK;
}
static bk_err_t hostname_to_fd(const char *host, size_t hostlen, int port, struct sockaddr_storage *address, int* fd)
{
struct addrinfo *address_info;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
char *use_host = strndup(host, hostlen);
if (!use_host) {
return BK_FAIL;
}
BK_LOGD(TAG, "host:%s: strlen %lu\r\n", use_host, (unsigned long)hostlen);
int res = getaddrinfo(use_host, NULL, &hints, &address_info);
if (res != 0 || address_info == NULL) {
BK_LOGE(TAG, "couldn't get hostname for :%s: "
"getaddrinfo() returns %d, addrinfo=%p", use_host, res, address_info);
free(use_host);
return BK_FAIL;
}
free(use_host);
*fd = socket(address_info->ai_family, address_info->ai_socktype, address_info->ai_protocol);
if (*fd < 0) {
BK_LOGE(TAG, "Failed to create socket (family %d socktype %d protocol %d)", address_info->ai_family, address_info->ai_socktype, address_info->ai_protocol);
freeaddrinfo(address_info);
return BK_FAIL;
}
if (address_info->ai_family == AF_INET) {
struct sockaddr_in *p = (struct sockaddr_in *)address_info->ai_addr;
p->sin_port = htons(port);
BK_LOGE(TAG, "[sock=%d] Resolved IPv4 address: %s\r\n", *fd, ipaddr_ntoa((const ip_addr_t*)&p->sin_addr.s_addr));
memcpy(address, p, sizeof(struct sockaddr ));
}
#if LWIP_IPV6
else if (address_info->ai_family == AF_INET6) {
struct sockaddr_in6 *p = (struct sockaddr_in6 *)address_info->ai_addr;
p->sin6_port = htons(port);
p->sin6_family = AF_INET6;
BK_LOGE(TAG, "[sock=%d] Resolved IPv6 address: %s", *fd, ip6addr_ntoa((const ip6_addr_t*)&p->sin6_addr));
memcpy(address, p, sizeof(struct sockaddr_in6 ));
}
#endif
else {
BK_LOGE(TAG, "Unsupported protocol family %d", address_info->ai_family);
close(*fd);
freeaddrinfo(address_info);
return BK_FAIL;
}
freeaddrinfo(address_info);
return BK_OK;
}
static bk_err_t _tcp_connect(int *sockfd, const char *host, int hostlen, int port, int timeout_ms)
{
struct sockaddr_storage address;
int fd;
bk_err_t ret = hostname_to_fd(host, hostlen, port, &address, &fd);
if (ret != BK_OK) {
BK_LOGE(TAG, "%s error fd\r\n", __func__);
return ret;
}
if(timeout_ms) {
struct timeval tv;
ms_to_timeval(timeout_ms, &tv);
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) {
BK_LOGE(TAG, "Fail to setsockopt SO_RCVTIMEO");
return BK_FAIL;
}
if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) {
BK_LOGE(TAG, "Fail to setsockopt SO_SNDTIMEO");
return BK_FAIL;
}
}
// Set to non block before connecting to better control connection timeout
ret = set_socket_non_blocking(fd, true);
if (ret != BK_OK) {
BK_LOGE(TAG, "%s error nb\r\n", __func__);
goto err;
}
BK_LOGE(TAG, "[sock=%d] Connecting to server. HOST: %s, Port: %d\r\n", fd, host, port);
if (connect(fd, (struct sockaddr *)&address, sizeof(struct sockaddr)) < 0) {
if (errno == EINPROGRESS) {
BK_LOGE(TAG, "EINPROGRESS\r\n");
fd_set fdset;
struct timeval tv = { .tv_usec = 0, .tv_sec = 10 }; // Default connection timeout is 10 s
if (timeout_ms > 0 ) {
ms_to_timeval(timeout_ms, &tv);
}
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
int res = select(fd+1, NULL, &fdset, NULL, &tv);
if (res < 0) {
BK_LOGE(TAG, "[sock=%d] select() error: %s\r\n", fd, strerror(errno));
ret = BK_FAIL;
goto err;
}
else if (res == 0) {
BK_LOGE(TAG, "[sock=%d] select() timeout\r\n", fd);
ret = BK_FAIL;
goto err;
} else {
int sockerr;
socklen_t len = (socklen_t)sizeof(int);
BK_LOGE(TAG, "[sock=%d] select() occur\r\n", fd);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)(&sockerr), &len) < 0) {
BK_LOGE(TAG, "[sock=%d] getsockopt() error: %s\r\n", fd, strerror(errno));
ret = BK_FAIL;
goto err;
}
else if (sockerr) {
BK_LOGE(TAG, "[sock=%d] delayed connect error: %s\r\n", fd, strerror(sockerr));
ret = BK_FAIL;
goto err;
}
else if (!sockerr) {
BK_LOGE(TAG, "[sock=%d] no blocking connected\r\n", fd);
}
}
} else {
BK_LOGE(TAG, "[sock=%d] connect() error: %s\r\n", fd, strerror(errno));
ret = BK_FAIL;
goto err;
}
}
// reset back to blocking mode (unless non_block configured)
ret = set_socket_non_blocking(fd, false);
if (ret != BK_OK) {
BK_LOGE(TAG, "%s error nb\r\n", __func__);
goto err;
}
*sockfd = fd;
return BK_OK;
err:
close(fd);
return ret;
}
static int ws_tcp_connect(int *sockfd, const char *host, int port, int timeout_ms)
{
bk_err_t err = _tcp_connect(sockfd, host, os_strlen(host), port, timeout_ms);
if (err != BK_OK) {
BK_LOGE(TAG, "%s failed\r\n", __func__);
return BK_FAIL;
}
return BK_OK;
}
void fill_random(void *buf, size_t len)
{
uint8_t *buf_bytes = (uint8_t *)buf;
while (len > 0) {
uint32_t word = (uint32_t)rand();
uint32_t to_copy = MIN(sizeof(word), len);
memcpy(buf_bytes, &word, to_copy);
buf_bytes += to_copy;
len -= to_copy;
}
}
static bk_err_t ws_disconnect(transport client)
{
if(client == NULL) {
BK_LOGE(TAG, "client aleady null\r\n");
return BK_FAIL;
}
ws_tcp_close(client);
if(client->auto_reconnect) {
client->reconnect_tick_ms = bk_tick_get_ms();
}
client->state = WEBSOCKET_STATE_WAIT_TIMEOUT;
bk_websocket_push_cb(WEBSOCKET_EVENT_DISCONNECTED, NULL, 0);
return BK_OK;
}
static int ws_connect(transport client, const char *host, int port, int timeout_ms)
{
transport_ws_t *ws = client->ws_transport;
if(ws_tcp_connect(&(client->sockfd), host, port, timeout_ms) < 0) {
return BK_FAIL;
}
unsigned char random_key[16];
fill_random(random_key, sizeof(random_key));
unsigned char client_key[28] = {0};
const char *user_agent_ptr = (ws->user_agent)?(ws->user_agent):"BEKEN Websocket Client";
size_t outlen = 0;
mbedtls_base64_encode(client_key, sizeof(client_key), &outlen, random_key, sizeof(random_key));
int len = snprintf(ws->buffer, WS_BUFFER_SIZE,
"GET %s HTTP/1.1\r\n"
"Connection: Upgrade\r\n"
"Host: %s:%d\r\n"
"Origin: http://%s\r\n"
"User-Agent: %s\r\n"
"Upgrade: websocket\r\n"
"Sec-WebSocket-Version: 13\r\n"
"Sec-WebSocket-Key: %s\r\n",
ws->path,
host, port, host, user_agent_ptr,
client_key);
os_printf("http request:\r\n");
bk_hex_dump(ws->buffer, 200);
//BK_LOGE(TAG, "len: %d\r\n", len);
if (len <= 0 || len >= WS_BUFFER_SIZE) {
BK_LOGE(TAG, "Error in request generation, desired request len: %d, buffer size: %d\r\n", len, WS_BUFFER_SIZE);
return BK_FAIL;
}
if (ws->sub_protocol) {
BK_LOGE(TAG, "sub_protocol: %s\r\n", ws->sub_protocol);
int r = snprintf(ws->buffer + len, WS_BUFFER_SIZE - len, "Sec-WebSocket-Protocol: %s\r\n", ws->sub_protocol);
len += r;
if (r <= 0 || len >= WS_BUFFER_SIZE) {
BK_LOGE(TAG, "Error in request generation"
"(snprintf of subprotocol returned %d, desired request len: %d, buffer size: %d\r\n", r, len, WS_BUFFER_SIZE);
return BK_FAIL;
}
}
if (ws->headers) {
BK_LOGE(TAG, "headers: %s\r\n", ws->headers);
int r = snprintf(ws->buffer + len, WS_BUFFER_SIZE - len, "%s", ws->headers);
len += r;
if (r <= 0 || len >= WS_BUFFER_SIZE) {
BK_LOGE(TAG, "Error in request generation"
"(strncpy of headers returned %d, desired request len: %d, buffer size: %d\r\n", r, len, WS_BUFFER_SIZE);
return BK_FAIL;
}
}
int r = snprintf(ws->buffer + len, WS_BUFFER_SIZE - len, "\r\n");
len += r;
BK_LOGE(TAG, "%s, connect write len:%d\r\n", __func__, len);
if (r <= 0 || len >= WS_BUFFER_SIZE) {
BK_LOGE(TAG, "Error in request generation"
"(snprintf of header terminal returned %d, desired request len: %d, buffer size: %d\r\n", r, len, WS_BUFFER_SIZE);
return BK_FAIL;
}
if(ws_tcp_write(&(client->sockfd), ws->buffer, len, timeout_ms)<0) {
BK_LOGE(TAG, "Write FAIL\r\n");
}
int header_len = 0;
do {
if((len = ws_tcp_read(&(client->sockfd), ws->buffer + header_len, WS_BUFFER_SIZE - header_len, timeout_ms)) <= 0)
{
BK_LOGE(TAG, "read FAIL for upgrade header:%s\r\n", ws->buffer);
return BK_FAIL;
}
header_len += len;
ws->buffer[header_len] = '\0';
BK_LOGE(TAG, "Read header chunk %d, current header size: %d\r\n", len, header_len);
} while (NULL == os_strstr(ws->buffer, "\r\n\r\n") && header_len < WS_BUFFER_SIZE);
os_printf("server buffer:\r\n");
bk_hex_dump(ws->buffer, 200);
char *server_key = get_http_header(ws->buffer, "Sec-WebSocket-Accept:");
if (server_key == NULL) {
BK_LOGE(TAG, "Sec-WebSocket-Accept not found\r\n");
return BK_FAIL;
}
unsigned char expected_server_sha1[20];
unsigned char expected_server_key[33] = {0};
// If you are interested, see https://tools.ietf.org/html/rfc6455
const char expected_server_magic[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
unsigned char expected_server_text[sizeof(client_key) + sizeof(expected_server_magic) + 1];
os_strcpy((char*)expected_server_text, (char*)client_key);
strcat((char*)expected_server_text, expected_server_magic);
size_t key_len = os_strlen((char*)expected_server_text);
int ret = mbedtls_sha1_ret(expected_server_text, key_len, expected_server_sha1);
if (ret != 0) {
BK_LOGE(TAG, "Error in calculating sha1 sum , Returned 0x%02X\r\n", ret);
return ret;
}
mbedtls_base64_encode(expected_server_key, sizeof(expected_server_key), &outlen, expected_server_sha1, sizeof(expected_server_sha1));
expected_server_key[ (outlen < sizeof(expected_server_key)) ? outlen : (sizeof(expected_server_key) - 1) ] = 0;
BK_LOGE(TAG, "server key=%s\r\n\r\n", (char *)server_key);
BK_LOGE(TAG, "send_key=%s\r\n\r\n", (char*)client_key);
BK_LOGE(TAG, "expected_server_key=%s\r\n\r\n", expected_server_key);
if (os_strcmp((char*)expected_server_key, (char*)server_key) != 0) {
BK_LOGE(TAG, "Invalid websocket key\r\n");
return BK_FAIL;
}
return BK_OK;
}
int ws_poll_connection_closed(int *sockfd, int timeout_ms)
{
struct timeval timeout;
fd_set readset;
fd_set errset;
FD_ZERO(&readset);
FD_ZERO(&errset);
FD_SET(*sockfd, &readset);
FD_SET(*sockfd, &errset);
int ret = select(*sockfd + 1, &readset, NULL, &errset, utils_ms_to_timeval(timeout_ms, &timeout));
if (ret > 0) {
if (FD_ISSET(*sockfd, &readset)) {
uint8_t buffer;
if (recv(*sockfd, &buffer, 1, MSG_PEEK) <= 0) {
// socket is readable, but reads zero bytes -- connection cleanly closed by FIN flag
return BK_OK;
}
BK_LOGW(TAG, "ws_poll_connection_closed: unexpected data readable on socket=%d", *sockfd);
} else if (FD_ISSET(*sockfd, &errset)) {
int sock_errno = 0;
uint32_t optlen = sizeof(sock_errno);
getsockopt(*sockfd, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen);
BK_LOGD(TAG, "ws_poll_connection_closed select error %d, errno = %s, fd = %d", sock_errno, strerror(sock_errno), *sockfd);
if (sock_errno == ENOTCONN || sock_errno == ECONNRESET || sock_errno == ECONNABORTED) {
return BK_OK;
}
BK_LOGE(TAG, "ws_poll_connection_closed: unexpected errno=%d on socket=%d", sock_errno, *sockfd);
}
return BK_FAIL;
}
return ret;
}
static int ws_client_recv(transport client)
{
int rlen;
client->payload_offset = 0;
transport_ws_t *ws = client->ws_transport;
do {
BK_LOGE(TAG, "----------begin receive--------------\r\n");
rlen = ws_read(client, client->rx_buffer, client->buffer_size, WEBSOCKET_NETWORK_TIMEOUT_MS);
if (rlen < 0) {
BK_LOGE(TAG, "Error read data\r\n");
return BK_FAIL;
}
client->payload_len = ws->frame_state.payload_len;
client->last_opcode = (ws_transport_opcodes_t)ws->frame_state.opcode;
if (rlen == 0 && client->last_opcode == WS_TRANSPORT_OPCODES_NONE ) {
BK_LOGE(TAG, "ws read timeouts\r\n");
return BK_OK;
}
bk_websocket_push_cb(WEBSOCKET_EVENT_DATA, client->rx_buffer, rlen);
client->payload_offset += rlen;
} while (client->payload_offset < client->payload_len);
//BK_LOGE(TAG, "%s, len:%d\r\n", __func__, client->payload_len);
if (client->last_opcode == WS_TRANSPORT_OPCODES_PING) {
const char *data = (client->payload_len == 0) ? NULL : client->rx_buffer;
BK_LOGE(TAG, "Received ping, Sending PONG with payload len=%d\r\n", client->payload_len);
BK_LOGE(TAG, "----------sending pong packet----------\r\n");
ws_write(client, WS_TRANSPORT_OPCODES_PONG | WS_TRANSPORT_OPCODES_FIN, WS_MASK, data, client->payload_len,
WEBSOCKET_NETWORK_TIMEOUT_MS);
} else if (client->last_opcode == WS_TRANSPORT_OPCODES_PONG) {
BK_LOGE(TAG, "Received pong frame, send ping success\r\n");
client->wait_for_pong_resp = false;
} else if (client->last_opcode == WS_TRANSPORT_OPCODES_CLOSE) {
BK_LOGE(TAG, "Received close frame\r\n");
client->state = WEBSOCKET_STATE_CLOSING;
} else if (client->last_opcode == WS_TRANSPORT_OPCODES_TEXT) {
BK_LOGE(TAG, "Received text frame:\r\n");
bk_hex_dump(client->rx_buffer, client->payload_len);
}
return BK_OK;
}
static bk_err_t websocket_client_destory_config(transport client)
{
BK_LOGE(TAG, "%s\r\n", __func__);
if (client == NULL) {
BK_LOGE(TAG, "%s, client already null, return\r\n", __func__);
return BK_FAIL;
}
if (client->config == NULL) {
BK_LOGE(TAG, "%s, config already null, return\r\n", __func__);
return BK_FAIL;
}
websocket_config_t *cfg = client->config;
if(cfg->host)
os_free(cfg->host);
if(cfg->path)
os_free(cfg->path);
if(cfg->scheme)
os_free(cfg->scheme);
if(cfg->username)
os_free(cfg->username);
if(cfg->password)
os_free(cfg->password);
memset(cfg, 0, sizeof(websocket_config_t));
if(client->config) {
os_free(client->config);
}
client->config = NULL;
return BK_OK;
}
static int websocket_client_send_with_opcode(transport client, ws_transport_opcodes_t opcode, const uint8_t *data, int len, int timeout)
{
int need_write = len;
int wlen = 0, widx = 0;
int ret = BK_FAIL;
BK_LOGE(TAG, "%s: begin send opcode: %d\r\n", __func__, opcode);
if (client == NULL || len < 0 ||
(opcode != WS_TRANSPORT_OPCODES_CLOSE && (data == NULL || len <= 0))) {
BK_LOGE(TAG, "Invalid arguments");
return BK_FAIL;
}
uint32_t current_opcode = opcode;
while (widx < len || current_opcode) {
if (need_write > client->buffer_size) {
need_write = client->buffer_size;
} else {
current_opcode |= WS_TRANSPORT_OPCODES_FIN;
}
memcpy(client->tx_buffer, data + widx, need_write);
if(need_write)
bk_hex_dump(client->tx_buffer, need_write);
wlen = ws_write(client, current_opcode, WS_MASK, (char *)client->tx_buffer, need_write, timeout);
//(timeout==portMAX_DELAY)? -1 : timeout * portTICK_PERIOD_MS);
if (wlen < 0 || (wlen == 0 && need_write != 0)) {
ret = wlen;
BK_LOGE(TAG, "Network error: ws_write() returned %d, errno=%d\r\n", ret, errno);
ws_disconnect(client);
return ret;
}
current_opcode = 0;
widx += wlen;
need_write = len - widx;
}
ret = widx;
return ret;
}
int websocket_client_send_text(transport client, const char *data, int len, int timeout)
{
return websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_TEXT, (const uint8_t *)data, len, timeout);
}
int websocket_client_send_close(transport client, const char *data, int len, int timeout)
{
if(websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_CLOSE, (const uint8_t *)data, len, timeout) < 0) {
BK_LOGE(TAG, "Error send close frame\r\n");
return BK_FAIL;
}
status_bits = 0;
status_bits |= CLOSE_SENT_BIT;
return BK_OK;
}
bk_err_t websocket_client_set_uri(transport client, const char *uri)
{
if (client == NULL || uri == NULL) {
BK_LOGE(TAG, "client has not initialized or uri has not be input\r\n");
return BK_FAIL;
}
struct http_parser_url puri;
http_parser_url_init(&puri);
int parser_status = http_parser_parse_url(uri, os_strlen(uri), 0, &puri);
if (parser_status != 0) {
BK_LOGE(TAG, "Error parse uri = %s", uri);
return BK_FAIL;
}
if (NULL == (client->config = (websocket_config_t *)os_malloc(sizeof(websocket_config_t)))) {
BK_LOGE(TAG, "alloc config fail\r\n");
return BK_FAIL;
}
memset(client->config, 0, sizeof(websocket_config_t));
if (puri.field_data[UF_SCHEMA].len) {
if (NULL == (client->config->scheme = (char *)os_malloc(puri.field_data[UF_SCHEMA].len))) {
BK_LOGE(TAG, "alloc scheme fail\r\n");
return BK_FAIL;
}
os_strncpy(client->config->scheme, uri + puri.field_data[UF_SCHEMA].off, puri.field_data[UF_SCHEMA].len);
}
if (puri.field_data[UF_HOST].len) {
asprintf(&client->config->host, "%.*s", puri.field_data[UF_HOST].len, uri + puri.field_data[UF_HOST].off);
}
if (puri.field_data[UF_PATH].len || puri.field_data[UF_QUERY].len) {
if (puri.field_data[UF_QUERY].len == 0) {
asprintf(&client->config->path, "%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off);
} else if (puri.field_data[UF_PATH].len == 0) {
asprintf(&client->config->path, "/?%.*s", puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off);
} else {
asprintf(&client->config->path, "%.*s?%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off,
puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off);
}
}
if (puri.field_data[UF_PORT].off) {
client->config->port = strtol((const char*)(uri + puri.field_data[UF_PORT].off), NULL, 10);
} else {
client->config->port = WEBSOCKET_TCP_DEFAULT_PORT;
}
if (puri.field_data[UF_USERINFO].len) {
char *user_info = NULL;
asprintf(&user_info, "%.*s", puri.field_data[UF_USERINFO].len, uri + puri.field_data[UF_USERINFO].off);
if (user_info) {
char *pass = strchr(user_info, ':');
if (pass) {
pass[0] = 0;
pass ++;
free(client->config->password);
client->config->password = strdup(pass);
}
free(client->config->username);
client->config->username = strdup(user_info);
free(user_info);
} else {
return BK_FAIL;
}
}
return BK_OK;
}
transport websocket_client_init(const websocket_client_input_t *input)
{
transport client = (transport)os_malloc(sizeof(websocket_transport_info_t));
memset(client, 0, sizeof(websocket_transport_info_t));
//parse websocket uri to websocket config
if (input->uri) {
if (websocket_client_set_uri(client, input->uri) != BK_OK) {
BK_LOGE(TAG, "set uri fail, client destory\r\n");
goto _websocket_init_fail;
}
}
//set autoreconnect
client->auto_reconnect = false;
//set ws_transport
client->ws_transport = (transport_ws_t *)os_malloc(sizeof(transport_ws_t));
memset(client->ws_transport, 0, sizeof(transport_ws_t));
if(!client->ws_transport) {
BK_LOGE(TAG, "alloc ws_transport fail\r\n");
goto _websocket_init_fail;
}
if(client->config->path) {
if(client->ws_transport->path)
{
free(client->ws_transport->path);
}
client->ws_transport->path = strdup(client->config->path);
}
else {
free(client->ws_transport->path);
client->ws_transport->path = strdup("/");
}
client->ws_transport->buffer = os_malloc(WS_BUFFER_SIZE);
if (!client->ws_transport->buffer) {
BK_LOGE(TAG, "alloc ws_transport buffer fail\r\n");
goto _websocket_init_fail;
}
if (input->subprotocol) {
free(client->ws_transport->sub_protocol);
client->ws_transport->sub_protocol = strdup(input->subprotocol);
}
if (input->user_agent) {
free(client->ws_transport->user_agent);
client->ws_transport->user_agent = strdup(input->user_agent);
}
if (input->headers) {
free(client->ws_transport->headers);
client->ws_transport->headers = strdup(input->headers);
}
client->ws_transport->frame_state.bytes_remaining = 0;
//tick...
client->reconnect_tick_ms = bk_tick_get_ms();
client->ping_tick_ms = bk_tick_get_ms();
client->wait_for_pong_resp = false;
//rx retry
if(input->rx_retry <= 0)
client->rx_retry = WEBSOCKET_RX_RETRY_COUNT;
else
client->rx_retry = input->rx_retry;
//buf malloc
int buffer_size = input->buffer_size;
if (buffer_size <= 0) {
buffer_size = WEBSOCKET_BUFFER_SIZE_BYTE;
}
client->buffer_size = buffer_size;
if (NULL == (client->rx_buffer = (char *)os_malloc(buffer_size))) {
BK_LOGE(TAG, "alloc rx_buffer fail\r\n");
goto _websocket_init_fail;
}
if (NULL == (client->tx_buffer = (char *)os_malloc(buffer_size))) {
BK_LOGE(TAG, "alloc tx_buffer fail\r\n");
goto _websocket_init_fail;
}
//sockfd
client->sockfd = -1;
return client;
_websocket_init_fail:
websocket_client_destroy(client);
return NULL;
}
bk_err_t websocket_client_destroy(transport client)
{
BK_LOGE(TAG, "%s\r\n", __func__);
if (client == NULL) {
return BK_FAIL;
}
if (client->run) {
if(client->state >= WEBSOCKET_STATE_CONNECTED) {
if(websocket_client_send_close(client, NULL, 0, WEBSOCKET_NETWORK_TIMEOUT_MS)) {
BK_LOGE(TAG, "%s, client send close frame fail\r\n", __func__);
return BK_FAIL;
}
}
if(websocket_client_stop(client)) {
BK_LOGE(TAG, "%s, client stop fail\r\n", __func__);
return BK_FAIL;
}
}
return BK_OK;
}
int test_case_text(transport client)
{
//send text packet
char *a = "hello,BEKEN";
return websocket_client_send_text(client, a, strlen(a), WEBSOCKET_NETWORK_TIMEOUT_MS);
}
static void free_client(transport client)
{
if(client==NULL)
return ;
if (client->tx_buffer)
{
os_free(client->tx_buffer);
client->tx_buffer=NULL;
}
if (client->rx_buffer)
{
os_free(client->rx_buffer);
client->rx_buffer=NULL;
}
if (client->ws_transport)
{
if (client->ws_transport->buffer)
{
os_free(client->ws_transport->buffer);
client->ws_transport->buffer=NULL;
}
if (client->ws_transport->path)
{
os_free(client->ws_transport->path);
}
if (client->ws_transport->sub_protocol)
{
os_free(client->ws_transport->sub_protocol);
}
if (client->ws_transport->headers)
{
os_free(client->ws_transport->headers);
}
if (client->ws_transport->user_agent)
{
os_free(client->ws_transport->user_agent);
}
os_free(client->ws_transport);
}
os_free(client);
client = NULL;
}
void websocket_client_task(beken_thread_arg_t *thread_param)
{
transport client = (transport) thread_param;
client->run = true;
client->state = WEBSOCKET_STATE_INIT;
int read_select = 0;
while (client->run) {
switch ((int)client->state) {
case WEBSOCKET_STATE_INIT:
BK_LOGE(TAG, "websocket connecting to %s://%s:%d\r\n", client->config->scheme, client->config->host, client->config->port);
if (ws_connect(client, client->config->host,
client->config->port,
WEBSOCKET_NETWORK_TIMEOUT_MS) < 0) {
BK_LOGE(TAG, "Error websocket connect\r\n");
ws_disconnect(client);
break;
}
BK_LOGE(TAG, "websocket connected to %s://%s:%d\r\n", client->config->scheme, client->config->host, client->config->port);
client->state = WEBSOCKET_STATE_CONNECTED;
client->wait_for_pong_resp = false;
bk_websocket_push_cb(WEBSOCKET_EVENT_CONNECTED, NULL, 0);
break;
case WEBSOCKET_STATE_CONNECTED:
BK_LOGD(TAG, "%s, status:%02x\r\n", __func__, status_bits);
if (bk_tick_get_ms() - client->ping_tick_ms > WEBSOCKET_PING_INTERVAL_SEC*1000) {
client->ping_tick_ms = bk_tick_get_ms();
if (status_bits & PING_SENT_BIT) {
BK_LOGE(TAG, "----------Sending ping packet----------\r\n");
ws_write(client, WS_TRANSPORT_OPCODES_PING | WS_TRANSPORT_OPCODES_FIN, WS_MASK, NULL, 0, WEBSOCKET_NETWORK_TIMEOUT_MS);
} else if(status_bits & TEXT_SENT_BIT) {
BK_LOGE(TAG, "----------Sending text packet----------\r\n");
test_case_text(client);
}
if (!client->wait_for_pong_resp) {
client->pingpong_tick_ms = bk_tick_get_ms();
client->wait_for_pong_resp = true;
}
}
if ( bk_tick_get_ms() - client->pingpong_tick_ms > WEBSOCKET_PINGPONG_TIMEOUT_SEC*1000) {
if (client->wait_for_pong_resp) {
BK_LOGE(TAG, "Error, no PONG received for more than %d seconds after PING\r\n", client->pingpong_tick_ms);
break;
}
}
if (read_select == 0) {
BK_LOGD(TAG, "Read poll timeout: skipping read()...\r\n");
break;
}
client->ping_tick_ms = bk_tick_get_ms();
if (ws_client_recv(client) == BK_FAIL) {
BK_LOGE(TAG, "Error receive data\r\n");
ws_disconnect(client);
break;
}
break;
case WEBSOCKET_STATE_WAIT_TIMEOUT:
if (!client->auto_reconnect) {
client->run = false;
break;
}
if (bk_tick_get_ms() - client->reconnect_tick_ms > WEBSOCKET_RECONNECT_TIMEOUT_MS) {
client->state = WEBSOCKET_STATE_INIT;
client->reconnect_tick_ms = bk_tick_get_ms();
BK_LOGE(TAG, "Reconnecting...\r\n");
}
break;
case WEBSOCKET_STATE_CLOSING:
BK_LOGE(TAG, "Closing initiated by the server, sending close frame\r\n");
ws_write(client, WS_TRANSPORT_OPCODES_CLOSE | WS_TRANSPORT_OPCODES_FIN, WS_MASK, NULL, 0, WEBSOCKET_NETWORK_TIMEOUT_MS);
break;
default:
BK_LOGE(TAG, "Client run iteration in a default state: %d\r\n", client->state);
break;
}
if (WEBSOCKET_STATE_CONNECTED == client->state) {
read_select = ws_tcp_poll_read(&(client->sockfd), 1000); //Poll every 1000ms
if (read_select < 0) {
BK_LOGE(TAG, "Network error: ws_tcp_poll_read() returned %d, errno=%d\r\n", read_select, errno);
ws_disconnect(client);
}
} else if (WEBSOCKET_STATE_WAIT_TIMEOUT == client->state) {
if(client->auto_reconnect)
rtos_delay_milliseconds(WEBSOCKET_RECONNECT_TIMEOUT_MS);
} else if (WEBSOCKET_STATE_CLOSING == client->state) {
BK_LOGE(TAG, " Waiting for TCP connection to be closed by the server\r\n");
int ret = ws_poll_connection_closed(&(client->sockfd), 1000);
if (ret == 0) {
// still waiting
break;
}
if (ret < 0) {
BK_LOGE(TAG, "Connection terminated while waiting for clean TCP close\r\n");
}
client->run = false;
client->state = WEBSOCKET_STATE_UNKNOW;
bk_websocket_push_cb(WEBSOCKET_EVENT_CLOSED, NULL, 0);
break;
}
}
BK_LOGE(TAG, "close connection...\r\n");
ws_tcp_close(client);
client->state = WEBSOCKET_STATE_UNKNOW;
if(websocket_client_destory_config(client)) {
BK_LOGE(TAG, "client config already free\r\n");
}
free_client(client);
client = NULL;
rtos_delete_thread(NULL);
}
int websocket_client_start(transport client)
{
int ret;
if (client == NULL) {
BK_LOGE(TAG, "The client has not be initialized\r\n");
return BK_FAIL;
}
if (client->state >= WEBSOCKET_STATE_INIT) {
BK_LOGE(TAG, "The client has started\r\n");
return BK_FAIL;
}
ret = rtos_create_thread(NULL,
WEBSOCKET_TASK_PRIORITY,
"websocketc",
(beken_thread_function_t)websocket_client_task,
WEBSOCKET_TASK_STACK,
(beken_thread_arg_t)client);
return ret;
}
bk_err_t websocket_client_stop(transport client)
{
if (client == NULL) {
BK_LOGW(TAG, "Client null");
return BK_FAIL;
}
if (!client->run) {
BK_LOGW(TAG, "Client was not started");
return BK_FAIL;
}
client->run = false;
client->state = WEBSOCKET_STATE_UNKNOW;
return BK_OK;
}
void *websocket_run = NULL;
/*********************************demo api*********************************/
bk_err_t websocket_start(websocket_client_input_t *websocket_cfg)
{
if(websocket_run == NULL) {
transport client = websocket_client_init(websocket_cfg);
websocket_run = (void *)client;
BK_LOGE(TAG, "----------connect server-------\r\n");
if(ws_connect(client, client->config->host, client->config->port, WEBSOCKET_NETWORK_TIMEOUT_MS) < 0){
BK_LOGE(TAG, "Error websocket connect\r\n");
bk_websocket_push_cb(WEBSOCKET_EVENT_DISCONNECTED, NULL, 0);
ws_tcp_close(client);
return BK_FAIL;
}
bk_websocket_push_cb(WEBSOCKET_EVENT_CONNECTED, NULL, 0);
return BK_OK;
} else {
BK_LOGE(TAG, "%s websocket client already start, stop first\r\n", __func__);
return BK_FAIL;
}
}
bk_err_t websocket_recv(transport client)
{
/* listening rx data*/
int read_select=0;
int retry = 0;
while(!read_select) {
read_select = ws_tcp_poll_read(&(client->sockfd), 1000);
if (read_select < 0) {
BK_LOGE(TAG, "Network error: ws_tcp_poll_read() returned %d, errno=%d\r\n", read_select, errno);
ws_tcp_close(client);
return BK_FAIL;
}
if(read_select == 0) {
BK_LOGE(TAG, "continue poll\r\n");
retry++;
}
if(retry >= client->rx_retry) {
BK_LOGE(TAG, "recv timeout\r\n");
ws_tcp_close(client);
return BK_FAIL;
}
}
/* receive rx data */
if (ws_client_recv(client) == BK_FAIL) {
BK_LOGE(TAG, "Error receive data\r\n");
ws_tcp_close(client);
return BK_FAIL;
}
return BK_OK;
}
bk_err_t websocket_send_text(websocket_client_input_t *websocket_cfg)
{
if(websocket_run) {
transport client = (transport)websocket_run;
BK_LOGE(TAG, "----------sending text----------\r\n");
if(websocket_client_send_text(client, websocket_cfg->user_context, strlen(websocket_cfg->user_context), WEBSOCKET_NETWORK_TIMEOUT_MS) < 0) {
BK_LOGE(TAG, "%s send text fail\r\n", __func__);
return BK_FAIL;
}
return BK_OK;
}
else {
BK_LOGE(TAG, "%s, client already stop\r\n", __func__);
return BK_FAIL;
}
}
bk_err_t websocket_send_ping(void)
{
if(websocket_run) {
transport client = (transport)websocket_run;
BK_LOGE(TAG, "----------sending ping----------\r\n");
if(ws_write(client, WS_TRANSPORT_OPCODES_PING | WS_TRANSPORT_OPCODES_FIN, WS_MASK, NULL, 0, WEBSOCKET_NETWORK_TIMEOUT_MS) < 0) {
BK_LOGE(TAG, "%s send ping fail\r\n", __func__);
return BK_FAIL;
}
return BK_OK;
}
else {
BK_LOGE(TAG, "%s, client already stop\r\n", __func__);
return BK_FAIL;
}
}
bk_err_t websocket_stop(void)
{
if(websocket_run) {
BK_LOGE(TAG, "%s stop websocket client\r\n", __func__);
websocket_client_destroy((transport)websocket_run);
websocket_run = NULL;
bk_websocket_push_cb(WEBSOCKET_EVENT_CLOSED, NULL, 0);
return BK_OK;
} else {
BK_LOGE(TAG, "%s, already stop return\r\n", __func__);
return BK_FAIL;
}
}
bk_err_t websocket_send_ping_pong(websocket_client_input_t *websocket_cfg)
{
if(websocket_run == NULL) {
transport client = websocket_client_init(websocket_cfg);
websocket_run = (void *)client;
status_bits = 0;
status_bits |= PING_SENT_BIT;
BK_LOGE(TAG, "START ping pong TASK\r\n", __func__);
if(websocket_client_start(client)) {
return BK_FAIL;
}
return BK_OK;
}
else {
BK_LOGE(TAG, "%s, client already start\r\n", __func__);
return BK_FAIL;
}
}
#endif