Index: work/remote/example_srv/test-server.c =================================================================== --- work/remote/example_srv/test-server.c (revision 7275) +++ work/remote/example_srv/test-server.c (nonexistent) @@ -1,702 +0,0 @@ -/* - * libwebsockets-test-server - libwebsockets test implementation - * - * Copyright (C) 2010-2011 Andy Green - * - * 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: - * version 2.1 of the License. - * - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../lib/libwebsockets.h" - -static int close_testing; - -#ifdef EXTERNAL_POLL -#ifndef LWS_NO_FORK -#define LWS_NO_FORK -#endif - -int max_poll_elements; - -struct pollfd *pollfds; -int *fd_lookup; -int count_pollfds; - -#endif /* EXTERNAL_POLL */ - - -/* - * This demo server shows how to use libwebsockets for one or more - * websocket protocols in the same server - * - * It defines the following websocket protocols: - * - * dumb-increment-protocol: once the socket is opened, an incrementing - * ascii string is sent down it every 50ms. - * If you send "reset\n" on the websocket, then - * the incrementing number is reset to 0. - * - * lws-mirror-protocol: copies any received packet to every connection also - * using this protocol, including the sender - */ - -enum demo_protocols { - /* always first */ - PROTOCOL_HTTP = 0, - - PROTOCOL_DUMB_INCREMENT, - PROTOCOL_LWS_MIRROR, - - /* always last */ - DEMO_PROTOCOL_COUNT -}; - - -#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server" - -/* - * We take a strict whitelist approach to stop ../ attacks - */ - -struct serveable { - const char *urlpath; - const char *mimetype; -}; - -static const struct serveable whitelist[] = { - {"/favicon.ico", "image/x-icon"}, - {"/libwebsockets.org-logo.png", "image/png"}, - - /* last one is the default served if no match */ - {"/test.html", "text/html"}, -}; - -/* this protocol server (always the first one) just knows how to do HTTP */ - -static int callback_http(struct libwebsocket_context *context, - struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len) -{ -#if 0 - char client_name[128]; - char client_ip[128]; -#endif - char buf[256]; - int n; -#ifdef EXTERNAL_POLL - int m; - int fd = (int) (long) user; -#endif - - switch (reason) { - case LWS_CALLBACK_HTTP: - - for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++) - if (in && strcmp(in, whitelist[n].urlpath) == 0) - break; - - sprintf(buf, LOCAL_RESOURCE_PATH "%s", whitelist[n].urlpath); - - if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype)) - lwsl_err("Failed to send HTTP file\n"); - - /* - * notice that the sending of the file completes asynchronously, - * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when - * it's done - */ - - break; - - case LWS_CALLBACK_HTTP_FILE_COMPLETION: -// lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n"); - /* kill the connection after we sent one file */ - return 1; - - /* - * callback for confirming to continue with client IP appear in - * protocol 0 callback since no websocket protocol has been agreed - * yet. You can just ignore this if you won't filter on client IP - * since the default uhandled callback return is 0 meaning let the - * connection continue. - */ - - case LWS_CALLBACK_FILTER_NETWORK_CONNECTION: -#if 0 - libwebsockets_get_peer_addresses((int) (long) user, client_name, sizeof(client_name), client_ip, sizeof(client_ip)); - - fprintf(stderr, "Received network connect from %s (%s)\n", client_name, client_ip); -#endif - /* if we returned non-zero from here, we kill the connection */ - break; - -#ifdef EXTERNAL_POLL - /* - * callbacks for managing the external poll() array appear in - * protocol 0 callback - */ - - case LWS_CALLBACK_ADD_POLL_FD: - - if (count_pollfds >= max_poll_elements) { - lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n"); - return 1; - } - - fd_lookup[fd] = count_pollfds; - pollfds[count_pollfds].fd = fd; - pollfds[count_pollfds].events = (int) (long) len; - pollfds[count_pollfds++].revents = 0; - break; - - case LWS_CALLBACK_DEL_POLL_FD: - if (!--count_pollfds) - break; - m = fd_lookup[fd]; - /* have the last guy take up the vacant slot */ - pollfds[m] = pollfds[count_pollfds]; - fd_lookup[pollfds[count_pollfds].fd] = m; - break; - - case LWS_CALLBACK_SET_MODE_POLL_FD: - pollfds[fd_lookup[fd]].events |= (int) (long) len; - break; - - case LWS_CALLBACK_CLEAR_MODE_POLL_FD: - pollfds[fd_lookup[fd]].events &= ~(int) (long) len; - break; -#endif - - default: - break; - } - - return 0; -} - -/* - * this is just an example of parsing handshake headers, you don't need this - * in your code unless you will filter allowing connections by the header - * content - */ - -static void dump_handshake_info(struct lws_tokens *lwst) -{ - int n; - static const char *token_names[WSI_TOKEN_COUNT] = { - /*[WSI_TOKEN_GET_URI] = */ "GET URI", - /*[WSI_TOKEN_HOST] = */ "Host", - /*[WSI_TOKEN_CONNECTION] = */ "Connection", - /*[WSI_TOKEN_KEY1] = */ "key 1", - /*[WSI_TOKEN_KEY2] = */ "key 2", - /*[WSI_TOKEN_PROTOCOL] = */ "Protocol", - /*[WSI_TOKEN_UPGRADE] = */ "Upgrade", - /*[WSI_TOKEN_ORIGIN] = */ "Origin", - /*[WSI_TOKEN_DRAFT] = */ "Draft", - /*[WSI_TOKEN_CHALLENGE] = */ "Challenge", - - /* new for 04 */ - /*[WSI_TOKEN_KEY] = */ "Key", - /*[WSI_TOKEN_VERSION] = */ "Version", - /*[WSI_TOKEN_SWORIGIN] = */ "Sworigin", - - /* new for 05 */ - /*[WSI_TOKEN_EXTENSIONS] = */ "Extensions", - - /* client receives these */ - /*[WSI_TOKEN_ACCEPT] = */ "Accept", - /*[WSI_TOKEN_NONCE] = */ "Nonce", - /*[WSI_TOKEN_HTTP] = */ "Http", - /*[WSI_TOKEN_MUXURL] = */ "MuxURL", - }; - - for (n = 0; n < WSI_TOKEN_COUNT; n++) { - if (lwst[n].token == NULL) - continue; - - fprintf(stderr, " %s = %s\n", token_names[n], lwst[n].token); - } -} - -/* dumb_increment protocol */ - -/* - * one of these is auto-created for each connection and a pointer to the - * appropriate instance is passed to the callback in the user parameter - * - * for this example protocol we use it to individualize the count for each - * connection. - */ - -struct per_session_data__dumb_increment { - int number; -}; - -static int -callback_dumb_increment(struct libwebsocket_context *context, - struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len) -{ - int n; - unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 + LWS_SEND_BUFFER_POST_PADDING]; - unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING]; - struct per_session_data__dumb_increment *pss = user; - - switch (reason) { - - case LWS_CALLBACK_ESTABLISHED: - lwsl_info("callback_dumb_increment: " "LWS_CALLBACK_ESTABLISHED\n"); - pss->number = 0; - break; - - /* - * in this protocol, we just use the broadcast action as the chance to - * send our own connection-specific data and ignore the broadcast info - * that is available in the 'in' parameter - */ - - case LWS_CALLBACK_BROADCAST: - n = sprintf((char *) p, "%d", pss->number++); - n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT); - if (n < 0) { - lwsl_err("ERROR %d writing to socket\n", n); - return 1; - } - if (close_testing && pss->number == 50) { - lwsl_info("close tesing limit, closing\n"); - libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NORMAL); - } - break; - - case LWS_CALLBACK_RECEIVE: -// fprintf(stderr, "rx %d\n", (int)len); - if (len < 6) - break; - if (strcmp(in, "reset\n") == 0) - pss->number = 0; - break; - /* - * this just demonstrates how to use the protocol filter. If you won't - * study and reject connections based on header content, you don't need - * to handle this callback - */ - - case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: - dump_handshake_info((struct lws_tokens *) (long) user); - /* you could return non-zero here and kill the connection */ - break; - - default: - break; - } - - return 0; -} - - -/* lws-mirror_protocol */ - -#define MAX_MESSAGE_QUEUE 128 - -struct per_session_data__lws_mirror { - struct libwebsocket *wsi; - int ringbuffer_tail; -}; - -struct a_message { - void *payload; - size_t len; -}; - -static struct a_message ringbuffer[MAX_MESSAGE_QUEUE]; -static int ringbuffer_head; - -static struct libwebsocket *wsi_choked[20]; -static int num_wsi_choked; - -static int -callback_lws_mirror(struct libwebsocket_context *context, - struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len) -{ - int n; - struct per_session_data__lws_mirror *pss = user; - - switch (reason) { - - case LWS_CALLBACK_ESTABLISHED: - lwsl_info("callback_lws_mirror: " "LWS_CALLBACK_ESTABLISHED\n"); - pss->ringbuffer_tail = ringbuffer_head; - pss->wsi = wsi; - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - if (close_testing) - break; - while (pss->ringbuffer_tail != ringbuffer_head) { - - n = libwebsocket_write(wsi, (unsigned char *) - ringbuffer[pss->ringbuffer_tail].payload + - LWS_SEND_BUFFER_PRE_PADDING, ringbuffer[pss->ringbuffer_tail].len, LWS_WRITE_TEXT); - if (n < 0) { - lwsl_err("ERROR %d writing to socket\n", n); - return 1; - } - - if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1)) - pss->ringbuffer_tail = 0; - else - pss->ringbuffer_tail++; - - if (((ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15)) { - for (n = 0; n < num_wsi_choked; n++) - libwebsocket_rx_flow_control(wsi_choked[n], 1); - num_wsi_choked = 0; - } - // lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)); - - if (lws_send_pipe_choked(wsi)) { - libwebsocket_callback_on_writable(context, wsi); - return 0; - } - } - break; - - case LWS_CALLBACK_BROADCAST: - n = libwebsocket_write(wsi, in, len, LWS_WRITE_TEXT); - if (n < 0) - lwsl_err("mirror write failed\n"); - break; - - case LWS_CALLBACK_RECEIVE: - - if (((ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) { - lwsl_err("dropping!\n"); - goto choke; - } - - if (ringbuffer[ringbuffer_head].payload) - free(ringbuffer[ringbuffer_head].payload); - - ringbuffer[ringbuffer_head].payload = malloc(LWS_SEND_BUFFER_PRE_PADDING + len + LWS_SEND_BUFFER_POST_PADDING); - ringbuffer[ringbuffer_head].len = len; - memcpy((char *) ringbuffer[ringbuffer_head].payload + LWS_SEND_BUFFER_PRE_PADDING, in, len); - if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1)) - ringbuffer_head = 0; - else - ringbuffer_head++; - - if (((ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2)) - goto done; - - choke: - if (num_wsi_choked < sizeof wsi_choked / sizeof wsi_choked[0]) { - libwebsocket_rx_flow_control(wsi, 0); - wsi_choked[num_wsi_choked++] = wsi; - } - -// lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)); - done: - libwebsocket_callback_on_writable_all_protocol(libwebsockets_get_protocol(wsi)); - break; - - /* - * this just demonstrates how to use the protocol filter. If you won't - * study and reject connections based on header content, you don't need - * to handle this callback - */ - - case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: - dump_handshake_info((struct lws_tokens *) (long) user); - /* you could return non-zero here and kill the connection */ - break; - - default: - break; - } - - return 0; -} - - -/* list of supported protocols and callbacks */ - -static struct libwebsocket_protocols protocols[] = { - /* first protocol must always be HTTP handler */ - - { - "http-only", /* name */ - callback_http, /* callback */ - 0 /* per_session_data_size */ - }, - { - "dumb-increment-protocol", - callback_dumb_increment, - sizeof(struct per_session_data__dumb_increment), - }, - { - "lws-mirror-protocol", - callback_lws_mirror, - sizeof(struct per_session_data__lws_mirror) - }, - { - NULL, NULL, 0 /* End of list */ - } -}; - -static struct option options[] = { - {"help", no_argument, NULL, 'h'}, - {"debug", required_argument, NULL, 'd'}, - {"port", required_argument, NULL, 'p'}, - {"ssl", no_argument, NULL, 's'}, - {"interface", required_argument, NULL, 'i'}, - {"closetest", no_argument, NULL, 'c'}, -#ifndef LWS_NO_DAEMONIZE - {"daemonize", no_argument, NULL, 'D'}, -#endif - {NULL, 0, 0, 0} -}; - -int main(int argc, char **argv) -{ - int n = 0; - const char *cert_path = LOCAL_RESOURCE_PATH "/libwebsockets-test-server.pem"; - const char *key_path = LOCAL_RESOURCE_PATH "/libwebsockets-test-server.key.pem"; - unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1024 + LWS_SEND_BUFFER_POST_PADDING]; - int port = 7681; - int use_ssl = 0; - struct libwebsocket_context *context; - int opts = 0; - char interface_name[128] = ""; - const char *interface = NULL; - int syslog_options = LOG_PID | LOG_PERROR; -#ifdef LWS_NO_FORK - unsigned int oldus = 0; -#endif - int debug_level = 7; -#ifndef LWS_NO_DAEMONIZE - int daemonize = 0; -#endif - - while (n >= 0) { - n = getopt_long(argc, argv, "ci:hsp:d:D", options, NULL); - if (n < 0) - continue; - switch (n) { -#ifndef LWS_NO_DAEMONIZE - case 'D': - daemonize = 1; - syslog_options &= ~LOG_PERROR; - break; -#endif - case 'd': - debug_level = atoi(optarg); - break; - case 's': - use_ssl = 1; - break; - case 'p': - port = atoi(optarg); - break; - case 'i': - strncpy(interface_name, optarg, sizeof interface_name); - interface_name[(sizeof interface_name) - 1] = '\0'; - interface = interface_name; - break; - case 'c': - close_testing = 1; - fprintf(stderr, " Close testing mode -- closes on " "client after 50 dumb increments" "and suppresses lws_mirror spam\n"); - break; - case 'h': - fprintf(stderr, "Usage: test-server " "[--port=

] [--ssl] " "[-d ]\n"); - exit(1); - } - } - -#ifndef LWS_NO_DAEMONIZE - /* - * normally lock path would be /var/lock/lwsts or similar, to - * simplify getting started without having to take care about - * permissions or running as root, set to /tmp/.lwsts-lock - */ - if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) { - fprintf(stderr, "Failed to daemonize\n"); - return 1; - } -#endif - /* we will only try to log things according to our debug_level */ - setlogmask(LOG_UPTO(LOG_DEBUG)); - openlog("lwsts", syslog_options, LOG_DAEMON); - - /* tell the library what debug level to emit and to send it to syslog */ - lws_set_log_level(debug_level, lwsl_emit_syslog); - - lwsl_notice("libwebsockets test server - " - "(C) Copyright 2010-2013 Andy Green - " "licensed under LGPL2.1\n"); - if (!use_ssl) - cert_path = key_path = NULL; -#ifdef EXTERNAL_POLL - max_poll_elements = getdtablesize(); - pollfds = malloc(max_poll_elements * sizeof(struct pollfd)); - fd_lookup = malloc(max_poll_elements * sizeof(int)); - if (pollfds == NULL || fd_lookup == NULL) { - lwsl_err("Out of memory pollfds=%d\n", max_poll_elements); - return -1; - } -#endif - - context = libwebsocket_create_context(port, interface, protocols, -#ifndef LWS_NO_EXTENSIONS - libwebsocket_internal_extensions, -#else - NULL, -#endif - cert_path, key_path, NULL, -1, -1, opts, NULL); - if (context == NULL) { - lwsl_err("libwebsocket init failed\n"); - return -1; - } - - buf[LWS_SEND_BUFFER_PRE_PADDING] = 'x'; - -#ifdef LWS_NO_FORK - - /* - * This example shows how to work with no forked service loop - */ - - lwsl_info(" Using no-fork service loop\n"); - - n = 0; - while (n >= 0) { - struct timeval tv; - - gettimeofday(&tv, NULL); - - /* - * This broadcasts to all dumb-increment-protocol connections - * at 20Hz. - * - * We're just sending a character 'x', in these examples the - * callbacks send their own per-connection content. - * - * You have to send something with nonzero length to get the - * callback actions delivered. - * - * We take care of pre-and-post padding allocation. - */ - - if (((unsigned int) tv.tv_usec - oldus) > 50000) { - libwebsockets_broadcast(&protocols[PROTOCOL_DUMB_INCREMENT], &buf[LWS_SEND_BUFFER_PRE_PADDING], 1); - oldus = tv.tv_usec; - } - - /* - * This example server does not fork or create a thread for - * websocket service, it all runs in this single loop. So, - * we have to give the websockets an opportunity to service - * "manually". - * - * If no socket is needing service, the call below returns - * immediately and quickly. Negative return means we are - * in process of closing - */ -#ifdef EXTERNAL_POLL - - /* - * this represents an existing server's single poll action - * which also includes libwebsocket sockets - */ - - n = poll(pollfds, count_pollfds, 50); - if (n < 0) - continue; - - - if (n) - for (n = 0; n < count_pollfds; n++) - if (pollfds[n].revents) - /* - * returns immediately if the fd does not - * match anything under libwebsockets - * control - */ - if (libwebsocket_service_fd(context, &pollfds[n]) < 0) - goto done; -#else - n = libwebsocket_service(context, 50); -#endif - } - -#else /* !LWS_NO_FORK */ - - /* - * This example shows how to work with the forked websocket service loop - */ - - lwsl_info(" Using forked service loop\n"); - - /* - * This forks the websocket service action into a subprocess so we - * don't have to take care about it. - */ - - n = libwebsockets_fork_service_loop(context); - if (n < 0) { - lwsl_err("Unable to fork service loop %d\n", n); - return 1; - } - if (n) - exit(0); - - while (1) { - - usleep(50000); - - /* - * This broadcasts to all dumb-increment-protocol connections - * at 20Hz. - * - * We're just sending a character 'x', in these examples the - * callbacks send their own per-connection content. - * - * You have to send something with nonzero length to get the - * callback actions delivered. - * - * We take care of pre-and-post padding allocation. - */ - - libwebsockets_broadcast(&protocols[PROTOCOL_DUMB_INCREMENT], &buf[LWS_SEND_BUFFER_PRE_PADDING], 1); - } - -#endif -#ifdef EXTERNAL_POLL -done: -#endif - - libwebsocket_context_destroy(context); - - lwsl_notice("libwebsockets-test-server exited cleanly\n"); - - closelog(); - - return 0; -}