[Spice-devel] [PATCH 4/5] server: add websockets support via libwebsockets

Alon Levy alevy at redhat.com
Tue Oct 23 08:37:21 PDT 2012


> New API: spice_server_set_ws_ports

Dropping this for now, I've noticed repeated connections failing, I screwed up something with adding multiple watches for the same fd (you end up having ERROR prints from libwebsockets - which still needs to be patched to reduce the amount of default messages to stderr to 0).

Jeremy, if you do test this and find a solution, don't forget to address Christophe's review.

Meanwhile I also discovered there is a fork of libwebsockets that might have some interesting performance benefits as well at https://github.com/davidgaleano/libwebsockets.

> 
> This adds an optional dependency on libwebsockets. You need to get my
> patched 0.0.3 version here:
>  git://people.freedesktop.org/~alon/libwebsockets
> 
> There is no qemu patches yet, to test change in reds.c the default
> value
> of spice_ws_port to 5959 (for the default of spice-html5).
> 
> For testing there is an online client at
>  http://spice-space.org/spice-html5/spice.html
> 
> Known issues:
>  1. The tester (server/tests/test_display_no_ssl) gets into dropping
>  all
>   data after a few seconds, I think it's an issue with the
>   implemented
>   watches, but haven't figured it out.
> 
>  2. libwebsocket's read interface is inverted to what our code
>  expects,
>  i.e. there is no libwebsocket_read, so there is an additional copy
>  involved (see RedsWebSocket). This can be fixed.
> 
>  3. Listening on a separate port. Since the headers are different, we
>  could listen on the same port (first three bytes RED/GET). I don't
>  know
>  if we want to?
> 
> Todos:
>  1. SSL not implemented yet. Needs some thought as to how.
> 
>  2. Serve spice-html5 when accessed as a http server. Nice to have.
> ---
>  configure.ac                     |  15 ++
>  server/Makefile.am               |   4 +
>  server/reds-private.h            |  47 +++++-
>  server/reds.c                    |  79 ++++++----
>  server/reds.h                    |  17 +++
>  server/reds_websockets.c         | 311
>  +++++++++++++++++++++++++++++++++++++++
>  server/reds_websockets.h         |   9 ++
>  server/spice-server.syms         |   5 +
>  server/spice.h                   |   7 +
>  server/tests/test_display_base.c |   4 +-
>  10 files changed, 466 insertions(+), 32 deletions(-)
>  create mode 100644 server/reds_websockets.c
>  create mode 100644 server/reds_websockets.h
> 
> diff --git a/configure.ac b/configure.ac
> index dff930d..5561d2c 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -160,6 +160,13 @@ AC_ARG_ENABLE(automated_tests,
>  AS_IF([test x"$enable_automated_tests" != "xno"],
>  [enable_automated_tests="yes"])
>  AM_CONDITIONAL(SUPPORT_AUTOMATED_TESTS, test
>  "x$enable_automated_tests" != "xno")
>  
> +AC_ARG_ENABLE(libwebsockets,
> +[  --enable-libwebsockets    Enable websockets server support (no
> need for proxy)],,
> +[enable_libwebsockets="yes"])
> +AS_IF([test x"enable_libwebsockets" != "xyes"],
> [enable_websockets="no"])
> +if test "x$enable_libwebsockets" = "xyes"; then
> +   AC_DEFINE([USE_LIBWEBSOCKETS], [1], [Define if supporting
> websocket connections])
> +fi
>  
>  dnl
>  =========================================================================
>  dnl Check deps
> @@ -237,6 +244,12 @@ if test "x$enable_smartcard" = "xyes"; then
>      SPICE_REQUIRES+=" libcacard >= 0.1.2"
>  fi
>  
> +if test "x$enable_libwebsockets" = "xyes"; then
> +    PKG_CHECK_MODULES(LIBWEBSOCKETS, libwebsockets >= 0.0.3)
> +    AC_SUBST(LIBWEBSOCKETS_LIBS)
> +    AC_SUBST(LIBWEBSOCKETS_CFLAGS)
> +    SPICE_REQUIRES+=" libwebsockets >= 0.0.3"
> +fi
>  
>  PKG_CHECK_MODULES(PIXMAN, pixman-1 >= 0.17.7)
>  AC_SUBST(PIXMAN_CFLAGS)
> @@ -536,6 +549,8 @@ echo "
>          SASL support:             ${enable_sasl}
>  
>          Automated tests:          ${enable_automated_tests}
> +
> +        libwebsockets:            ${enable_libwebsockets}
>  "
>  
>  if test $os_win32 == "yes" ; then
> diff --git a/server/Makefile.am b/server/Makefile.am
> index b62d98c..a7d56d4 100644
> --- a/server/Makefile.am
> +++ b/server/Makefile.am
> @@ -10,6 +10,7 @@ AM_CPPFLAGS =					\
>  	$(SLIRP_CFLAGS)				\
>  	$(SMARTCARD_CFLAGS)			\
>  	$(SSL_CFLAGS)				\
> +	$(LIBWEBSOCKETS_CFLAGS)		\
>  	$(VISIBILITY_HIDDEN_CFLAGS)		\
>  	$(WARN_CFLAGS)				\
>  	$(NULL)
> @@ -38,6 +39,7 @@ libspice_server_la_LIBADD =						\
>  	$(SLIRP_LIBS)							\
>  	$(SSL_LIBS)							\
>  	$(Z_LIBS)							\
> +	$(LIBWEBSOCKETS_LIBS)				\
>  	$(NULL)
>  
>  libspice_server_la_SOURCES =			\
> @@ -91,6 +93,8 @@ libspice_server_la_SOURCES =			\
>  	spicevmc.c				\
>  	zlib_encoder.c				\
>  	zlib_encoder.h				\
> +	reds_websockets.c				\
> +	reds_websockets.h				\
>  	$(NULL)
>  
>  if SUPPORT_TUNNEL
> diff --git a/server/reds-private.h b/server/reds-private.h
> index 3db6565..a5903b3 100644
> --- a/server/reds-private.h
> +++ b/server/reds-private.h
> @@ -4,6 +4,16 @@
>  #include <time.h>
>  
>  #include <spice/protocol.h>
> +#include <spice/stats.h>
> +
> +#if USE_LIBWEBSOCKETS
> +#include <libwebsockets.h>
> +#endif
> +
> +#include "reds.h"
> +#include "char_device.h"
> +#include "agent-msg-filter.h"
> +#include "main_channel.h"
>  
>  #define MIGRATE_TIMEOUT (1000 * 10) /* 10sec */
>  #define MM_TIMER_GRANULARITY_MS (1000 / 30)
> @@ -34,10 +44,6 @@ typedef struct VDIReadBuf {
>      uint8_t data[SPICE_AGENT_MAX_DATA_SIZE];
>  } VDIReadBuf;
>  
> -static VDIReadBuf *vdi_port_read_buf_get(void);
> -static VDIReadBuf *vdi_port_read_buf_ref(VDIReadBuf *buf);
> -static void vdi_port_read_buf_unref(VDIReadBuf *buf);
> -
>  enum {
>      VDI_PORT_READ_STATE_READ_HEADER,
>      VDI_PORT_READ_STATE_GET_BUFF,
> @@ -125,9 +131,19 @@ typedef struct RedsClientMonitorsConfig {
>      int buffer_pos;
>  } RedsClientMonitorsConfig;
>  
> +#ifdef USE_LIBWEBSOCKETS
> +#define REDS_MAX_WEBSOCKETS 32
> +#endif
> +
>  typedef struct RedsState {
>      int listen_socket;
>      int secure_listen_socket;
> +#ifdef USE_LIBWEBSOCKETS
> +	struct libwebsocket_context *ws_context;
> +    RedsWebSocket ws[REDS_MAX_WEBSOCKETS];
> +    int ws_in_service_fd;
> +    int ws_count;
> +#endif
>      SpiceWatch *listen_watch;
>      SpiceWatch *secure_listen_watch;
>      VDIPortState agent_state;
> @@ -179,4 +195,27 @@ typedef struct RedsState {
>      RedsClientMonitorsConfig client_monitors_config;
>  } RedsState;
>  
> +typedef struct AsyncRead {
> +    RedsStream *stream;
> +    void *opaque;
> +    uint8_t *now;
> +    uint8_t *end;
> +    void (*done)(void *opaque);
> +    void (*error)(void *opaque, int err);
> +} AsyncRead;
> +
> +typedef struct RedLinkInfo {
> +    RedsStream *stream;
> +    AsyncRead asyc_read;
> +    SpiceLinkHeader link_header;
> +    SpiceLinkMess *link_mess;
> +    int mess_pos;
> +    TicketInfo tiTicketing;
> +    SpiceLinkAuthMechanism auth_mechanism;
> +    int skip_auth;
> +} RedLinkInfo;
> +
> +RedLinkInfo *spice_server_add_client_create_link(SpiceServer *s, int
> socket, int skip_auth);
> +void reds_handle_new_link(RedLinkInfo *link);
> +
>  #endif
> diff --git a/server/reds.c b/server/reds.c
> index 98c8706..bd16764 100644
> --- a/server/reds.c
> +++ b/server/reds.c
> @@ -74,9 +74,16 @@
>  #ifdef USE_SMARTCARD
>  #include "smartcard.h"
>  #endif
> +#if USE_LIBWEBSOCKETS
> +#include "reds_websockets.h"
> +#endif
>  
>  #include "reds-private.h"
>  
> +static VDIReadBuf *vdi_port_read_buf_get(void);
> +static VDIReadBuf *vdi_port_read_buf_ref(VDIReadBuf *buf);
> +static void vdi_port_read_buf_unref(VDIReadBuf *buf);
> +
>  SpiceCoreInterface *core = NULL;
>  static SpiceCharDeviceInstance *vdagent = NULL;
>  static SpiceMigrateInstance *migration_interface = NULL;
> @@ -99,6 +106,10 @@ static TicketAuthentication taTicket;
>  
>  static int spice_port = -1;
>  static int spice_secure_port = -1;
> +#if USE_LIBWEBSOCKETS
> +static int spice_ws_port = -1;
> +static int spice_wss_port = -1;
> +#endif
>  static int spice_listen_socket_fd = -1;
>  static char spice_addr[256];
>  static int spice_family = PF_UNSPEC;
> @@ -127,26 +138,6 @@ static bool exit_on_disconnect = FALSE;
>  
>  static RedsState *reds = NULL;
>  
> -typedef struct AsyncRead {
> -    RedsStream *stream;
> -    void *opaque;
> -    uint8_t *now;
> -    uint8_t *end;
> -    void (*done)(void *opaque);
> -    void (*error)(void *opaque, int err);
> -} AsyncRead;
> -
> -typedef struct RedLinkInfo {
> -    RedsStream *stream;
> -    AsyncRead asyc_read;
> -    SpiceLinkHeader link_header;
> -    SpiceLinkMess *link_mess;
> -    int mess_pos;
> -    TicketInfo tiTicketing;
> -    SpiceLinkAuthMechanism auth_mechanism;
> -    int skip_auth;
> -} RedLinkInfo;
> -
>  typedef struct RedSSLParameters {
>      char keyfile_password[256];
>      char certs_file[256];
> @@ -2718,7 +2709,7 @@ static void reds_handle_read_header_done(void
> *opaque)
>      async_read_handler(0, 0, &link->asyc_read);
>  }
>  
> -static void reds_handle_new_link(RedLinkInfo *link)
> +void reds_handle_new_link(RedLinkInfo *link)
>  {
>      AsyncRead *obj = &link->asyc_read;
>      obj->opaque = link;
> @@ -2882,7 +2873,6 @@ static void reds_accept_ssl_connection(int fd,
> int event, void *data)
>      }
>  }
>  
> -
>  static void reds_accept(int fd, int event, void *data)
>  {
>      int socket;
> @@ -2896,25 +2886,33 @@ static void reds_accept(int fd, int event,
> void *data)
>          close(socket);
>  }
>  
> -
> -SPICE_GNUC_VISIBLE int spice_server_add_client(SpiceServer *s, int
> socket, int skip_auth)
> +RedLinkInfo *spice_server_add_client_create_link(SpiceServer *s, int
> socket, int skip_auth)
>  {
>      RedLinkInfo *link;
> -    RedsStream *stream;
>  
>      spice_assert(reds == s);
>      if (!(link = reds_init_client_connection(socket))) {
>          spice_warning("accept failed");
> -        return -1;
> +        return NULL;
>      }
>  
>      link->skip_auth = skip_auth;
> +    return link;
> +}
> +
> +SPICE_GNUC_VISIBLE int spice_server_add_client(SpiceServer *s, int
> socket, int skip_auth)
> +{
> +    RedLinkInfo *link;
> +    RedsStream *stream;
>  
> +    link = spice_server_add_client_create_link(s, socket,
> skip_auth);
> +    if (!link) {
> +        return -1;
> +    }
>      stream = link->stream;
>      stream->read = stream_read_cb;
>      stream->write = stream_write_cb;
>      stream->writev = stream_writev_cb;
> -
>      reds_handle_new_link(link);
>      return 0;
>  }
> @@ -3033,6 +3031,12 @@ static int reds_init_net(void)
>              return -1;
>          }
>      }
> +
> +#if USE_LIBWEBSOCKETS
> +    if (spice_ws_port != -1 || spice_wss_port != -1) {
> +        reds_init_websocket(reds, spice_addr, spice_ws_port,
> spice_wss_port);
> +    }
> +#endif
>      return 0;
>  }
>  
> @@ -3949,6 +3953,27 @@ SPICE_GNUC_VISIBLE int
> spice_server_set_port(SpiceServer *s, int port)
>      return 0;
>  }
>  
> +
> +SPICE_GNUC_VISIBLE int spice_server_set_ws_ports(SpiceServer *s, int
> ws_port, int wss_port)
> +{
> +#if USE_LIBWEBSOCKETS
> +    spice_assert(reds == s);
> +    if ((ws_port < 1 || ws_port > 0xffff) && (wss_port < 1 ||
> wss_port > 0xffff)) {
> +        return -1;
> +    }
> +    if (ws_port > 0 && ws_port < 0xffff) {
> +        spice_ws_port = ws_port;
> +    }
> +    if (wss_port > 0 && wss_port < 0xffff) {
> +        spice_wss_port = wss_port;
> +    }
> +    return 0;
> +#else
> +    fprintf(stderr, "libwebsockets is unsupported in this spice
> build\n");
> +    return -1;
> +#endif
> +}
> +
>  SPICE_GNUC_VISIBLE void spice_server_set_addr(SpiceServer *s, const
>  char *addr, int flags)
>  {
>      spice_assert(reds == s);
> diff --git a/server/reds.h b/server/reds.h
> index f8e8d56..0d4f933 100644
> --- a/server/reds.h
> +++ b/server/reds.h
> @@ -65,6 +65,20 @@ typedef struct RedsSASL {
>  } RedsSASL;
>  #endif
>  
> +#ifdef USE_LIBWEBSOCKETS
> +typedef struct RedsWebSocket {
> +    struct libwebsocket_context *context;
> +    struct libwebsocket *wsi;
> +    SpiceWatch *watch;
> +    int fd;
> +    unsigned events;
> +    /* buffer of available data to read, always starts at offset 0
> to data_avail - 1. */
> +    unsigned char *data;
> +    unsigned data_len;
> +    unsigned data_avail;
> +} RedsWebSocket;
> +#endif
> +
>  struct RedsStream {
>      int socket;
>      SpiceWatch *watch;
> @@ -73,6 +87,9 @@ struct RedsStream {
>         receive may return data afterward. check the flag before
>         calling receive*/
>      int shutdown;
>      SSL *ssl;
> +#ifdef USE_LIBWEBSOCKETS
> +    RedsWebSocket *ws;
> +#endif
>  
>  #if HAVE_SASL
>      RedsSASL sasl;
> diff --git a/server/reds_websockets.c b/server/reds_websockets.c
> new file mode 100644
> index 0000000..24df2e5
> --- /dev/null
> +++ b/server/reds_websockets.c
> @@ -0,0 +1,311 @@
> +#include "config.h"
> +
> +#ifdef USE_LIBWEBSOCKETS
> +#include <unistd.h>
> +#include <errno.h>
> +#include <libwebsockets.h>
> +
> +#include "spice.h"
> +#include "reds.h"
> +#include "reds-private.h"
> +#include "reds_websockets.h"
> +
> +static ssize_t stream_write_ws_cb(RedsStream *s, const void *buf,
> size_t size)
> +{
> +    /* TODO: better way to handle the requirement of libwebsocket,
> perhaps
> +     * we should make a writev version for libwebsocket. Assuming
> writev doesn't
> +     * cause a linearlizing copy itself. */
> +    ssize_t ret;
> +    unsigned char *padded_buf = spice_malloc(size +
> LWS_SEND_BUFFER_PRE_PADDING +
> +
>                                          LWS_SEND_BUFFER_POST_PADDING);
> +    spice_assert(s && s->ws);
> +    memcpy(padded_buf + LWS_SEND_BUFFER_PRE_PADDING, buf, size);
> +    ret = libwebsocket_write(s->ws->wsi,
> &padded_buf[LWS_SEND_BUFFER_PRE_PADDING], size,
> +                             LWS_WRITE_BINARY);
> +    free(padded_buf);
> +    return ret == 0 ? size : -1; /* XXX exact bytes required? if not
> this is
> +                                    good enough, else need to change
> +                                    libwebsocket */
> +}
> +
> +static void reds_websocket_append_data(RedsWebSocket *ws, unsigned
> char *buf,
> +                                       size_t size)
> +{
> +    if (!ws->data) {
> +        ws->data = spice_malloc(size);
> +        ws->data_len = size;
> +        ws->data_avail = 0;
> +    }
> +    if (ws->data_len < size + ws->data_avail) {
> +        ws->data_len = size + ws->data_avail;
> +        ws->data = realloc(ws->data, ws->data_len);
> +    }
> +    memcpy(ws->data + ws->data_avail, buf, size);
> +    ws->data_avail += size;
> +}
> +
> +static ssize_t reds_websocket_read_data(RedsWebSocket *ws, unsigned
> char *buf,
> +                                        size_t size)
> +{
> +    ssize_t ret;
> +
> +    ret = ws->data_avail > size ? size : ws->data_avail;
> +    if (ret > 0) {
> +        memcpy(buf, ws->data, ret);
> +    }
> +    if (ret > 0 && ret < ws->data_avail) {
> +        memmove(ws->data, ws->data + ret, ws->data_avail - ret);
> +    }
> +    ws->data_avail -= ret;
> +    if (ws->data_avail == 0 && ret == size) {
> +        ws->data = NULL;
> +        ws->data_len = ws->data_avail = 0;
> +    }
> +    return ret;
> +}
> +
> +static int reds_libwebsocket_service_fd(RedsState *s, struct pollfd
> *pfd)
> +{
> +    int ret;
> +    if (s->ws_in_service_fd) {
> +        return 0;
> +    }
> +    s->ws_in_service_fd = 1;
> +    ret = libwebsocket_service_fd(s->ws_context, pfd);
> +    s->ws_in_service_fd = 0;
> +    if (ret != 0) {
> +        if (errno == EAGAIN) {
> +            spice_debug("libwebsocket_servide_fd EAGAIN,
> pfd->revents = %d",
> +                        pfd->revents);
> +            return 0;
> +        }
> +        /* since read is the last systemcall, errno should be set
> correctly */
> +        spice_debug("libwebsocket_service_fd errored; (%d) %s",
> +                    errno, sys_errlist[errno]);
> +        return -1;
> +    }
> +    return 0;
> +}
> +
> +static ssize_t stream_read_ws_cb(RedsStream *s, void *buf, size_t
> size)
> +{
> +    RedsWebSocket *ws;
> +    struct pollfd pfd;
> +    RedsState *reds_state;
> +
> +    /* TODO: perhaps change libwebsocket to allow a socket like
> read. Then
> +     * we can avoid the whole RedsWebSocket->data{,_len,_avail}. */
> +    spice_assert(s && s->ws);
> +    ws = s->ws;
> +    reds_state = libwebsocket_context_user(ws->context);
> +    if (size == 0) {
> +        return 0;
> +    }
> +    spice_debug("%p %d / %d", ws->data, ws->data_avail,
> ws->data_len);
> +    if (ws->data_avail < size && !reds_state->ws_in_service_fd) {
> +        pfd.fd = ws->fd;
> +        pfd.events = ws->events;
> +        pfd.revents = POLLIN;
> +        if (reds_libwebsocket_service_fd(reds_state, &pfd)) {
> +            return -1;
> +        }
> +    }
> +    if (ws->data_avail == 0) {
> +        errno = EAGAIN; /* force a reset of the watch on the fd, so
> that
> +                           libwebsocket_service_fd has a chance to
> run */
> +        return -1;
> +    }
> +    return reds_websocket_read_data(ws, buf, size);
> +}
> +
> +static RedsWebSocket *reds_ws_from_fd(RedsState *s, int fd)
> +{
> +    int i;
> +
> +    for (i = 0 ; i < s->ws_count ; ++i) {
> +        if (s->ws[i].fd == fd) {
> +            return &s->ws[i];
> +        }
> +    }
> +    spice_error("%s: no match for %d (%d ws sockets)\n", __func__,
> +                fd, s->ws_count);
> +    return NULL;
> +}
> +
> +static int callback_http(struct libwebsocket_context *context,
> +                         struct libwebsocket *wsi,
> +                         void *user, void *in, size_t len)
> +{
> +    const char *message = "TODO: serve spice-html5";
> +    char buf[512];
> +    int n;
> +
> +    n = snprintf(buf, sizeof(buf),
> +                "HTTP/1.0 200 OK\x0d\x0a"
> +                "Server: spice\x0d\x0a"
> +                "Content-Type: text/html\x0d\x0a"
> +                "Content-Length: %zu\x0d\x0a"
> +                "\x0d\x0a"
> +                "%s", strlen(message), message);
> +    libwebsocket_write(wsi, (unsigned char *)buf, n,
> LWS_WRITE_HTTP);
> +    return 0;
> +}
> +
> +static int spice_server_add_ws_client(SpiceServer *s, int socket,
> int skip_auth,
> +                                      RedsWebSocket *ws)
> +{
> +    RedLinkInfo *link;
> +    RedsStream *stream;
> +
> +    link = spice_server_add_client_create_link(s, socket,
> skip_auth);
> +    if (!link) {
> +        return -1;
> +    }
> +    stream = link->stream;
> +    stream->read = stream_read_ws_cb;
> +    stream->write = stream_write_ws_cb;
> +    stream->writev = NULL; /* falls back to write iteration */
> +    stream->ws = ws;
> +    reds_handle_new_link(link);
> +    return 0;
> +}
> +
> +static void watch_ws(int fd, int event, void *data)
> +{
> +    struct libwebsocket_context *context = data;
> +    RedsState *s = libwebsocket_context_user(context);
> +    struct pollfd pfd = {
> +        .fd = fd,
> +        .events = reds_ws_from_fd(s, fd)->events,
> +        .revents = (event & SPICE_WATCH_EVENT_READ ? POLLIN : 0) |
> +                   (event & SPICE_WATCH_EVENT_WRITE ? POLLOUT : 0)
> +    };
> +
> +    reds_libwebsocket_service_fd(s, &pfd);
> +}
> +
> +static int callback_ws(struct libwebsocket_context *context,
> +                       struct libwebsocket *wsi,
> +                       enum libwebsocket_callback_reasons reason,
> void *user,
> +                       void *in, size_t len)
> +{
> +    int fd;
> +    RedsState *s = libwebsocket_context_user(context);
> +    int n;
> +    RedsWebSocket *ws;
> +    int events;
> +
> +    spice_debug("%s: reason %d user %lu len %zd \n", __func__,
> reason,
> +                (unsigned long)user, len);
> +    switch (reason) {
> +    case LWS_CALLBACK_HTTP:
> +        return callback_http(context, wsi, user, in, len);
> +
> +    case LWS_CALLBACK_ADD_POLL_FD:
> +        if (s->ws_count >= REDS_MAX_WEBSOCKETS) {
> +            spice_warning("exceeded websockets maximum watches");
> +            return 1; /* close connection */
> +        }
> +        spice_debug("adding ws for fd %d", (int)(long)user);
> +        events = (int)(long)len;
> +        ws = &s->ws[s->ws_count];
> +        ws->watch = core->watch_add((int)(long)user,
> +                                (events & POLLIN ?
> SPICE_WATCH_EVENT_READ: 0) |
> +                                (events & POLLOUT ?
> SPICE_WATCH_EVENT_WRITE : 0),
> +                                watch_ws, (void *)context);
> +        ws->fd = (int)(long)user;
> +        ws->events = events;
> +        s->ws_count++;
> +        break;
> +
> +    case LWS_CALLBACK_DEL_POLL_FD:
> +        spice_debug("removing ws for fd %d", (int)(long)user);
> +        for (n = 0; n < s->ws_count; n++) {
> +            if (s->ws[n].fd == (int)(long)user) {
> +                s->ws[n] = s->ws[s->ws_count - 1];
> +            }
> +            s->ws_count--;
> +        }
> +        break;
> +
> +    case LWS_CALLBACK_SET_MODE_POLL_FD:
> +        reds_ws_from_fd(s, (int)(long)user)->events |=
> (int)(long)len;
> +        break;
> +
> +    case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
> +        reds_ws_from_fd(s, (int)(long)user)->events &=
> (int)(long)len;
> +        break;
> +
> +    case LWS_CALLBACK_ESTABLISHED:
> +        fd = libwebsocket_get_socket_fd(wsi);
> +        ws = reds_ws_from_fd(s, fd);
> +        *(RedsWebSocket **)user = ws;
> +        ws->wsi = wsi;
> +        ws->context = context;
> +        ws->data_avail = 0;
> +        spice_debug("LWS_CALLBACK_ESTABLISHED\n");
> +        spice_server_add_ws_client(s, fd, 0, ws);
> +        break;
> +
> +    case LWS_CALLBACK_RECEIVE:
> +        spice_debug("LWS_CALLBACK_CLIENT_RECEIVE\n");
> +        spice_assert(user != NULL);
> +        ws = *(RedsWebSocket **)user;
> +        spice_assert(ws != NULL);
> +        reds_websocket_append_data(ws, in, len);
> +        break;
> +
> +    case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
> +    case LWS_CALLBACK_CLIENT_ESTABLISHED:
> +    case LWS_CALLBACK_CLOSED:
> +    case LWS_CALLBACK_CLIENT_RECEIVE:
> +    case LWS_CALLBACK_CLIENT_RECEIVE_PONG:
> +    case LWS_CALLBACK_CLIENT_WRITEABLE:
> +    case LWS_CALLBACK_SERVER_WRITEABLE:
> +    case LWS_CALLBACK_BROADCAST:
> +    case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
> +    case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
> +    case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
> +    case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS:
> +    case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
> +    case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
> +    case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY:
> +    case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
> +        break;
> +    }
> +    return 0;
> +}
> +
> +static struct libwebsocket_protocols ws_protocols[] = {
> +	/* first protocol must always be HTTP handler */
> +
> +	{
> +		"binary",		/* name  - based on spice-html5 :) */
> +		callback_ws,	/* callback */
> +		sizeof(void*),	/* per_session_data_size */
> +        /* below initializing library used values to avoid warning
> */
> +        NULL,
> +        0,
> +        0,
> +        0
> +	},
> +	{
> +		NULL, NULL, 0, NULL, 0, 0, 0		/* End of list */
> +	}
> +};
> +
> +void reds_init_websocket(RedsState *s, const char *addr,
> +                         int ws_port, int wss_port)
> +{
> +    if (ws_port != -1) {
> +        s->ws_context = libwebsocket_create_context(ws_port,
> +                strlen(addr) ? addr : NULL,
> +                ws_protocols, libwebsocket_internal_extensions,
> +                NULL /*cert_path*/, NULL /*key_path*/, -1, -1, 0
> /*opts*/,
> +                s);
> +    }
> +    if (wss_port != -1) {
> +        spice_error("TODO: secure websocket not supported");
> +    }
> +}
> +#endif
> diff --git a/server/reds_websockets.h b/server/reds_websockets.h
> new file mode 100644
> index 0000000..ce0a975
> --- /dev/null
> +++ b/server/reds_websockets.h
> @@ -0,0 +1,9 @@
> +#ifndef REDS_WEBSOCKETS_H
> +#define REDS_WEBSOCKETS_H
> +
> +#include "reds-private.h"
> +
> +void reds_init_websocket(RedsState *s, const char *addr,
> +                         int ws_port, int wss_port);
> +
> +#endif
> diff --git a/server/spice-server.syms b/server/spice-server.syms
> index eadfed8..aef8f5a 100644
> --- a/server/spice-server.syms
> +++ b/server/spice-server.syms
> @@ -130,3 +130,8 @@ SPICE_SERVER_0.11.4 {
>  global:
>      spice_server_set_exit_on_disconnect;
>  } SPICE_SERVER_0.11.2;
> +
> +SPICE_SERVER_0.12.1 {
> +global:
> +    spice_server_set_ws_ports;
> +} SPICE_SERVER_0.11.4;
> diff --git a/server/spice.h b/server/spice.h
> index c1478e0..7ca5033 100644
> --- a/server/spice.h
> +++ b/server/spice.h
> @@ -434,6 +434,13 @@ void spice_server_destroy(SpiceServer *s);
>  int spice_server_set_compat_version(SpiceServer *s,
>                                      spice_compat_version_t version);
>  int spice_server_set_port(SpiceServer *s, int port);
> +
> +/* @ws_port: -1 for don't set, otherwise in [1,65535] port to listen
> + *           for unencrypted websocket connections.
> + * @wss_port: -1 for don't set, otherwise in [1,65535] port to
> listen
> + *           for encrypted websocket connections.
> + */
> +int spice_server_set_ws_ports(SpiceServer *s, int ws_port, int
> wss_port);
>  void spice_server_set_addr(SpiceServer *s, const char *addr, int
>  flags);
>  int spice_server_set_listen_socket_fd(SpiceServer *s, int
>  listen_fd);
>  int spice_server_set_exit_on_disconnect(SpiceServer *s, int flag);
> diff --git a/server/tests/test_display_base.c
> b/server/tests/test_display_base.c
> index 588e960..9eaa584 100644
> --- a/server/tests/test_display_base.c
> +++ b/server/tests/test_display_base.c
> @@ -701,13 +701,14 @@ static int flush_resources(QXLInstance *qin)
>      return TRUE;
>  }
>  
> -static void client_monitors_config(QXLInstance *qin,
> VDAgentMonitorsConfig *monitors_config)
> +static int client_monitors_config(QXLInstance *qin,
> VDAgentMonitorsConfig *monitors_config)
>  {
>      if (!monitors_config) {
>          printf("%s: NULL monitors_config\n", __func__);
>      } else {
>          printf("%s: %d\n", __func__,
>          monitors_config->num_of_monitors);
>      }
> +    return 0;
>  }
>  
>  QXLInterface display_sif = {
> @@ -815,6 +816,7 @@ Test *test_new(SpiceCoreInterface *core)
>      printf("TESTER: listening on port %d (unsecure)\n", port);
>      spice_server_set_port(server, port);
>      spice_server_set_noauth(server);
> +    spice_server_set_ws_ports(test->server, 5959, -1);
>      spice_server_init(server, core);
>  
>      cursor_init();
> --
> 1.7.12.1
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
> 


More information about the Spice-devel mailing list