websocket: Better encapsulation

Move websocket structure declarations to C file.
Make some functions static as now not used externally.
Introduce a websocket_free function for symmetry.

Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
Acked-by: Jeremy White <jwhite@codeweavers.com>
This commit is contained in:
Frediano Ziglio 2016-11-21 12:40:31 +00:00
parent a5c05ad845
commit 55df5fa841
3 changed files with 65 additions and 62 deletions

View File

@ -436,7 +436,7 @@ void red_stream_free(RedStream *s)
SSL_free(s->priv->ssl);
}
g_free(s->priv->ws);
websocket_free(s->priv->ws);
red_stream_remove_watch(s);
socket_close(s->socket);
@ -1184,38 +1184,11 @@ static ssize_t stream_websocket_writev(RedStream *s, const struct iovec *iov, in
*/
bool red_stream_is_websocket(RedStream *stream, const void *buf, size_t len)
{
char rbuf[4096];
int rc;
if (stream->priv->ws) {
return false;
}
memcpy(rbuf, buf, len);
rc = stream->priv->read(stream, rbuf + len, sizeof(rbuf) - len - 1);
if (rc <= 0) {
return false;
}
len += rc;
rbuf[len] = 0;
/* TODO: this has a theoretical flaw around packet buffering
that is not likely to occur in practice. That is,
to be fully correct, we should repeatedly read bytes until
either we get the end of the GET header (\r\n\r\n), or until
an amount of time has passed. Instead, we just read for
16 bytes, and then read up to the sizeof rbuf. So if the
GET request is only partially complete at this point we
will fail.
A typical GET request is 520 bytes, and it's difficult to
imagine a real world case where that will come in fragmented
such that we trigger this failure. Further, the spice reds
code has no real mechanism to do variable length/time based reads,
so it seems wisest to live with this theoretical flaw.
*/
stream->priv->ws = websocket_new(rbuf, stream,
stream->priv->ws = websocket_new(buf, len, stream,
(websocket_read_cb_t) stream->priv->read,
(websocket_write_cb_t) stream->priv->write,
(websocket_writev_cb_t) stream->priv->writev);

View File

@ -63,6 +63,31 @@
#define WEBSOCKET_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
#define WEBSOCKET_MAX_HEADER_SIZE (1 + 9 + 4)
typedef struct {
int type;
int masked;
uint8_t header[WEBSOCKET_MAX_HEADER_SIZE];
int header_pos;
int frame_ready:1;
uint8_t mask[4];
uint64_t relayed;
uint64_t expected_len;
} websocket_frame_t;
struct RedsWebSocket {
bool closed;
websocket_frame_t read_frame;
uint64_t write_remainder;
void *raw_stream;
websocket_read_cb_t raw_read;
websocket_write_cb_t raw_write;
websocket_writev_cb_t raw_writev;
};
static void websocket_ack_close(void *opaque, websocket_write_cb_t write_cb);
/* Perform a case insensitive search for needle in haystack.
@ -453,7 +478,7 @@ static void websocket_ack_close(void *opaque, websocket_write_cb_t write_cb)
write_cb(opaque, header, sizeof(header));
}
bool websocket_is_start(char *buf)
static bool websocket_is_start(char *buf)
{
if (strncmp(buf, "GET ", 4) == 0 &&
// TODO strip, do not assume a single space
@ -466,7 +491,7 @@ bool websocket_is_start(char *buf)
return false;
}
void websocket_create_reply(char *buf, char *outbuf)
static void websocket_create_reply(char *buf, char *outbuf)
{
char *key;
@ -479,9 +504,35 @@ void websocket_create_reply(char *buf, char *outbuf)
g_free(key);
}
RedsWebSocket *websocket_new(char *rbuf, struct RedStream *stream, websocket_read_cb_t read_cb,
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)
{
char rbuf[4096];
memcpy(rbuf, buf, len);
int rc = read_cb(stream, rbuf + len, sizeof(rbuf) - len - 1);
if (rc <= 0) {
return NULL;
}
len += rc;
rbuf[len] = 0;
/* TODO: this has a theoretical flaw around packet buffering
that is not likely to occur in practice. That is,
to be fully correct, we should repeatedly read bytes until
either we get the end of the GET header (\r\n\r\n), or until
an amount of time has passed. Instead, we just read for
16 bytes, and then read up to the sizeof rbuf. So if the
GET request is only partially complete at this point we
will fail.
A typical GET request is 520 bytes, and it's difficult to
imagine a real world case where that will come in fragmented
such that we trigger this failure. Further, the spice reds
code has no real mechanism to do variable length/time based reads,
so it seems wisest to live with this theoretical flaw.
*/
if (!websocket_is_start(rbuf)) {
return NULL;
}
@ -489,7 +540,7 @@ RedsWebSocket *websocket_new(char *rbuf, struct RedStream *stream, websocket_rea
char outbuf[1024];
websocket_create_reply(rbuf, outbuf);
int rc = write_cb(stream, outbuf, strlen(outbuf));
rc = write_cb(stream, outbuf, strlen(outbuf));
if (rc != strlen(outbuf)) {
return NULL;
}
@ -503,3 +554,8 @@ RedsWebSocket *websocket_new(char *rbuf, struct RedStream *stream, websocket_rea
return ws;
}
void websocket_free(RedsWebSocket *ws)
{
g_free(ws);
}

View File

@ -15,41 +15,15 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#define WEBSOCKET_MAX_HEADER_SIZE (1 + 9 + 4)
struct RedStream;
typedef struct {
int type;
int masked;
uint8_t header[WEBSOCKET_MAX_HEADER_SIZE];
int header_pos;
int frame_ready:1;
uint8_t mask[4];
uint64_t relayed;
uint64_t expected_len;
} websocket_frame_t;
typedef ssize_t (*websocket_read_cb_t)(void *opaque, void *buf, size_t nbyte);
typedef ssize_t (*websocket_write_cb_t)(void *opaque, const void *buf, size_t nbyte);
typedef ssize_t (*websocket_writev_cb_t)(void *opaque, struct iovec *iov, int iovcnt);
typedef struct {
int closed;
typedef struct RedsWebSocket RedsWebSocket;
websocket_frame_t read_frame;
uint64_t write_remainder;
struct RedStream *raw_stream;
websocket_read_cb_t raw_read;
websocket_write_cb_t raw_write;
websocket_writev_cb_t raw_writev;
} RedsWebSocket;
RedsWebSocket *websocket_new(char *buf, struct RedStream *s, websocket_read_cb_t read_cb,
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);
bool websocket_is_start(char *buf);
void websocket_create_reply(char *buf, char *outbuf);
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);