websocket: Handle text data

Allows to specify and get frame type.
Type and flags are returned calling websocket_read and returned
calling websocket_write or websocket_writev.

Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
Acked-by: Jeremy White <jwhite@codeweavers.com>
This commit is contained in:
Frediano Ziglio 2019-06-23 09:03:54 +01:00
parent 0c7f91511d
commit 9d6a438aae
4 changed files with 48 additions and 18 deletions

View File

@ -1163,17 +1163,23 @@ error:
static ssize_t stream_websocket_read(RedStream *s, void *buf, size_t size)
{
return websocket_read(s->priv->ws, buf, size);
unsigned flags;
int len;
do {
len = websocket_read(s->priv->ws, buf, size, &flags);
} while (len == 0 && flags != 0);
return len;
}
static ssize_t stream_websocket_write(RedStream *s, const void *buf, size_t size)
{
return websocket_write(s->priv->ws, buf, size);
return websocket_write(s->priv->ws, buf, size, WEBSOCKET_BINARY_FINAL);
}
static ssize_t stream_websocket_writev(RedStream *s, const struct iovec *iov, int iovcnt)
{
return websocket_writev(s->priv->ws, (struct iovec *) iov, iovcnt);
return websocket_writev(s->priv->ws, (struct iovec *) iov, iovcnt, WEBSOCKET_BINARY_FINAL);
}
/*

View File

@ -220,6 +220,7 @@ handle_client(int new_sock)
char buffer[4096];
size_t to_send = 0;
unsigned ws_flags = WEBSOCKET_BINARY_FINAL;
while (!got_term) {
int events = 0;
if (sizeof(buffer) > to_send) {
@ -231,7 +232,8 @@ handle_client(int new_sock)
events = wait_for(new_sock, events);
if (events & POLLIN) {
assert(sizeof(buffer) > to_send);
int size = websocket_read(ws, (void *) (buffer + to_send), sizeof(buffer) - to_send);
int size = websocket_read(ws, (void *) (buffer + to_send), sizeof(buffer) - to_send,
&ws_flags);
if (size < 0) {
if (errno == EIO) {
@ -254,7 +256,7 @@ handle_client(int new_sock)
}
if (events & POLLOUT) {
int size = websocket_write(ws, buffer, to_send);
int size = websocket_write(ws, buffer, to_send, ws_flags);
if (size < 0) {
switch (errno) {

View File

@ -313,12 +313,14 @@ static void relay_data(uint8_t* buf, size_t size, websocket_frame_t *frame)
}
}
int websocket_read(RedsWebSocket *ws, uint8_t *buf, size_t size)
int websocket_read(RedsWebSocket *ws, uint8_t *buf, size_t size, unsigned *flags)
{
int n = 0;
int rc;
websocket_frame_t *frame = &ws->read_frame;
*flags = 0;
if (ws->closed || ws->close_pending) {
/* this avoids infinite loop in the case connection is still open and we have
* pending data */
@ -355,13 +357,15 @@ int websocket_read(RedsWebSocket *ws, uint8_t *buf, size_t size)
websocket_clear_frame(frame);
send_pending_data(ws);
return 0;
} else if (frame->type == BINARY_FRAME) {
} else if (frame->type == BINARY_FRAME || frame->type == TEXT_FRAME) {
rc = ws->raw_read(ws->raw_stream, buf,
MIN(size, frame->expected_len - frame->relayed));
if (rc <= 0) {
goto read_error;
}
*flags = frame->type;
relay_data(buf, rc, frame);
n += rc;
buf += rc;
@ -406,6 +410,9 @@ int websocket_read(RedsWebSocket *ws, uint8_t *buf, size_t size)
frame->relayed += rc;
if (frame->relayed >= frame->expected_len) {
websocket_clear_frame(frame);
if (n) {
break;
}
}
}
@ -421,12 +428,13 @@ read_error:
return rc;
}
static int fill_header(uint8_t *header, uint64_t len)
static int fill_header(uint8_t *header, uint64_t len, uint8_t type)
{
int used = 0;
int i;
header[0] = FIN_FLAG | BINARY_FRAME;
type &= TYPE_MASK;
header[0] = FIN_FLAG | (type ? type : BINARY_FRAME);
used++;
header[1] = 0;
@ -497,14 +505,14 @@ static int send_data_header_left(RedsWebSocket *ws)
return -1;
}
static int send_data_header(RedsWebSocket *ws, uint64_t len)
static int send_data_header(RedsWebSocket *ws, uint64_t len, uint8_t type)
{
spice_assert(ws->write_header_pos >= ws->write_header_len);
spice_assert(ws->write_remainder == 0);
/* fill a new header */
ws->write_header_pos = 0;
ws->write_header_len = fill_header(ws->write_header, len);
ws->write_header_len = fill_header(ws->write_header, len, type);
return send_data_header_left(ws);
}
@ -557,7 +565,7 @@ static int send_pending_data(RedsWebSocket *ws)
}
/* Write a WebSocket frame with the enclosed data out. */
int websocket_writev(RedsWebSocket *ws, const struct iovec *iov, int iovcnt)
int websocket_writev(RedsWebSocket *ws, const struct iovec *iov, int iovcnt, unsigned flags)
{
uint64_t len;
int rc;
@ -595,7 +603,7 @@ int websocket_writev(RedsWebSocket *ws, const struct iovec *iov, int iovcnt)
}
ws->write_header_pos = 0;
ws->write_header_len = fill_header(ws->write_header, len);
ws->write_header_len = fill_header(ws->write_header, len, flags);
iov_out[0].iov_len = ws->write_header_len;
iov_out[0].iov_base = ws->write_header;
rc = ws->raw_writev(ws->raw_stream, iov_out, iov_out_cnt);
@ -622,7 +630,7 @@ int websocket_writev(RedsWebSocket *ws, const struct iovec *iov, int iovcnt)
return rc;
}
int websocket_write(RedsWebSocket *ws, const void *buf, size_t len)
int websocket_write(RedsWebSocket *ws, const void *buf, size_t len, unsigned flags)
{
int rc;
@ -636,7 +644,7 @@ int websocket_write(RedsWebSocket *ws, const void *buf, size_t len)
return rc;
}
if (ws->write_remainder == 0) {
rc = send_data_header(ws, len);
rc = send_data_header(ws, len, flags);
if (rc <= 0) {
return rc;
}

View File

@ -21,9 +21,23 @@ typedef ssize_t (*websocket_writev_cb_t)(void *opaque, struct iovec *iov, int io
typedef struct RedsWebSocket RedsWebSocket;
enum {
WEBSOCKET_TEXT = 1,
WEBSOCKET_BINARY= 2,
WEBSOCKET_FINAL = 0x80,
WEBSOCKET_TEXT_FINAL = WEBSOCKET_TEXT | WEBSOCKET_FINAL,
WEBSOCKET_BINARY_FINAL = WEBSOCKET_BINARY | WEBSOCKET_FINAL,
};
RedsWebSocket *websocket_new(const void *buf, size_t len, void *stream, websocket_read_cb_t read_cb,
websocket_write_cb_t write_cb, websocket_writev_cb_t writev_cb);
void websocket_free(RedsWebSocket *ws);
int websocket_read(RedsWebSocket *ws, uint8_t *buf, size_t len);
int websocket_write(RedsWebSocket *ws, const void *buf, size_t len);
int websocket_writev(RedsWebSocket *ws, const struct iovec *iov, int iovcnt);
/**
* Read data from websocket.
* Can return 0 in case client sent an empty text/binary data, check
* flags to detect this.
*/
int websocket_read(RedsWebSocket *ws, uint8_t *buf, size_t len, unsigned *flags);
int websocket_write(RedsWebSocket *ws, const void *buf, size_t len, unsigned flags);
int websocket_writev(RedsWebSocket *ws, const struct iovec *iov, int iovcnt, unsigned flags);