Re: [Spice-devel] [PATCH spice ] Add support for clients connecting with the WebSocket protocol.

2016-11-23 Thread Christophe Fergeau
On Mon, Nov 21, 2016 at 12:06:21PM -0500, Frediano Ziglio wrote:
> > 
> > On 11/03/2015 04:32 AM, Daniel P. Berrange wrote:
> > > On Fri, Oct 30, 2015 at 03:52:56PM -0500, Jeremy White wrote:
> > >> We do this by auto detecting the inbound http(s) 'GET' and probing
> > >> for a well formulated WebSocket binary connection, such as used
> > >> by the spice-html5 client.  If detected, we implement a set of
> > >> cover functions that abstract the read/write/writev functions,
> > >> in a fashion similar to the SASL implemented.
> > > 
> > > I'm not really a huge fan of overloading two protocols on the
> > > same socket in this way.
> > 
> > Yeah, I see your unease, but I still think the approach I've chosen is
> > the best option.
> > 
> > > 
> > > I'd be rather inclined to have a separate port open for the
> > > websockets protocol, in the same way that QEMU does the VNC
> > > server.
> > 
> > I think the more apt analogy is the SASL layer, which, afaik, is not
> > inherently a different protocol, but a layer on top of the core
> > protocol.  I believe my proposed implementation implements WebSocket
> > support the way we support SASL.
> > 
> > > 
> > > Admins should be able to choose which protocol is available
> > > to their clients. For example, they might launch QEMU with
> > > both protocols available, but only wish to make one of the
> > > protocols available to the public internet. By overloading
> > > both protocols on the same port, you prevent them from
> > > being able todo this in firewall rules.
> > 
> > I'm a fan of choice, although option overload becomes an issue.  But I
> > find myself hard pressed to imagine someone wanting to have the regular
> > protocol open but not the WebSocket protocol.  If the regular port is
> > open, an evil doer can easily use websockify to get in anyway.
> > 
> > Further, you start to get a combinatorial problem.  What you're
> > proposing would add a --websocket-port option, but then it also requires
> > a --websocket-secure-port.  And if we implement SASL over WebSocket...
> > 
> > Cheers,
> > 
> > Jeremy
> 
> I agree mostly with Jeremy, adding extra ports would be quite complicated.
> On the other way I understand that someone could want to disable
> WebSockets so I would add an option (default disabled) to enable websockets,
> something in Qemu like websocket=yes/no.

As Daniel mentioned, from a libvirt/QEMU point of view, VNC websockets
use a separate port, it would be odd if the SPICE configuration was very
different. However, both are not mutually exclusive, we can have
separate ports, and an option to have the 2 protocols on the same
socket.

Christophe


signature.asc
Description: PGP signature
___
Spice-devel mailing list
Spice-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/spice-devel


Re: [Spice-devel] [PATCH spice ] Add support for clients connecting with the WebSocket protocol.

2016-11-21 Thread Frediano Ziglio
Looks weird to reply to an email after one year but I was pointed
out I miss this thread entirely.

> 
> On 11/03/2015 04:32 AM, Daniel P. Berrange wrote:
> > On Fri, Oct 30, 2015 at 03:52:56PM -0500, Jeremy White wrote:
> >> We do this by auto detecting the inbound http(s) 'GET' and probing
> >> for a well formulated WebSocket binary connection, such as used
> >> by the spice-html5 client.  If detected, we implement a set of
> >> cover functions that abstract the read/write/writev functions,
> >> in a fashion similar to the SASL implemented.
> > 
> > I'm not really a huge fan of overloading two protocols on the
> > same socket in this way.
> 
> Yeah, I see your unease, but I still think the approach I've chosen is
> the best option.
> 
> > 
> > I'd be rather inclined to have a separate port open for the
> > websockets protocol, in the same way that QEMU does the VNC
> > server.
> 
> I think the more apt analogy is the SASL layer, which, afaik, is not
> inherently a different protocol, but a layer on top of the core
> protocol.  I believe my proposed implementation implements WebSocket
> support the way we support SASL.
> 
> > 
> > Admins should be able to choose which protocol is available
> > to their clients. For example, they might launch QEMU with
> > both protocols available, but only wish to make one of the
> > protocols available to the public internet. By overloading
> > both protocols on the same port, you prevent them from
> > being able todo this in firewall rules.
> 
> I'm a fan of choice, although option overload becomes an issue.  But I
> find myself hard pressed to imagine someone wanting to have the regular
> protocol open but not the WebSocket protocol.  If the regular port is
> open, an evil doer can easily use websockify to get in anyway.
> 
> Further, you start to get a combinatorial problem.  What you're
> proposing would add a --websocket-port option, but then it also requires
> a --websocket-secure-port.  And if we implement SASL over WebSocket...
> 
> Cheers,
> 
> Jeremy

