[Spice-devel] [PATCH spice ] Add support for clients connecting with the WebSocket protocol.
Pavel Grunt
pgrunt at redhat.com
Tue Nov 3 02:24:56 PST 2015
Hi Jeremy,
On Mon, 2015-11-02 at 15:20 -0600, Jeremy White wrote:
> Hi Pavel,
>
> Thanks for the review.
>
> > >
> > > +static void reds_handle_new_link(RedLinkInfo *link);
> > > static void reds_handle_read_header_done(void *opaque)
> > > {
> > > RedLinkInfo *link = (RedLinkInfo *)opaque;
> > > @@ -2182,6 +2183,11 @@ static void reds_handle_read_header_done(void
> > > *opaque)
> > > header->size = GUINT32_FROM_LE(header->size);
> > >
> > > if (header->magic != SPICE_MAGIC) {
> > > + if (reds_stream_is_websocket(link->stream,
> > > + (guchar *) header, sizeof(*header))) {
I did not mentioned it, but please fix the indentation ^
> > > + reds_handle_new_link(link);
> > > + return;
> > > + }
> > This looks hacky, it would be nice to detect that we are dealing with
> > websockets
> > before reds_handle_read_header_done() is called.
>
> The problem is that we cannot know we're dealing with websockets until
> we've done an initial read. And a websocket connection is going to have
> to write at least 16 bytes, so it seemed to me that we may as well read
> the 16 bytes in a SpiceLinkHeader.
ok, I think it deserves a comment in the code
>
> The alternate I considered was to inject a 3 byte read before
> read_header_done and then have that do an async read of
> sizeof(SpiceLinkHeader) - 3 if we do not have the 'GET' that signals a
> websocket connection. That didn't seem any better to me; I'm happy to
> defer to others if they feel that would be superior (or if there is an
> altogether different approach I've missed).
I think your current approach is better.
>
> In reviewing this code, there is a flaw I've skipped over. That is, the
> current implementation makes the assumption that the websocket header
> will arrive in a single packet; that there is no chance we'll get a few
> bytes, and then need another read in order to get the remaining bytes.
> I think that, in practice, this will always be the case. Further,
> afaik, we don't really have a mechanism to do a variable length/timed
> read in the current code base. My instinct is to say that limitation is
> acceptable; that the complexity of a theoretically correct
> implementation would do more harm than accepting this assumption. But I
> bring it up to give others the chance to correct me <grin>.
>
You can mention the limitation in the commit message.
> [snip]
> > > + websocket_create_reply(rbuf, len, outbuf);
> > > + rc = stream->priv->write(stream, outbuf, strlen(outbuf));
> > > + if (rc == strlen(outbuf)) {
> > > + stream->priv->ws = spice_malloc0(sizeof(*stream->priv->ws));
> >
> > It is not freed
>
> Yes, right, will fix it.
>
> > > +int websocket_is_start(gchar *buf, int len)
> >
> > It sounds like boolean
>
> Fair enough, I'll fix it.
>
> >
> > > +{
> > > + if (len < 4)
> > > + return 0;
> > > +
> > > + if (find_str(buf, "GET", len) == (buf + 3) &&
> > > + find_str(buf, "Sec-WebSocket-Protocol: binary", len) &&
> > > + find_str(buf, "Sec-WebSocket-Key:", len) &&
> > > + buf[len - 2] == '\r' && buf[len - 1] == '\n')
> > > + return 1;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +int websocket_create_reply(gchar *buf, int len, gchar *outbuf)
> >
> > It always returns 0
>
> Yep, should be a void, I'll fix it.
>
> >
> > > +{
> > > + gchar *key;
> > > +
> > > + key = generate_reply_key(buf, len);
> >
> > Can NULL be valid value? Should we have a warning for it, or change the
> > return
> > value?
>
> NULL is not a valid value; this code path should only be exercised if
> we've found the Sec-WebSocket-Key already.
>
> >
> > > + sprintf(outbuf, "HTTP/1.1 101 Switching Protocols\r\n"
> > > + "Upgrade: websocket\r\n"
> > > + "Connection: Upgrade\r\n"
> > > + "Sec-WebSocket-Accept: %s\r\n"
> > > + "Sec-WebSocket-Protocol: binary\r\n\r\n", key);
> > > + g_free(key);
> > > + return 0;
> > > +}
> > > diff --git a/server/websocket.h b/server/websocket.h
> > > new file mode 100644
> > > index 0000000..b35cb5e
> > > --- /dev/null
> > > +++ b/server/websocket.h
> > > @@ -0,0 +1,62 @@
> > > +/*
> > > + * Copyright (C) 2015 Jeremy White
> > > + *
> > > + * This library is free software; you can redistribute it and/or
> > > + * modify it under the terms of the GNU Lesser General Public
> > > + * License as published by the Free Software Foundation; either
> > > + * version 2.1 of the License, or (at your option) any later version.
> > > + *
> > > + * This library is distributed in the hope that it will be useful,
> > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> > > + * Lesser General Public License for more details.
> > > + *
> > > + * You should have received a copy of the GNU Lesser General Public
> > > + * License along with this library; if not, see <http://www.gnu.org/lice
> > > nses
> > > />.
> > > + */
> > > +
> > > +#define WEBSOCKET_MAX_HEADER_SIZE (1 + 9 + 4)
> > > +
> > > +#define FIN_FLAG 0x80
> > > +#define TYPE_MASK 0x0F
> > > +
> > > +#define BINARY_FRAME 0x2
> > > +#define CLOSE_FRAME 0x8
> > > +#define PING_FRAME 0x9
> > > +#define PONG_FRAME 0xA
> > > +
> > > +#define LENGTH_MASK 0x7F
> > > +#define LENGTH_16BIT 0x7E
> > > +#define LENGTH_64BIT 0x7F
> > > +
> > > +#define MASK_FLAG 0x80
> > > +
> > > +#define WEBSOCKET_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
> > > +
> > > +typedef struct
> > > +{
> > > + int type;
> > > + int masked;
> > > + guint8 header[WEBSOCKET_MAX_HEADER_SIZE];
> > > + int header_pos;
> > > + int frame_ready:1;
> > > + guint8 mask[4];
> > > + guint64 relayed;
> > > + guint64 expected_len;
> > > +} websocket_frame_t;
> >
> > I would prefer to have these ^ definitions private.
>
> I could probably use some guidance; there may be conventions I've
> missed. It would be easy to move most of the websocket specific
> constants into websocket.c, I'm happy to do that.
thanks
>
> It would be a bit more work to make the websocket_frame_t type be
> opaque; it would require an allocation and a free I'd find untidy.
ok, it is not the expected standard. You can keep it as it is.
> I'm
> willing to do the work if that's the expected standard; but I don't see
> the harm of having a non private WEBSOCKET_MAX_HEADER_SIZE and
> websocket_frame_t structure.
> >
> > > +
> > > +typedef size_t (*websocket_writev_cb_t)(void *opaque, struct iovec *iov,
> > > int
> > > iovcnt);
> > > +typedef size_t (*websocket_write_cb_t)(void *opaque, const void *buf,
> > > size_t
> > > nbyte);
> > > +typedef size_t (*websocket_read_cb_t)(void *opaque, const void *buf,
> > > size_t
> > > nbyte);
> >
> > RedsStreamPrivate has these ^ as ssize_t
>
> Yes, right, I'll fix it.
>
> >
> > > +
> > > +int websocket_is_start(gchar *buf, int len);
> > > +int websocket_create_reply(gchar *buf, int len, gchar *outbuf);
> > > +int websocket_read(void *opaque, guchar *buf, int len, websocket_frame_t
> > > *frame,
> > > + websocket_read_cb_t read_cb,
> > > + websocket_write_cb_t write_cb);
> > > +int websocket_write(void *opaque, const guchar *buf, int len, guint64
> > > *remainder,
> > > + websocket_write_cb_t write_cb);
> > > +int websocket_writev(void *opaque, struct iovec *iov, int iovcnt, guint64
> > > *remainder,
> > > + websocket_writev_cb_t writev_cb);
> > > +void websocket_ack_close(void *opaque, websocket_write_cb_t write_cb);
> > > +
> > extra line
>
> Right, will fix.
>
> Thanks again for the review.
>
> Cheers,
>
> Jeremy
Thanks,
Pavel
More information about the Spice-devel
mailing list