added support for large WebSocket frames
This commit is contained in:
parent
bce2139f06
commit
3b5397f2a4
3 changed files with 70 additions and 32 deletions
|
@ -2411,18 +2411,31 @@ websocket_register_callbacks(tWsOpenHandler ws_open_cb, tWsHandler ws_cb)
|
||||||
err_t
|
err_t
|
||||||
websocket_write(struct tcp_pcb *pcb, const uint8_t *data, uint16_t len, uint8_t mode)
|
websocket_write(struct tcp_pcb *pcb, const uint8_t *data, uint16_t len, uint8_t mode)
|
||||||
{
|
{
|
||||||
if (len > 125)
|
uint8_t *buf = mem_malloc(len + 4);
|
||||||
return ERR_BUF;
|
if (buf == NULL) {
|
||||||
|
LWIP_DEBUGF(HTTPD_DEBUG, ("[websocket_write] out of memory\n"));
|
||||||
|
return ERR_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char buf[len + 2];
|
int offset = 2;
|
||||||
buf[0] = 0x80 | mode;
|
buf[0] = 0x80 | mode;
|
||||||
buf[1] = len;
|
if (len > 125) {
|
||||||
memcpy(&buf[2], data, len);
|
offset = 4;
|
||||||
len += 2;
|
buf[1] = 126;
|
||||||
|
buf[2] = len >> 8;
|
||||||
|
buf[3] = len;
|
||||||
|
} else {
|
||||||
|
buf[1] = len;
|
||||||
|
}
|
||||||
|
|
||||||
LWIP_DEBUGF(HTTPD_DEBUG, ("[wsoc] sending packet\n"));
|
memcpy(&buf[offset], data, len);
|
||||||
|
len += offset;
|
||||||
|
|
||||||
return http_write(pcb, buf, &len, TCP_WRITE_FLAG_COPY);
|
LWIP_DEBUGF(HTTPD_DEBUG, ("[websocket_write] sending packet\n"));
|
||||||
|
err_t retval = http_write(pcb, buf, &len, TCP_WRITE_FLAG_COPY);
|
||||||
|
mem_free(buf);
|
||||||
|
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2431,7 +2444,7 @@ websocket_write(struct tcp_pcb *pcb, const uint8_t *data, uint16_t len, uint8_t
|
||||||
static err_t
|
static err_t
|
||||||
websocket_send_close(struct tcp_pcb *pcb)
|
websocket_send_close(struct tcp_pcb *pcb)
|
||||||
{
|
{
|
||||||
const char buf[] = {0x88, 0x02, 0x03, 0xe8};
|
const u8_t buf[] = {0x88, 0x02, 0x03, 0xe8};
|
||||||
u16_t len = sizeof (buf);
|
u16_t len = sizeof (buf);
|
||||||
LWIP_DEBUGF(HTTPD_DEBUG, ("[wsoc] closing connection\n"));
|
LWIP_DEBUGF(HTTPD_DEBUG, ("[wsoc] closing connection\n"));
|
||||||
return tcp_write(pcb, buf, len, TCP_WRITE_FLAG_COPY);
|
return tcp_write(pcb, buf, len, TCP_WRITE_FLAG_COPY);
|
||||||
|
@ -2447,39 +2460,59 @@ websocket_send_close(struct tcp_pcb *pcb)
|
||||||
static err_t
|
static err_t
|
||||||
websocket_parse(struct tcp_pcb *pcb, struct pbuf *p)
|
websocket_parse(struct tcp_pcb *pcb, struct pbuf *p)
|
||||||
{
|
{
|
||||||
unsigned char *data;
|
u8_t *data = (u8_t *) p->payload;
|
||||||
data = (unsigned char*) p->payload;
|
|
||||||
u16_t data_len = p->len;
|
u16_t data_len = p->len;
|
||||||
|
|
||||||
if (data != NULL && data_len > 1) {
|
if (data != NULL && data_len > 1) {
|
||||||
LWIP_DEBUGF(HTTPD_DEBUG, ("[wsoc] frame received\n"));
|
LWIP_DEBUGF(HTTPD_DEBUG, ("[wsoc] frame received\n"));
|
||||||
|
if ((data[0] & 0x80) == 0) {
|
||||||
|
LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: continuation frames not supported\n"));
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
||||||
u8_t opcode = data[0] & 0x0F;
|
u8_t opcode = data[0] & 0x0F;
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case WS_TEXT_MODE:
|
case WS_TEXT_MODE:
|
||||||
case WS_BIN_MODE:
|
case WS_BIN_MODE:
|
||||||
LWIP_DEBUGF(HTTPD_DEBUG, ("Opcode: 0x%hX, frame length: %d\n", opcode, data_len));
|
LWIP_DEBUGF(HTTPD_DEBUG, ("Opcode: 0x%hX, frame length: %d\n", opcode, data_len));
|
||||||
if (data_len > 6) {
|
if (data_len > 6 && websocket_cb != NULL) {
|
||||||
u8_t len = data[1] & 0x7F;
|
int data_offset = 6;
|
||||||
if (len > data_len || len > 125) {
|
u8_t *dptr = &data[6];
|
||||||
LWIP_DEBUGF(HTTPD_DEBUG, ("Error: large frames not supported\n"));
|
u8_t *kptr = &data[2];
|
||||||
return ERR_VAL;
|
u16_t len = data[1] & 0x7F;
|
||||||
} else {
|
|
||||||
if (data_len - 6 != len)
|
if (len == 127) {
|
||||||
LWIP_DEBUGF(HTTPD_DEBUG, ("Multiple frames received\n"));
|
/* most likely won't happen inside non-fragmented frame */
|
||||||
data_len = len;
|
LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: frame is too long\n"));
|
||||||
|
return ERR_OK;
|
||||||
|
} else if (len == 126) {
|
||||||
|
/* extended length */
|
||||||
|
dptr += 2;
|
||||||
|
kptr += 2;
|
||||||
|
data_offset += 2;
|
||||||
|
len = (data[2] << 8) | data[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data_len -= data_offset;
|
||||||
|
|
||||||
|
if (len > data_len) {
|
||||||
|
LWIP_DEBUGF(HTTPD_DEBUG, ("Error: incorrect frame size\n"));
|
||||||
|
return ERR_VAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_len != len)
|
||||||
|
LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: segmented frame received\n"));
|
||||||
|
|
||||||
/* unmask */
|
/* unmask */
|
||||||
for (int i = 0; i < data_len; i++)
|
for (int i = 0; i < len; i++)
|
||||||
data[i + 6] = (data[i + 6] ^ data[2 + i % 4]);
|
*(dptr++) ^= kptr[i % 4];
|
||||||
|
|
||||||
/* user callback */
|
/* user callback */
|
||||||
if (websocket_cb)
|
websocket_cb(pcb, &data[data_offset], len, opcode);
|
||||||
websocket_cb(pcb, &data[6], data_len, opcode);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x08: // close
|
case 0x08: // close
|
||||||
LWIP_DEBUGF(HTTPD_DEBUG, ("Close request\n"));
|
LWIP_DEBUGF(HTTPD_DEBUG, ("Close request\n"));
|
||||||
return ERR_CLSD;
|
return ERR_CLSD;
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
LWIP_DEBUGF(HTTPD_DEBUG, ("Unsupported opcode 0x%hX\n", opcode));
|
LWIP_DEBUGF(HTTPD_DEBUG, ("Unsupported opcode 0x%hX\n", opcode));
|
||||||
break;
|
break;
|
||||||
|
@ -2508,19 +2541,17 @@ http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
|
||||||
return ERR_BUF;
|
return ERR_BUF;
|
||||||
}
|
}
|
||||||
tcp_recved(pcb, p->tot_len);
|
tcp_recved(pcb, p->tot_len);
|
||||||
|
|
||||||
err_t err = websocket_parse(pcb, p);
|
err_t err = websocket_parse(pcb, p);
|
||||||
|
|
||||||
if (p != NULL) {
|
if (p != NULL) {
|
||||||
/* otherwise tcp buffer hogs */
|
/* otherwise tcp buffer hogs */
|
||||||
LWIP_DEBUGF(HTTPD_DEBUG, ("[wsoc] freeing buffer\n"));
|
LWIP_DEBUGF(HTTPD_DEBUG, ("[wsoc] freeing buffer\n"));
|
||||||
pbuf_free(p);
|
pbuf_free(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err == ERR_CLSD) {
|
if (err == ERR_CLSD) {
|
||||||
http_close_conn(pcb, hs);
|
http_close_conn(pcb, hs);
|
||||||
}
|
}
|
||||||
|
/* reset timeout */
|
||||||
|
hs->retries = 0;
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -244,7 +244,7 @@ typedef void (*tWsOpenHandler)(struct tcp_pcb *pcb, const char *uri);
|
||||||
*
|
*
|
||||||
* @param pcb tcp_pcb to send.
|
* @param pcb tcp_pcb to send.
|
||||||
* @param data data to send.
|
* @param data data to send.
|
||||||
* @param len data length (should not exceed 125).
|
* @param len data length.
|
||||||
* @param mode WS_TEXT_MODE or WS_BIN_MODE.
|
* @param mode WS_TEXT_MODE or WS_BIN_MODE.
|
||||||
* @return ERR_OK if write succeeded.
|
* @return ERR_OK if write succeeded.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
Maintained by lujji (https://github.com/lujji/esp-httpd).
|
This is a basic HTTP server with WebSockets based on httpd from LwIP.
|
||||||
|
|
||||||
Note: this module expects your project to provide "fsdata.c" created with "makefsdata" utility.
|
WebSockets implementation supports binary and text modes. Multiple sockets are supported. Continuation frames are not implemented.
|
||||||
|
By default, a WebSocket is closed after 20 seconds of inactivity to conserve memory. This behavior can be changed by overriding `WS_TIMEOUT` option.
|
||||||
|
|
||||||
|
To enable debugging extra flags `-DLWIP_DEBUG=1 -DHTTPD_DEBUG=LWIP_DBG_ON` should be passed at compile-time.
|
||||||
|
|
||||||
|
This module expects your project to provide "fsdata.c" created with "makefsdata" utility.
|
||||||
See examples/http_server.
|
See examples/http_server.
|
||||||
|
|
||||||
|
Maintained by lujji (https://github.com/lujji/esp-httpd).
|
||||||
|
|
Loading…
Reference in a new issue