I agree mostly with Jeremy, adding extra ports would be quite complicated.
On the other way I understand that someone could want to disable
WebSockets so I would add an option (default disabled) to enable websockets,
something in Qemu like websocket=yes/no.

Frediano
___
Spice-devel mailing list
Spice-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/spice-devel


Re: [Spice-devel] [PATCH spice ] Add support for clients connecting with the WebSocket protocol.

2015-11-03 Thread Daniel P. Berrange
On Fri, Oct 30, 2015 at 03:52:56PM -0500, Jeremy White wrote:
> We do this by auto detecting the inbound http(s) 'GET' and probing
> for a well formulated WebSocket binary connection, such as used
> by the spice-html5 client.  If detected, we implement a set of
> cover functions that abstract the read/write/writev functions,
> in a fashion similar to the SASL implemented.

I'm not really a huge fan of overloading two protocols on the
same socket in this way.

I'd be rather inclined to have a separate port open for the
websockets protocol, in the same way that QEMU does the VNC
server.

Admins should be able to choose which protocol is available
to their clients. For example, they might launch QEMU with
both protocols available, but only wish to make one of the
protocols available to the public internet. By overloading
both protocols on the same port, you prevent them from
being able todo this in firewall rules.

Regards,
Daniel
-- 
|: http://berrange.com  -o-http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org  -o- http://virt-manager.org :|
|: http://autobuild.org   -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org   -o-   http://live.gnome.org/gtk-vnc :|
___
Spice-devel mailing list
Spice-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/spice-devel


Re: [Spice-devel] [PATCH spice ] Add support for clients connecting with the WebSocket protocol.

2015-11-03 Thread Jeremy White
On 11/03/2015 04:32 AM, Daniel P. Berrange wrote:
> On Fri, Oct 30, 2015 at 03:52:56PM -0500, Jeremy White wrote:
>> We do this by auto detecting the inbound http(s) 'GET' and probing
>> for a well formulated WebSocket binary connection, such as used
>> by the spice-html5 client.  If detected, we implement a set of
>> cover functions that abstract the read/write/writev functions,
>> in a fashion similar to the SASL implemented.
> 
> I'm not really a huge fan of overloading two protocols on the
> same socket in this way.

Yeah, I see your unease, but I still think the approach I've chosen is
the best option.

> 
> I'd be rather inclined to have a separate port open for the
> websockets protocol, in the same way that QEMU does the VNC
> server.

I think the more apt analogy is the SASL layer, which, afaik, is not
inherently a different protocol, but a layer on top of the core
protocol.  I believe my proposed implementation implements WebSocket
support the way we support SASL.

> 
> Admins should be able to choose which protocol is available
> to their clients. For example, they might launch QEMU with
> both protocols available, but only wish to make one of the
> protocols available to the public internet. By overloading
> both protocols on the same port, you prevent them from
> being able todo this in firewall rules.

I'm a fan of choice, although option overload becomes an issue.  But I
find myself hard pressed to imagine someone wanting to have the regular
protocol open but not the WebSocket protocol.  If the regular port is
open, an evil doer can easily use websockify to get in anyway.

Further, you start to get a combinatorial problem.  What you're
proposing would add a --websocket-port option, but then it also requires
a --websocket-secure-port.  And if we implement SASL over WebSocket...

Cheers,

Jeremy
___
Spice-devel mailing list
Spice-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/spice-devel


Re: [Spice-devel] [PATCH spice ] Add support for clients connecting with the WebSocket protocol.

2015-11-03 Thread Pavel Grunt
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 .
> 
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 000..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 

Re: [Spice-devel] [PATCH spice ] Add support for clients connecting with the WebSocket protocol.

2015-11-02 Thread Pavel Grunt
Hi Jeremy,

I really like how it works, that you can use the same port value for html5 and
gtk clients.
Not everything was clear to me, so I put some comments inline.

