Some notes I forgot / updates. > New API: spice_server_set_ws_ports > > This adds an optional dependency on libwebsockets. You need to get my > patched 0.0.3 version here: > git://people.freedesktop.org/~alon/libwebsockets These patches are already upstream (git://git.warmcat.com/libwebsockets). The library is not new, the version number probably reflects expectations of unstable API, but since we don't build with it by default I don't think it's a problem. > > 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). qemu patch exists, like I wrote in the cover letter. > > 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@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/spice-devel > _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel