This commit is contained in:
Daniel Brunner 2024-10-01 12:14:35 +02:00 committed by GitHub
commit da08931e03
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 139 additions and 7 deletions

View File

@ -127,7 +127,7 @@ static ssize_t tcp_read(esp_tls_t *tls, char *data, size_t datalen)
static ssize_t tcp_write(esp_tls_t *tls, const char *data, size_t datalen) static ssize_t tcp_write(esp_tls_t *tls, const char *data, size_t datalen)
{ {
return send(tls->sockfd, data, datalen, 0); return send(tls->sockfd, data, datalen, MSG_MORE);
} }
ssize_t esp_tls_conn_read(esp_tls_t *tls, void *data, size_t datalen) ssize_t esp_tls_conn_read(esp_tls_t *tls, void *data, size_t datalen)

View File

@ -56,7 +56,7 @@ int httpd_send(httpd_req_t *r, const char *buf, size_t buf_len)
} }
struct httpd_req_aux *ra = r->aux; struct httpd_req_aux *ra = r->aux;
int ret = ra->sd->send_fn(ra->sd->handle, ra->sd->fd, buf, buf_len, 0); int ret = ra->sd->send_fn(ra->sd->handle, ra->sd->fd, buf, buf_len, MSG_MORE);
if (ret < 0) { if (ret < 0) {
ESP_LOGD(TAG, LOG_FMT("error in send_fn")); ESP_LOGD(TAG, LOG_FMT("error in send_fn"));
return ret; return ret;
@ -70,7 +70,7 @@ static esp_err_t httpd_send_all(httpd_req_t *r, const char *buf, size_t buf_len)
int ret; int ret;
while (buf_len > 0) { while (buf_len > 0) {
ret = ra->sd->send_fn(ra->sd->handle, ra->sd->fd, buf, buf_len, 0); ret = ra->sd->send_fn(ra->sd->handle, ra->sd->fd, buf, buf_len, MSG_MORE);
if (ret < 0) { if (ret < 0) {
ESP_LOGD(TAG, LOG_FMT("error in send_fn")); ESP_LOGD(TAG, LOG_FMT("error in send_fn"));
return ESP_FAIL; return ESP_FAIL;

View File

@ -416,14 +416,14 @@ esp_err_t httpd_ws_send_frame_async(httpd_handle_t hd, int fd, httpd_ws_frame_t
} }
/* Send off header */ /* Send off header */
if (sess->send_fn(hd, fd, (const char *)header_buf, tx_len, 0) < 0) { if (sess->send_fn(hd, fd, (const char *)header_buf, tx_len, MSG_MORE) < 0) {
ESP_LOGW(TAG, LOG_FMT("Failed to send WS header")); ESP_LOGW(TAG, LOG_FMT("Failed to send WS header"));
return ESP_FAIL; return ESP_FAIL;
} }
/* Send off payload */ /* Send off payload */
if(frame->len > 0 && frame->payload != NULL) { if(frame->len > 0 && frame->payload != NULL) {
if (sess->send_fn(hd, fd, (const char *)frame->payload, frame->len, 0) < 0) { if (sess->send_fn(hd, fd, (const char *)frame->payload, frame->len, MSG_MORE) < 0) {
ESP_LOGW(TAG, LOG_FMT("Failed to send WS payload")); ESP_LOGW(TAG, LOG_FMT("Failed to send WS payload"));
return ESP_FAIL; return ESP_FAIL;
} }

View File

@ -14,6 +14,11 @@
extern "C" { extern "C" {
#endif #endif
// this define will also be used for optimized websocket sends with the
// optimized version to prepend the required space for the websocket
// header
#define MAX_WEBSOCKET_HEADER_SIZE 16
typedef enum ws_transport_opcodes { typedef enum ws_transport_opcodes {
WS_TRANSPORT_OPCODES_CONT = 0x00, WS_TRANSPORT_OPCODES_CONT = 0x00,
WS_TRANSPORT_OPCODES_TEXT = 0x01, WS_TRANSPORT_OPCODES_TEXT = 0x01,
@ -139,6 +144,37 @@ esp_err_t esp_transport_ws_set_config(esp_transport_handle_t t, const esp_transp
*/ */
int esp_transport_ws_send_raw(esp_transport_handle_t t, ws_transport_opcodes_t opcode, const char *b, int len, int timeout_ms); int esp_transport_ws_send_raw(esp_transport_handle_t t, ws_transport_opcodes_t opcode, const char *b, int len, int timeout_ms);
/**
* @brief Sends websocket raw message with custom opcode and payload in a optimized way
*
* This method is similar to esp_transport_ws_send_raw(), but
* it assumes, that the first MAX_WEBSOCKET_HEADER_SIZE bytes
* of the buffer should not be sent and should rather be used
* for the required websocket header itself. This is done to
* have a single TCP packet for header and payload and to avoid
* copying and allocating additional resources. The first
* MAX_WEBSOCKET_HEADER_SIZE bytes should not be initialized in
* any specific way, and the return value (length) will also
* include the MAX_WEBSOCKET_HEADER_SIZE byte extra buffer.
*
* Note that generic esp_transport_write for ws handle sends
* binary massages by default if size is > 0 and
* ping message if message size is set to 0.
* This API is provided to support explicit messages with arbitrary opcode,
* should it be PING, PONG or TEXT message with arbitrary data.
*
* @param[in] t Websocket transport handle
* @param[in] opcode ws operation code
* @param[in] buffer The buffer
* @param[in] len The length
* @param[in] timeout_ms The timeout milliseconds (-1 indicates block forever)
*
* @return
* - Number of bytes was written
* - (-1) if there are any errors, should check errno
*/
int esp_transport_ws_send_raw_optimized(esp_transport_handle_t t, ws_transport_opcodes_t opcode, const char *b, int len, int timeout_ms);
/** /**
* @brief Returns websocket fin flag for last received data * @brief Returns websocket fin flag for last received data
* *

View File

@ -246,7 +246,7 @@ static int tcp_write(esp_transport_handle_t t, const char *buffer, int len, int
ESP_LOGW(TAG, "Poll timeout or error, errno=%s, fd=%d, timeout_ms=%d", strerror(errno), ssl->sockfd, timeout_ms); ESP_LOGW(TAG, "Poll timeout or error, errno=%s, fd=%d, timeout_ms=%d", strerror(errno), ssl->sockfd, timeout_ms);
return poll; return poll;
} }
int ret = send(ssl->sockfd, (const unsigned char *) buffer, len, 0); int ret = send(ssl->sockfd, (const unsigned char *) buffer, len, MSG_MORE);
if (ret < 0) { if (ret < 0) {
ESP_LOGE(TAG, "tcp_write error, errno=%s", strerror(errno)); ESP_LOGE(TAG, "tcp_write error, errno=%s", strerror(errno));
esp_transport_capture_errno(t, errno); esp_transport_capture_errno(t, errno);

View File

@ -36,7 +36,6 @@ static const char *TAG = "transport_ws";
#define WS_MASK 0x80 #define WS_MASK 0x80
#define WS_SIZE16 126 #define WS_SIZE16 126
#define WS_SIZE64 127 #define WS_SIZE64 127
#define MAX_WEBSOCKET_HEADER_SIZE 16
#define WS_RESPONSE_OK 101 #define WS_RESPONSE_OK 101
#define WS_TRANSPORT_MAX_CONTROL_FRAME_BUFFER_LEN 125 #define WS_TRANSPORT_MAX_CONTROL_FRAME_BUFFER_LEN 125
@ -415,6 +414,92 @@ static int _ws_write(esp_transport_handle_t t, int opcode, int mask_flag, const
return ret; return ret;
} }
// This method is similar to _ws_write() but it assumes, that the first 16 bytes of the buffer should not be sent and
// should rather be used for the required websocket header itself. This is done to have a single TCP packet for header
// and payload and to avoid copying and allocating additional resources. The first 16 bytes should not be initialized
// in any specific way, and the return value (length) will also include the 16 byte extra buffer.
static int _ws_write_optimized(esp_transport_handle_t t, int opcode, int mask_flag, const char *b, int len, int timeout_ms)
{
assert(len >= MAX_WEBSOCKET_HEADER_SIZE);
transport_ws_t *ws = esp_transport_get_context_data(t);
char *buffer = (char *)b;
char *ws_header;
// char *mask;
int header_len = 0; //, i;
int poll_write;
if ((poll_write = esp_transport_poll_write(ws->parent, timeout_ms)) <= 0) {
ESP_LOGE(TAG, "Error transport_poll_write");
return poll_write;
}
int len2 = len - MAX_WEBSOCKET_HEADER_SIZE;
if (len2 <= 125) {
ws_header = buffer+MAX_WEBSOCKET_HEADER_SIZE-2-4;
ws_header[header_len++] = opcode;
ws_header[header_len++] = (uint8_t)(len2 | mask_flag);
} else if (len2 < 65536) {
ws_header = buffer+MAX_WEBSOCKET_HEADER_SIZE-4-4;
ws_header[header_len++] = opcode;
ws_header[header_len++] = WS_SIZE16 | mask_flag;
ws_header[header_len++] = (uint8_t)(len2 >> 8);
ws_header[header_len++] = (uint8_t)(len2 & 0xFF);
} else {
ws_header = buffer+MAX_WEBSOCKET_HEADER_SIZE-10-4;
ws_header[header_len++] = opcode;
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)((len2 >> 24) & 0xFF);
ws_header[header_len++] = (uint8_t)((len2 >> 16) & 0xFF);
ws_header[header_len++] = (uint8_t)((len2 >> 8) & 0xFF);
ws_header[header_len++] = (uint8_t)((len2 >> 0) & 0xFF);
}
if (mask_flag) {
ws_header[header_len++] = 0;
ws_header[header_len++] = 0;
ws_header[header_len++] = 0;
ws_header[header_len++] = 0;
// mask = &ws_header[header_len];
// mask = 0;
// ssize_t rc;
// if ((rc = getrandom(ws_header + header_len, 4, 0)) < 0) {
// ESP_LOGD(TAG, "getrandom() returned %zd", rc);
// return -1;
// }
// header_len += 4;
// for (i = MAX_WEBSOCKET_HEADER_SIZE; i < len; ++i) {
// buffer[i] = (buffer[i] ^ mask[i % 4]);
// }
}
// if (esp_transport_write(ws->parent, ws_header, len - MAX_WEBSOCKET_HEADER_SIZE + header_len, timeout_ms) != header_len) {
// ESP_LOGE(TAG, "Error write header");
// return -1;
// }
// if (len == 0) {
// return 0;
// }
int ret = esp_transport_write(ws->parent, ws_header, len - MAX_WEBSOCKET_HEADER_SIZE + header_len, timeout_ms);
ESP_LOGI(TAG, "len=%d header_len=%d total_size=%d sent=%d", len, header_len, len - MAX_WEBSOCKET_HEADER_SIZE + header_len, ret);
// in case of masked transport we have to revert back to the original data, as ws layer
// does not create its own copy of data to be sent
if (mask_flag) {
// mask = &ws_header[header_len - 4];
// for (i = 0; i < len; ++i) {
// buffer[i] = (buffer[i] ^ mask[i % 4]);
// }
}
return ret + (ws_header - buffer);
}
int esp_transport_ws_send_raw(esp_transport_handle_t t, ws_transport_opcodes_t opcode, const char *b, int len, int timeout_ms) int esp_transport_ws_send_raw(esp_transport_handle_t t, ws_transport_opcodes_t opcode, const char *b, int len, int timeout_ms)
{ {
uint8_t op_code = ws_get_bin_opcode(opcode); uint8_t op_code = ws_get_bin_opcode(opcode);
@ -426,6 +511,17 @@ int esp_transport_ws_send_raw(esp_transport_handle_t t, ws_transport_opcodes_t o
return _ws_write(t, op_code, WS_MASK, b, len, timeout_ms); return _ws_write(t, op_code, WS_MASK, b, len, timeout_ms);
} }
int esp_transport_ws_send_raw_optimized(esp_transport_handle_t t, ws_transport_opcodes_t opcode, const char *b, int len, int timeout_ms)
{
uint8_t op_code = ws_get_bin_opcode(opcode);
if (t == NULL) {
ESP_LOGE(TAG, "Transport must be a valid ws handle");
return ESP_ERR_INVALID_ARG;
}
ESP_LOGD(TAG, "Sending raw ws message with opcode %d", op_code);
return _ws_write_optimized(t, op_code, WS_MASK, b, len, timeout_ms);
}
static int ws_write(esp_transport_handle_t t, const char *b, int len, int timeout_ms) static int ws_write(esp_transport_handle_t t, const char *b, int len, int timeout_ms)
{ {
if (len == 0) { if (len == 0) {