On Fri, 2015-10-30 at 15:52 -0500, Jeremy White wrote:
> We do this by auto detecting the inbound http(s) 'GET' and probing
> for a well formulated WebSocket binary connection, such as used
> by the spice-html5 client.  If detected, we implement a set of
> cover functions that abstract the read/write/writev functions,
> in a fashion similar to the SASL implemented.
> 
> This includes a limited implementation of the WebSocket protocol,
> sufficient for our purposes.
> 
> Signed-off-by: Jeremy White 
> ---
>  server/Makefile.am   |   2 +
>  server/reds.c|   6 +
>  server/reds_stream.c |  98 
>  server/reds_stream.h |   2 +
>  server/websocket.c   | 432
> +++
>  server/websocket.h   |  62 
>  6 files changed, 602 insertions(+)
>  create mode 100644 server/websocket.c
>  create mode 100644 server/websocket.h
> 
> diff --git a/server/Makefile.am b/server/Makefile.am
> index 87288cc..e2f5452 100644
> --- a/server/Makefile.am
> +++ b/server/Makefile.am
> @@ -113,6 +113,8 @@ libspice_server_la_SOURCES =  \
>   reds-private.h  \
>   reds_stream.c   \
>   reds_stream.h   \
> + websocket.c \
> + websocket.h \
>   reds_sw_canvas.c\
>   reds_sw_canvas.h\
>   snd_worker.c\
> diff --git a/server/reds.c b/server/reds.c
> index 1f6774e..701ac9d 100644
> --- a/server/reds.c
> +++ b/server/reds.c
> @@ -2172,6 +2172,7 @@ static void reds_handle_link_error(void *opaque, int
> err)
>  reds_link_free(link);
>  }
>  
> +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))) {
> +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.

>  reds_send_link_error(link, SPICE_LINK_ERR_INVALID_MAGIC);
>  reds_link_free(link);
>  return;
> diff --git a/server/reds_stream.c b/server/reds_stream.c
> index 3b47391..9f813cf 100644
> --- a/server/reds_stream.c
> +++ b/server/reds_stream.c
> @@ -30,6 +30,7 @@
>  #include 
>  
>  #include 
> +#include "websocket.h"
>  
>  #include 
>  
> @@ -76,6 +77,17 @@ typedef struct RedsSASL {
>  } RedsSASL;
>  #endif
>  
> +typedef struct {
> +int closed;
> +
> +websocket_frame_t read_frame;
> +guint64 write_remainder;
> +
> +ssize_t (*raw_read)(RedsStream *s, void *buf, size_t nbyte);
> +ssize_t (*raw_write)(RedsStream *s, const void *buf, size_t nbyte);
> +ssize_t (*raw_writev)(RedsStream *s, const struct iovec *iov, int
> iovcnt);
> +} RedsWebSocket;
> +
>  struct RedsStreamPrivate {
>  SSL *ssl;
>  
> @@ -85,6 +97,8 @@ struct RedsStreamPrivate {
>  
>  AsyncRead async_read;
>  
> +RedsWebSocket *ws;
> +
>  /* life time of info:
>   * allocated when creating RedsStream.
>   * deallocated when main_dispatcher handles the
> SPICE_CHANNEL_EVENT_DISCONNECTED
> @@ -1068,3 +1082,87 @@ error:
>  return FALSE;
>  }
>  #endif
> +
> +static ssize_t stream_websocket_read(RedsStream *s, void *buf, size_t size)
> +{
> +int rc;
> +
> +if (s->priv->ws->closed)
> +return 0;
> +
> +rc = websocket_read((void *)s, buf, size, >priv->ws->read_frame,
> +(websocket_write_cb_t) s->priv->ws->raw_read,
> +(websocket_write_cb_t) s->priv->ws->raw_write);
> +
> +if (rc == 0)
> +s->priv->ws->closed = 1;
> +
> +return rc;
> +}
> +
> +static ssize_t stream_websocket_write(RedsStream *s, const void *buf, size_t
> size)
> +{
> +if (s->priv->ws->closed) {
> +errno = EPIPE;
> +return -1;
> +}
> +return websocket_write((void *)s, buf, size, >priv->ws-
> >write_remainder,
> +(websocket_write_cb_t) s->priv->ws->raw_write);
> +}
> +
> +static ssize_t stream_websocket_writev(RedsStream *s, const struct iovec
> *iov, int iovcnt)
> +{
> +if (s->priv->ws->closed) {
> +errno = EPIPE;
> +return -1;
> +}
> +return websocket_writev((void *)s, (struct iovec *) iov, iovcnt, 
> >priv->ws->write_remainder,
> +(websocket_writev_cb_t) s->priv->ws->raw_writev);
> +}
> +
> +/*
> +If we detect that a 

Re: [Spice-devel] [PATCH spice ] Add support for clients connecting with the WebSocket protocol.

2015-11-02 Thread Jeremy White
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))) {
>> +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.

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).

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 .

[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 000..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 
>> > />.
>> + */
>> +
>> +#define WEBSOCKET_MAX_HEADER_SIZE (1 + 9 + 4)
>> +
>> +#define FIN_FLAG0x80
>> +#define TYPE_MASK   0x0F
>> +
>> +#define BINARY_FRAME0x2
>> +#define CLOSE_FRAME 0x8
>> +#define PING_FRAME  0x9
>> +#define PONG_FRAME  0xA
>> +
>> +#define LENGTH_MASK 0x7F
>> +#define LENGTH_16BIT0x7E
>> +#define LENGTH_64BIT0x7F
>> +
>> +#define MASK_FLAG   0x80
>> +
>> +#define WEBSOCKET_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
>> +
>> 

[Spice-devel] [PATCH spice ] Add support for clients connecting with the WebSocket protocol.

2015-10-30 Thread Jeremy White
We do this by auto detecting the inbound http(s) 'GET' and probing
for a well formulated WebSocket binary connection, such as used
by the spice-html5 client.  If detected, we implement a set of
cover functions that abstract the read/write/writev functions,
in a fashion similar to the SASL implemented.

This includes a limited implementation of the WebSocket protocol,
sufficient for our purposes.

Signed-off-by: Jeremy White 
---
 server/Makefile.am   |   2 +
 server/reds.c|   6 +
 server/reds_stream.c |  98 
 server/reds_stream.h |   2 +
 server/websocket.c   | 432 +++
 server/websocket.h   |  62 
 6 files changed, 602 insertions(+)
 create mode 100644 server/websocket.c
 create mode 100644 server/websocket.h

diff --git a/server/Makefile.am b/server/Makefile.am
index 87288cc..e2f5452 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -113,6 +113,8 @@ libspice_server_la_SOURCES =\
reds-private.h  \
reds_stream.c   \
reds_stream.h   \
+   websocket.c \
+   websocket.h \
reds_sw_canvas.c\
reds_sw_canvas.h\
snd_worker.c\
diff --git a/server/reds.c b/server/reds.c
index 1f6774e..701ac9d 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -2172,6 +2172,7 @@ static void reds_handle_link_error(void *opaque, int err)
 reds_link_free(link);
 }
 
+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))) {
+reds_handle_new_link(link);
+return;
+}
 reds_send_link_error(link, SPICE_LINK_ERR_INVALID_MAGIC);
 reds_link_free(link);
 return;
