feat(reynabot): Improve UI and update voice endpoint to kids gateway
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
#include "websocket.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <lwip/sockets.h>
|
||||
#include <lwip/inet.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
static int recv_all(int fd, void* buf, size_t len) {
|
||||
size_t total = 0;
|
||||
char* p = (char*)buf;
|
||||
while (total < len) {
|
||||
int r = lwip_recv(fd, p + total, len - total, 0);
|
||||
if (r <= 0) {
|
||||
return -1;
|
||||
}
|
||||
total += r;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint16_t my_htons(uint16_t val) {
|
||||
return (uint16_t)(((val & 0xff) << 8) | ((val & 0xff00) >> 8));
|
||||
}
|
||||
|
||||
int ws_connect(const char* host, int port, const char* path, const char* device_id, const char* auth_key) {
|
||||
int fd = lwip_socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd < 0) return -1;
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = my_htons(port);
|
||||
addr.sin_addr.s_addr = ipaddr_addr(host);
|
||||
|
||||
if (lwip_connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set socket receive timeout (e.g. 90 seconds) to prevent blocking indefinitely
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 90;
|
||||
tv.tv_usec = 0;
|
||||
lwip_setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||
|
||||
// Send HTTP upgrade handshake request
|
||||
char req[1024];
|
||||
snprintf(req, sizeof(req),
|
||||
"GET %s HTTP/1.1\r\n"
|
||||
"Host: %s:%d\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"Authorization: Bearer %s\r\n"
|
||||
"X-Device-ID: %s\r\n"
|
||||
"\r\n",
|
||||
path, host, port, auth_key, device_id);
|
||||
|
||||
if (lwip_send(fd, req, strlen(req), 0) < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Read HTTP response headers until we hit "\r\n\r\n"
|
||||
char header_buf[1024];
|
||||
size_t header_len = 0;
|
||||
while (header_len < sizeof(header_buf) - 1) {
|
||||
char c;
|
||||
int r = lwip_recv(fd, &c, 1, 0);
|
||||
if (r <= 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
header_buf[header_len++] = c;
|
||||
header_buf[header_len] = '\0';
|
||||
|
||||
if (header_len >= 4 && strcmp(header_buf + header_len - 4, "\r\n\r\n") == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify HTTP 101 Switching Protocols response status
|
||||
if (strstr(header_buf, "HTTP/1.1 101") == NULL && strstr(header_buf, "HTTP/1.0 101") == NULL) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int ws_send(int fd, const uint8_t* data, size_t len, bool binary) {
|
||||
uint8_t header[10];
|
||||
size_t header_len = 0;
|
||||
|
||||
header[0] = binary ? 0x82 : 0x81;
|
||||
|
||||
if (len < 126) {
|
||||
header[1] = 0x80 | (uint8_t)len;
|
||||
header_len = 2;
|
||||
} else {
|
||||
header[1] = 0x80 | 126;
|
||||
header[2] = (uint8_t)((len >> 8) & 0xFF);
|
||||
header[3] = (uint8_t)(len & 0xFF);
|
||||
header_len = 4;
|
||||
}
|
||||
|
||||
// Use fixed client mask for performance: 0x12, 0x34, 0x56, 0x78
|
||||
uint8_t mask[4] = { 0x12, 0x34, 0x56, 0x78 };
|
||||
memcpy(header + header_len, mask, 4);
|
||||
header_len += 4;
|
||||
|
||||
// Send WebSocket frame header
|
||||
int sent = lwip_send(fd, header, header_len, 0);
|
||||
if (sent < 0) return -1;
|
||||
|
||||
// Mask the payload
|
||||
uint8_t* masked = malloc(len);
|
||||
if (masked == NULL) return -1;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
masked[i] = data[i] ^ mask[i % 4];
|
||||
}
|
||||
|
||||
// Send masked payload
|
||||
sent = lwip_send(fd, masked, len, 0);
|
||||
free(masked);
|
||||
|
||||
return sent >= 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
int ws_recv(int fd, int* out_opcode, uint8_t* payload, size_t max_len) {
|
||||
uint8_t header[2];
|
||||
if (recv_all(fd, header, 2) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int opcode = header[0] & 0x0F;
|
||||
if (out_opcode != NULL) {
|
||||
*out_opcode = opcode;
|
||||
}
|
||||
|
||||
int masked = (header[1] & 0x80) != 0;
|
||||
size_t len = header[1] & 0x7F;
|
||||
|
||||
if (len == 126) {
|
||||
uint8_t ext_len[2];
|
||||
if (recv_all(fd, ext_len, 2) < 0) return -1;
|
||||
len = ((size_t)ext_len[0] << 8) | ext_len[1];
|
||||
} else if (len == 127) {
|
||||
uint8_t ext_len[8];
|
||||
if (recv_all(fd, ext_len, 8) < 0) return -1;
|
||||
// Parse 64-bit length into size_t
|
||||
len = ((size_t)ext_len[4] << 24) | ((size_t)ext_len[5] << 16) | ((size_t)ext_len[6] << 8) | ext_len[7];
|
||||
}
|
||||
|
||||
if (masked) {
|
||||
uint8_t mask[4];
|
||||
if (recv_all(fd, mask, 4) < 0) return -1;
|
||||
|
||||
if (len > max_len) {
|
||||
ESP_LOGE("websocket", "ws_recv overflow (masked): len=%u, max_len=%u", (unsigned)len, (unsigned)max_len);
|
||||
// Buffer overflow, skip payload to align stream
|
||||
size_t to_discard = len;
|
||||
uint8_t discard_buf[256];
|
||||
while (to_discard > 0) {
|
||||
size_t chunk = to_discard < sizeof(discard_buf) ? to_discard : sizeof(discard_buf);
|
||||
if (recv_all(fd, discard_buf, chunk) < 0) return -1;
|
||||
to_discard -= chunk;
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (recv_all(fd, payload, len) < 0) return -1;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
payload[i] ^= mask[i % 4];
|
||||
}
|
||||
} else {
|
||||
if (len > max_len) {
|
||||
ESP_LOGE("websocket", "ws_recv overflow (unmasked): len=%u, max_len=%u", (unsigned)len, (unsigned)max_len);
|
||||
size_t to_discard = len;
|
||||
uint8_t discard_buf[256];
|
||||
while (to_discard > 0) {
|
||||
size_t chunk = to_discard < sizeof(discard_buf) ? to_discard : sizeof(discard_buf);
|
||||
if (recv_all(fd, discard_buf, chunk) < 0) return -1;
|
||||
to_discard -= chunk;
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (recv_all(fd, payload, len) < 0) return -1;
|
||||
}
|
||||
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
void ws_close(int fd) {
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
int ws_send_pong(int fd, const uint8_t* payload, size_t len) {
|
||||
uint8_t header[10];
|
||||
size_t header_len = 0;
|
||||
|
||||
header[0] = 0x8A; // FIN | PONG (0x0A)
|
||||
|
||||
if (len < 126) {
|
||||
header[1] = 0x80 | (uint8_t)len;
|
||||
header_len = 2;
|
||||
} else {
|
||||
header[1] = 0x80 | 126;
|
||||
header[2] = (uint8_t)((len >> 8) & 0xFF);
|
||||
header[3] = (uint8_t)(len & 0xFF);
|
||||
header_len = 4;
|
||||
}
|
||||
|
||||
uint8_t mask[4] = { 0x12, 0x34, 0x56, 0x78 };
|
||||
memcpy(header + header_len, mask, 4);
|
||||
header_len += 4;
|
||||
|
||||
int sent = lwip_send(fd, header, header_len, 0);
|
||||
if (sent < 0) return -1;
|
||||
|
||||
if (len > 0 && payload != NULL) {
|
||||
uint8_t* masked = malloc(len);
|
||||
if (masked == NULL) return -1;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
masked[i] = payload[i] ^ mask[i % 4];
|
||||
}
|
||||
sent = lwip_send(fd, masked, len, 0);
|
||||
free(masked);
|
||||
}
|
||||
|
||||
return sent >= 0 ? 0 : -1;
|
||||
}
|
||||
Reference in New Issue
Block a user