mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2026-08-06 16:25:31 +00:00
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:
parent
0c7f91511d
commit
9d6a438aae
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user