diff --git a/server/reds_stream.c b/server/reds_stream.c
index 3b47391..9f813cf 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -30,6 +30,7 @@
 #include 
 
 #include 
+#include "websocket.h"
 
 #include 
 
@@ -76,6 +77,17 @@ typedef struct RedsSASL {
 } RedsSASL;
 #endif
 
+typedef struct {
+int closed;
+
+websocket_frame_t read_frame;
+guint64 write_remainder;
+
+ssize_t (*raw_read)(RedsStream *s, void *buf, size_t nbyte);
+ssize_t (*raw_write)(RedsStream *s, const void *buf, size_t nbyte);
+ssize_t (*raw_writev)(RedsStream *s, const struct iovec *iov, int iovcnt);
+} RedsWebSocket;
+
 struct RedsStreamPrivate {
 SSL *ssl;
 
@@ -85,6 +97,8 @@ struct RedsStreamPrivate {
 
 AsyncRead async_read;
 
+RedsWebSocket *ws;
+
 /* life time of info:
  * allocated when creating RedsStream.
  * deallocated when main_dispatcher handles the 
SPICE_CHANNEL_EVENT_DISCONNECTED
@@ -1068,3 +1082,87 @@ error:
 return FALSE;
 }
 #endif
+
+static ssize_t stream_websocket_read(RedsStream *s, void *buf, size_t size)
+{
+int rc;
+
+if (s->priv->ws->closed)
+return 0;
+
+rc = websocket_read((void *)s, buf, size, >priv->ws->read_frame,
+(websocket_write_cb_t) s->priv->ws->raw_read,
+(websocket_write_cb_t) s->priv->ws->raw_write);
+
+if (rc == 0)
+s->priv->ws->closed = 1;
+
+return rc;
+}
+
+static ssize_t stream_websocket_write(RedsStream *s, const void *buf, size_t 
size)
+{
+if (s->priv->ws->closed) {
+errno = EPIPE;
+return -1;
+}
+return websocket_write((void *)s, buf, size, >priv->ws->write_remainder,
+(websocket_write_cb_t) s->priv->ws->raw_write);
+}
+
+static ssize_t stream_websocket_writev(RedsStream *s, const struct iovec *iov, 
int iovcnt)
+{
+if (s->priv->ws->closed) {
+errno = EPIPE;
+return -1;
+}
+return websocket_writev((void *)s, (struct iovec *) iov, iovcnt, 
>priv->ws->write_remainder,
+(websocket_writev_cb_t) s->priv->ws->raw_writev);
+}
+
+/*
+If we detect that a newly opened stream appears to be using
+the WebSocket protocol, we will put in place cover functions
+that will speak WebSocket to the client, but allow the server
+to continue to use normal stream read/write/writev semantics.
+*/
+bool reds_stream_is_websocket(RedsStream *stream, unsigned char *buf, int 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);
+if (rc <= 0)
+return FALSE;
+len += rc;
+
+if (websocket_is_start(rbuf, len)) {
+