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 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@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel