1
0
mirror of https://github.com/lammertb/libhttp.git synced 2025-08-09 03:22:45 +03:00
Files
libhttp/src/httplib_read_websocket.c
2016-12-28 20:06:16 +01:00

288 lines
7.1 KiB
C

/*
* Copyright (c) 2016 Lammert Bies
* Copyright (c) 2013-2016 the Civetweb developers
* Copyright (c) 2004-2013 Sergey Lyubka
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* ============
* Release: 1.9
*/
#include "httplib_main.h"
/*
* void XX_httplib_read_websocket( struct httplib_connection *conn, httplib_websocket_data_handler ws_data_handler, void *calback_data );
*
* The function XX_httplib_read_websocket() reads from a websocket connection.
*/
void XX_httplib_read_websocket( struct httplib_connection *conn, httplib_websocket_data_handler ws_data_handler, void *callback_data ) {
/* Pointer to the beginning of the portion of the incoming websocket
* message queue.
* The original websocket upgrade request is never removed, so the queue
* begins after it. */
unsigned char *buf = (unsigned char *)conn->buf + conn->request_len;
int n;
int error;
int exit_by_callback;
/*
* body_len is the length of the entire queue in bytes
* len is the length of the current message
* data_len is the length of the current message's data payload
* header_len is the length of the current message's header
*/
size_t i;
size_t len;
size_t mask_len = 0;
size_t data_len = 0;
size_t header_len;
size_t body_len;
/*
* "The masking key is a 32-bit value chosen at random by the client."
* http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5
*/
unsigned char mask[4];
/*
* data points to the place where the message is stored when passed to
* the websocket_data callback. This is either mem on the stack, or a
* dynamically allocated buffer if it is too large.
*/
char mem[4096];
char *data;
unsigned char mop; /* mask flag and opcode */
double timeout;
if ( conn == NULL || conn->ctx == NULL ) return;
data = mem;
timeout = ((double)conn->ctx->websocket_timeout) / 1000.0;
if ( timeout <= 0.0 ) timeout = ((double)conn->ctx->request_timeout) / 1000.0;
XX_httplib_set_thread_name( "wsock" );
/*
* Loop continuously, reading messages from the socket, invoking the
* callback, and waiting repeatedly until an error occurs.
*/
while ( conn->ctx->status == CTX_STATUS_RUNNING ) {
header_len = 0;
if ( conn->data_len < conn->request_len ) {
httplib_cry( conn->ctx, conn, "websocket error: data len less than request len, closing connection" );
break;
}
body_len = (size_t)(conn->data_len - conn->request_len);
if ( body_len >= 2 ) {
len = buf[1] & 127;
mask_len = (buf[1] & 128) ? 4 : 0;
if ( len < 126 && body_len >= mask_len ) {
data_len = len;
header_len = 2 + mask_len;
}
else if ( len == 126 && body_len >= mask_len+4 ) {
header_len = mask_len+4;
data_len = (((size_t)buf[2]) << 8) + buf[3];
}
else if ( body_len >= 10+mask_len+10 ) {
header_len = mask_len+10;
data_len = (((uint64_t)ntohl(*(uint32_t *)(void *)&buf[2])) << 32) + ntohl(*(uint32_t *)(void *)&buf[6]);
}
}
if ( header_len > 0 && body_len >= header_len ) {
/*
* Allocate space to hold websocket payload
*/
data = mem;
if ( data_len > sizeof(mem) ) {
data = httplib_malloc( data_len );
if ( data == NULL ) {
/*
* Allocation failed, exit the loop and then close the
* connection
*/
httplib_cry( conn->ctx, conn, "websocket out of memory; closing connection" );
break;
}
}
/* Copy the mask before we shift the queue and destroy it */
if ( mask_len > 0 ) memcpy( mask, buf + header_len - mask_len, sizeof(mask) );
else memset( mask, 0, sizeof(mask) );
/*
* Read frame payload from the first message in the queue into
* data and advance the queue by moving the memory in place.
*/
if ( body_len < header_len ) {
httplib_cry( conn->ctx, conn, "websocket error: body len less than header len, closing connection" );
break;
}
if (data_len + header_len > body_len) {
/*
* current mask and opcode
*/
mop = buf[0];
/*
* Overflow case
*/
len = body_len - header_len;
memcpy( data, buf + header_len, len );
error = 0;
while ( len < data_len ) {
n = XX_httplib_pull( NULL, conn, data + len, (int)(data_len - len), timeout );
if ( n <= 0 ) {
error = 1;
break;
}
len += (size_t)n;
}
if (error) {
httplib_cry( conn->ctx, conn, "Websocket pull failed; closing connection" );
break;
}
conn->data_len = conn->request_len;
}
else {
/*
* current mask and opcode, overwritten by
* memmove()
*/
mop = buf[0];
/*
* Length of the message being read at the front of the
* queue
*/
len = data_len + header_len;
/*
* Copy the data payload into the data pointer for the
* callback
*/
memcpy( data, buf + header_len, data_len );
/*
* Move the queue forward len bytes
*/
memmove( buf, buf + len, body_len - len );
/*
* Mark the queue as advanced
*/
conn->data_len -= (int)len;
}
/*
* Apply mask if necessary
*/
if ( mask_len > 0 ) for (i=0; i<data_len; i++) data[i] ^= mask[i % 4];
/*
* Exit the loop if callback signals to exit (server side),
* or "connection close" opcode received (client side).
*/
exit_by_callback = 0;
if ((ws_data_handler != NULL) && !ws_data_handler(conn, mop, data, data_len, callback_data)) exit_by_callback = 1;
if ( data != mem ) data = httplib_free( data );
/*
* Opcode == 8, connection close
*/
if ( exit_by_callback || (mop & 0xf) == WEBSOCKET_OPCODE_CONNECTION_CLOSE ) break;
/*
* Not breaking the loop, process next websocket frame.
*/
}
else {
/*
* Read from the socket into the next available location in the
* message queue.
*/
n = XX_httplib_pull( NULL, conn, conn->buf + conn->data_len, conn->buf_size - conn->data_len, timeout );
/*
* Error, no bytes read
*/
if ( n <= 0 ) break;
conn->data_len += n;
}
}
XX_httplib_set_thread_name( "worker" );
} /* XX_httplib_read_websocket */