diff options
Diffstat (limited to 'server/reds_websockets.c')
-rw-r--r-- | server/reds_websockets.c | 310 |
1 files changed, 0 insertions, 310 deletions
diff --git a/server/reds_websockets.c b/server/reds_websockets.c deleted file mode 100644 index afffacec..00000000 --- a/server/reds_websockets.c +++ /dev/null @@ -1,310 +0,0 @@ -#include "config.h" - -#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 = spice_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) { - free(ws->data); - 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"); - } -} |