introduce-ssl-client-connections--flow-control.patch
authorAndy Green <andy@warmcat.com>
Thu, 27 Jan 2011 06:26:52 +0000 (06:26 +0000)
committerAndy Green <andy@warmcat.com>
Thu, 27 Jan 2011 06:26:52 +0000 (06:26 +0000)
Signed-off-by: Andy Green <andy@warmcat.com>
20 files changed:
Makefile.am
Makefile.in
README-test-server
configure
configure.ac
lib/Makefile.am
lib/Makefile.in
lib/client-handshake.c
lib/handshake.c
lib/libwebsockets.c
lib/libwebsockets.h
lib/parsers.c
lib/private-libwebsockets.h
lib/sha-1.c
libwebsockets-api-doc.html
test-server/Makefile.am
test-server/Makefile.in
test-server/test-client.c
test-server/test-server.c
test-server/test.html

index bb67621..dec9704 100644 (file)
@@ -1,2 +1,2 @@
-SUBDIRS=lib test-server 
 
+SUBDIRS=lib test-server
index 02a4a84..20e9dba 100644 (file)
@@ -181,6 +181,7 @@ build_cpu = @build_cpu@
 build_os = @build_os@
 build_vendor = @build_vendor@
 builddir = @builddir@
+clientcertdir = @clientcertdir@
 datadir = @datadir@
 datarootdir = @datarootdir@
 docdir = @docdir@
@@ -214,7 +215,7 @@ target_alias = @target_alias@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-SUBDIRS = lib test-server 
+SUBDIRS = lib test-server
 all: config.h
        $(MAKE) $(AM_MAKEFLAGS) all-recursive
 
index 9a48db2..f10520e 100644 (file)
@@ -33,6 +33,10 @@ There are a couple of other possible configure options
                        to disable the built-in ones and force use
                        of the libcrypto (part of openssl) ones.
 
+--with-client-cert-dir=dir   tells the client ssl support where to
+                            look for trust certificates to validate
+                            the remote certificate against.
+
 Testing server with a browser
 -----------------------------
 
@@ -44,11 +48,8 @@ It will fetch a script in the form of test.html, and then run the
 script in there on the browser to open a websocket connection.
 Incrementing numbers should appear in the browser display.
 
-Using SSL
----------
-
-The client side operation does not support SSL yet, but the
-server side does.
+Using SSL on the server side
+----------------------------
 
 To test it using SSL/WSS, just run the test server with
 
@@ -68,6 +69,7 @@ same.
 test-server.c is all that is needed to use libwebsockets for
 serving both the script html over http and websockets.
 
+
 Forkless operation
 ------------------
 
@@ -76,6 +78,7 @@ libwebsockets from your own main loop instead.  Use the
 configure option --nofork and simply call libwebsocket_service()
 from your own main loop as shown in the test app sources.
 
+
 Testing websocket client support
 --------------------------------
 
@@ -91,6 +94,19 @@ if you connect to the test server using a browser at the
 same time you will be able to see the circles being drawn.
 
 
+Testing SSL on the client side
+------------------------------
+
+To test SSL/WSS client action, just run the client test with
+
+$ libwebsockets-test-client localhost --ssl
+
+By default the client test applet is set to accept selfsigned
+certificates used by the test server, this is indicated by the
+use_ssl var being set to 2.  Set it to 1 to reject any server
+certificate that it doesn't have a trusted CA cert for.
+
+
 Websocket version supported
 ---------------------------
 
index 3e0ce28..4ff42ac 100755 (executable)
--- a/configure
+++ b/configure
@@ -616,6 +616,7 @@ ac_subst_vars='am__EXEEXT_FALSE
 am__EXEEXT_TRUE
 LTLIBOBJS
 LIBOBJS
+clientcertdir
 LIBCRYPTO_FALSE
 LIBCRYPTO_TRUE
 CPP
@@ -739,6 +740,7 @@ enable_libtool_lock
 enable_openssl
 enable_nofork
 enable_libcrypto
+with_client_cert_dir
 '
       ac_precious_vars='build_alias
 host_alias
@@ -1383,7 +1385,7 @@ Optional Features:
   --disable-libtool-lock  avoid locking (might break parallel builds)
   --enable-openssl  Enables https support and needs openssl libs
   --enable-nofork  Disables fork-related options
-  --enable-libcrypto  Use libcrypto MD5 and SHA1 implemntations
+  --enable-libcrypto  Use libcrypto MD5 and SHA1 implementations
 
 Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
@@ -1393,6 +1395,8 @@ Optional Packages:
   --with-gnu-ld           assume the C compiler uses GNU ld [default=no]
   --with-sysroot=DIR Search for dependent libraries within DIR
                         (or the compiler's sysroot if not specified).
+  --with-client-cert-dir  directory containing client certs, defaults to
+                          /etc/pki/tls/certs/
 
 Some influential environment variables:
   CC          C compiler command
@@ -11963,6 +11967,7 @@ fi
 
 
 
+
 #
 #
 #
 
 
 
+#
+#
+#
+
+# Check whether --with-client-cert-dir was given.
+if test "${with_client_cert_dir+set}" = set; then :
+  withval=$with_client_cert_dir; clientcertdir=$withval
+else
+  clientcertdir=/etc/pki/tls/certs/
+
+fi
+
+
+
+
+
+
 
 # Checks for header files.
 for ac_header in fcntl.h netinet/in.h stdlib.h string.h sys/socket.h unistd.h
index 019b3f9..00b69f4 100644 (file)
@@ -17,6 +17,7 @@ AC_PROG_INSTALL
 AC_PROG_MAKE_SET
 AC_CONFIG_MACRO_DIR([m4])
 
+
 #
 #
 #
@@ -48,7 +49,7 @@ fi
 #
 #
 AC_ARG_ENABLE(libcrypto,
- [  --enable-libcrypto  Use libcrypto MD5 and SHA1 implemntations],
+ [  --enable-libcrypto  Use libcrypto MD5 and SHA1 implementations],
  [ libcrypto=yes
  ])
 
@@ -59,6 +60,19 @@ fi
 AM_CONDITIONAL(LIBCRYPTO, test x$libcrypto = xyes)
 
 
+#
+#
+#
+AC_ARG_WITH([client-cert-dir],
+[AS_HELP_STRING([--with-client-cert-dir],[directory containing client certs, defaults to /etc/pki/tls/certs/])],
+[clientcertdir=$withval],
+[clientcertdir=/etc/pki/tls/certs/]
+)
+AC_SUBST([clientcertdir])
+
+AC_SUBST([CFLAGS])
+
+
 
 # Checks for header files.
 AC_CHECK_HEADERS([fcntl.h netinet/in.h stdlib.h string.h sys/socket.h unistd.h])
index 2280bdc..8328c29 100644 (file)
@@ -12,7 +12,7 @@ else
 dist_libwebsockets_la_SOURCES += md5.c sha-1.c
 endif
 
-libwebsockets_la_CFLAGS=-Wall -Werror -std=gnu99 -pedantic -rdynamic -fPIC -c
+libwebsockets_la_CFLAGS:=-rdynamic -fPIC -Wall -Werror -std=gnu99 -pedantic -c -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\"
 libwebsockets_la_LDFLAGS=-version-info 0:2
 
 all-local:
index 9f58996..775baba 100644 (file)
@@ -183,6 +183,7 @@ build_cpu = @build_cpu@
 build_os = @build_os@
 build_vendor = @build_vendor@
 builddir = @builddir@
+clientcertdir = @clientcertdir@
 datadir = @datadir@
 datarootdir = @datarootdir@
 docdir = @docdir@
@@ -221,7 +222,7 @@ include_HEADERS = libwebsockets.h
 dist_libwebsockets_la_SOURCES = libwebsockets.c handshake.c parsers.c \
        libwebsockets.h base64-decode.c client-handshake.c \
        private-libwebsockets.h $(am__append_1)
-libwebsockets_la_CFLAGS = -Wall -Werror -std=gnu99 -pedantic -rdynamic -fPIC -c
+libwebsockets_la_CFLAGS := -rdynamic -fPIC -Wall -Werror -std=gnu99 -pedantic -c -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\"
 libwebsockets_la_LDFLAGS = -version-info 0:2
 all: all-am
 
index 64f72cb..e93ce99 100644 (file)
@@ -9,8 +9,10 @@
 void
 strtolower(char *s)
 {
-       while (*s)
-               *s++ = tolower(*s);
+       while (*s) {
+               *s = tolower(*s);
+               s++;
+       }
 }
 
 void
@@ -53,7 +55,7 @@ libwebsocket_client_close(struct libwebsocket *wsi)
        /* shut down reasonably cleanly */
 
 #ifdef LWS_OPENSSL_SUPPORT
-       if (use_ssl) {
+       if (wsi->ssl) {
                n = SSL_get_fd(wsi->ssl);
                SSL_shutdown(wsi->ssl);
                close(n);
@@ -67,10 +69,29 @@ libwebsocket_client_close(struct libwebsocket *wsi)
 #endif
 }
 
+
+/**
+ * libwebsocket_client_connect() - Connect to another websocket server
+ * @this:      Websocket context
+ * @address:   Remote server address, eg, "myserver.com"
+ * @port:      Port to connect to on the remote server, eg, 80
+ * @ssl_connection:    0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self
+ *                     signed certs
+ * @path:      Websocket path on server
+ * @host:      Hostname on server
+ * @origin:    Socket origin name
+ * @protocol:  Comma-separated list of protocols being asked for from
+ *             the server, or just one.  The server will pick the one it
+ *             likes best.
+ *
+ *     This function creates a connection to a remote server
+ */
+
 struct libwebsocket *
-libwebsocket_client_connect(struct libwebsocket_context *clients,
+libwebsocket_client_connect(struct libwebsocket_context *this,
                              const char *address,
                              int port,
+                             int ssl_connection,
                              const char *path,
                              const char *host,
                              const char *origin,
@@ -94,12 +115,22 @@ libwebsocket_client_connect(struct libwebsocket_context *clients,
        int okay = 0;
        struct libwebsocket *wsi;
        int n;
+#ifdef LWS_OPENSSL_SUPPORT
+       char ssl_err_buf[512];
+#else
+       if (ssl_connection) {
+               fprintf(stderr, "libwebsockets not configured for ssl\n");
+               return NULL;
+       }
+#endif
 
        wsi = malloc(sizeof(struct libwebsocket));
-       if (wsi == NULL)
+       if (wsi == NULL) {
+               fprintf(stderr, "Out of memort allocing new connection\n");
                return NULL;
+       }
 
-       clients->wsi[clients->fds_count] = wsi;
+       this->wsi[this->fds_count] = wsi;
 
        wsi->ietf_spec_revision = 4;
        wsi->name_buffer_pos = 0;
@@ -137,10 +168,36 @@ libwebsocket_client_connect(struct libwebsocket_context *clients,
 
        if (connect(wsi->sock, (struct sockaddr *)&server_addr,
                                              sizeof(struct sockaddr)) == -1)  {
-               fprintf(stderr, "Connect failed");
+               fprintf(stderr, "Connect failed\n");
                goto bail1;
        }
 
+#ifdef LWS_OPENSSL_SUPPORT
+       if (ssl_connection) {
+
+               wsi->ssl = SSL_new(this->ssl_client_ctx);
+               wsi->client_bio = BIO_new_socket(wsi->sock, BIO_NOCLOSE);
+               SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
+
+               if (SSL_connect(wsi->ssl) <= 0) {
+                       fprintf(stderr, "SSL connect error %s\n",
+                               ERR_error_string(ERR_get_error(), ssl_err_buf));
+                       goto bail2;
+               }
+
+               n = SSL_get_verify_result(wsi->ssl);
+               if (n != X509_V_OK) {
+                       if (n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT &&
+                                                           ssl_connection == 2)
+                               goto cert_okay;
+
+                   fprintf(stderr, "server's cert didn't look good %d\n", n);
+                   goto bail2;
+               }
+       } else
+               wsi->ssl = NULL;
+cert_okay:
+#endif
        /*
         * create the random key
         */
@@ -199,7 +256,17 @@ libwebsocket_client_connect(struct libwebsocket_context *clients,
 
        /* send our request to the server */
 
-       n = send(wsi->sock, pkt, p - pkt, 0);
+#ifdef LWS_OPENSSL_SUPPORT
+       if (ssl_connection)
+               n = SSL_write(wsi->ssl, pkt, p - pkt);
+       else
+#endif
+               n = send(wsi->sock, pkt, p - pkt, 0);
+
+       if (n < 0) {
+               fprintf(stderr, "ERROR writing to client socket\n");
+               goto bail2;
+       }
 
        wsi->parser_state = WSI_TOKEN_NAME_PART;
 
@@ -230,7 +297,13 @@ libwebsocket_client_connect(struct libwebsocket_context *clients,
         *  Sec-WebSocket-Protocol: chat
         */
 
-       len = recv(wsi->sock, pkt, sizeof pkt, 0);
+#ifdef LWS_OPENSSL_SUPPORT
+       if (ssl_connection)
+               len = SSL_read(wsi->ssl, pkt, sizeof pkt);
+       else
+#endif
+               len = recv(wsi->sock, pkt, sizeof pkt, 0);
+
        if (len < 0) {
                fprintf(stderr, "libwebsocket_client_handshake read error\n");
                goto bail2;
@@ -300,7 +373,7 @@ libwebsocket_client_connect(struct libwebsocket_context *clients,
        if (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len) {
 
                /* no protocol name to work from, default to first protocol */
-               wsi->protocol = &clients->protocols[0];
+               wsi->protocol = &this->protocols[0];
 
                goto check_accept;
        }
@@ -331,10 +404,10 @@ libwebsocket_client_connect(struct libwebsocket_context *clients,
         */
        n = 0;
        wsi->protocol = NULL;
-       while (clients->protocols[n].callback) {
+       while (this->protocols[n].callback) {
                if (strcmp(wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
-                                      clients->protocols[n].name) == 0)
-                       wsi->protocol = &clients->protocols[n];
+                                      this->protocols[n].name) == 0)
+                       wsi->protocol = &this->protocols[n];
                n++;
        }
 
@@ -373,15 +446,21 @@ check_accept:
 
        /* okay he is good to go */
 
-       clients->fds[clients->fds_count].fd = wsi->sock;
-       clients->fds[clients->fds_count].revents = 0;
-       clients->fds[clients->fds_count++].events = POLLIN;
+       this->fds[this->fds_count].fd = wsi->sock;
+       this->fds[this->fds_count].revents = 0;
+       this->fds[this->fds_count++].events = POLLIN;
 
        wsi->state = WSI_STATE_ESTABLISHED;
        wsi->client_mode = 1;
 
        fprintf(stderr, "handshake OK for protocol %s\n", wsi->protocol->name);
 
+       /* call him back to inform him he is up */
+
+       wsi->protocol->callback(wsi,
+                        LWS_CALLBACK_CLIENT_ESTABLISHED,
+                        wsi->user_space,
+                        NULL, 0);
        return wsi;
 
 
index d6928d7..d126809 100644 (file)
@@ -131,7 +131,7 @@ handshake_00(struct libwebsocket *wsi)
        strcpy(p, wsi->utf8_token[WSI_TOKEN_ORIGIN].token);
        p += wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len;
 #ifdef LWS_OPENSSL_SUPPORT
-       if (use_ssl) {
+       if (wsi->ssl) {
                strcpy(p,   "\x0d\x0aSec-WebSocket-Location: wss://");
                p += strlen("\x0d\x0aSec-WebSocket-Location: wss://");
        } else {
index 1c4bd17..8d46209 100644 (file)
 
 #include "private-libwebsockets.h"
 
-#ifdef LWS_OPENSSL_SUPPORT
-SSL_CTX *ssl_ctx;
-int use_ssl;
-#endif
-
 void
 libwebsocket_close_and_free_session(struct libwebsocket *wsi)
 {
@@ -49,7 +44,7 @@ libwebsocket_close_and_free_session(struct libwebsocket *wsi)
 /*     fprintf(stderr, "closing fd=%d\n", wsi->sock); */
 
 #ifdef LWS_OPENSSL_SUPPORT
-       if (use_ssl) {
+       if (wsi->ssl) {
                n = SSL_get_fd(wsi->ssl);
                SSL_shutdown(wsi->ssl);
                close(n);
@@ -93,6 +88,19 @@ libwebsocket_poll_connections(struct libwebsocket_context *this)
                        goto nuke_this;
                }
 
+               /* the guy requested a callback when it was OK to write */
+
+               if ((unsigned long)this->wsi[client] > LWS_MAX_PROTOCOLS &&
+                                         this->fds[client].revents & POLLOUT) {
+
+                       this->fds[client].events &= ~POLLOUT;
+
+                       this->wsi[client]->protocol->callback(this->wsi[client],
+                               LWS_CALLBACK_CLIENT_WRITEABLE,
+                               this->wsi[client]->user_space,
+                               NULL, 0);
+               }
+
                /* any incoming data ready? */
 
                if (!(this->fds[client].revents & POLLIN))
@@ -158,7 +166,7 @@ libwebsocket_poll_connections(struct libwebsocket_context *this)
                }
 
 #ifdef LWS_OPENSSL_SUPPORT
-               if (this->use_ssl)
+               if (this->wsi[client]->ssl)
                        n = SSL_read(this->wsi[client]->ssl, buf, sizeof buf);
                else
 #endif
@@ -218,8 +226,8 @@ libwebsocket_context_destroy(struct libwebsocket_context *this)
                libwebsocket_close_and_free_session(this->wsi[client]);
 
 #ifdef LWS_OPENSSL_SUPPORT
-       if (ssl_ctx)
-               SSL_CTX_free(ssl_ctx);
+       if (this->ssl_ctx)
+               SSL_CTX_free(this->ssl_ctx);
 #endif
 
        if (this)
@@ -336,9 +344,12 @@ libwebsocket_service(struct libwebsocket_context *this, int timeout_ms)
                }
 
 #ifdef LWS_OPENSSL_SUPPORT
+               this->wsi[this->fds_count]->ssl = NULL;
+
                if (this->use_ssl) {
 
-                       this->wsi[this->fds_count]->ssl = SSL_new(ssl_ctx);
+                       this->wsi[this->fds_count]->ssl =
+                                                        SSL_new(this->ssl_ctx);
                        if (this->wsi[this->fds_count]->ssl == NULL) {
                                fprintf(stderr, "SSL_new failed: %s\n",
                                    ERR_error_string(SSL_get_error(
@@ -440,7 +451,8 @@ fatal:
                close(this->fds[0].fd);
 
 #ifdef LWS_OPENSSL_SUPPORT
-       SSL_CTX_free(ssl_ctx);
+       if (this->ssl_ctx)
+               SSL_CTX_free(this->ssl_ctx);
 #endif
 
        if (this)
@@ -453,6 +465,91 @@ fatal:
        return 1;
 }
 
+/**
+ * libwebsocket_callback_on_writable() - Request a callback when this socket
+ *                                      becomes able to be written to without
+ *                                      blocking
+ *
+ * @wsi:       Websocket connection instance to get callback for
+ */
+
+int
+libwebsocket_callback_on_writable(struct libwebsocket *wsi)
+{
+       struct libwebsocket_context *this = wsi->protocol->owning_server;
+       int n;
+
+       for (n = this->count_protocols + 1; n < this->fds_count; n++)
+               if (this->wsi[n] == wsi) {
+                       this->fds[n].events |= POLLOUT;
+                       return 0;
+               }
+
+       fprintf(stderr, "libwebsocket_callback_on_writable "
+                                                       "didn't find socket\n");
+       return 1;
+}
+
+/**
+ * libwebsocket_callback_on_writable_all_protocol() - Request a callback for
+ *                     all connections using the given protocol when it
+ *                     becomes possible to write to each socket without
+ *                     blocking in turn.
+ *
+ * @protocol:  Protocol whose connections will get callbacks
+ */
+
+int
+libwebsocket_callback_on_writable_all_protocol(
+                                 const struct libwebsocket_protocols *protocol)
+{
+       struct libwebsocket_context *this = protocol->owning_server;
+       int n;
+
+       for (n = this->count_protocols + 1; n < this->fds_count; n++)
+               if ((unsigned long)this->wsi[n] > LWS_MAX_PROTOCOLS)
+                       if (this->wsi[n]->protocol == protocol)
+                               this->fds[n].events |= POLLOUT;
+
+       return 0;
+}
+
+/**
+ * libwebsocket_rx_flow_control() - Enable and disable socket servicing for
+ *                             receieved packets.
+ *
+ * If the output side of a server process becomes choked, this allows flow
+ * control for the input side.
+ *
+ * @wsi:       Websocket connection instance to get callback for
+ * @enable:    0 = disable read servicing for this connection, 1 = enable
+ */
+
+int
+libwebsocket_rx_flow_control(struct libwebsocket *wsi, int enable)
+{
+       struct libwebsocket_context *this = wsi->protocol->owning_server;
+       int n;
+
+       for (n = this->count_protocols + 1; n < this->fds_count; n++)
+               if (this->wsi[n] == wsi) {
+                       if (enable)
+                               this->fds[n].events |= POLLIN;
+                       else
+                               this->fds[n].events &= ~POLLIN;
+
+                       return 0;
+               }
+
+       fprintf(stderr, "libwebsocket_callback_on_writable "
+                                                    "unable to find socket\n");
+       return 1;
+}
+
+static void sigpipe_handler(int x)
+{
+}
+
 
 /**
  * libwebsocket_create_context() - Create the websocket handler
@@ -515,47 +612,103 @@ libwebsocket_create_context(int port,
 #ifdef LWS_OPENSSL_SUPPORT
        SSL_METHOD *method;
        char ssl_err_buf[512];
+#endif
 
-       use_ssl = ssl_cert_filepath != NULL && ssl_private_key_filepath != NULL;
-       if (use_ssl)
-               fprintf(stderr, " Compiled with SSL support, using it\n");
-       else
-               fprintf(stderr, " Compiled with SSL support, not using it\n");
-
-#else
-       if (ssl_cert_filepath != NULL && ssl_private_key_filepath != NULL) {
-               fprintf(stderr, " Not compiled for OpenSSl support!\n");
+       this = malloc(sizeof(struct libwebsocket_context));
+       if (!this) {
+               fprintf(stderr, "No memory for websocket context\n");
                return NULL;
        }
-       fprintf(stderr, " Compiled without SSL support, serving unencrypted\n");
+       this->protocols = protocols;
+       this->listen_port = port;
+
+       if (port) {
+
+#ifdef LWS_OPENSSL_SUPPORT
+               this->use_ssl = ssl_cert_filepath != NULL &&
+                                              ssl_private_key_filepath != NULL;
+               if (this->use_ssl)
+                       fprintf(stderr, " Compiled with SSL support, "
+                                                                 "using it\n");
+               else
+                       fprintf(stderr, " Compiled with SSL support, "
+                                                             "not using it\n");
+
+#else
+               if (ssl_cert_filepath != NULL &&
+                                            ssl_private_key_filepath != NULL) {
+                       fprintf(stderr, " Not compiled for OpenSSl support!\n");
+                       return NULL;
+               }
+               fprintf(stderr, " Compiled without SSL support, "
+                                                      "serving unencrypted\n");
 #endif
+       }
+
+       /* ignore SIGPIPE */
+
+       signal(SIGPIPE, sigpipe_handler);
+
 
 #ifdef LWS_OPENSSL_SUPPORT
-       if (use_ssl) {
-               SSL_library_init();
 
-               OpenSSL_add_all_algorithms();
-               SSL_load_error_strings();
+       /* basic openssl init */
 
-               /*
-                * Firefox insists on SSLv23 not SSLv3
-                * Konq disables SSLv2 by default now, SSLv23 works
-                */
+       SSL_library_init();
+
+       OpenSSL_add_all_algorithms();
+       SSL_load_error_strings();
+
+       /*
+        * Firefox insists on SSLv23 not SSLv3
+        * Konq disables SSLv2 by default now, SSLv23 works
+        */
+
+       method = (SSL_METHOD *)SSLv23_server_method();
+       if (!method) {
+               fprintf(stderr, "problem creating ssl method: %s\n",
+                       ERR_error_string(ERR_get_error(), ssl_err_buf));
+               return NULL;
+       }
+       this->ssl_ctx = SSL_CTX_new(method);    /* create context */
+       if (!this->ssl_ctx) {
+               fprintf(stderr, "problem creating ssl context: %s\n",
+                       ERR_error_string(ERR_get_error(), ssl_err_buf));
+               return NULL;
+       }
+
+       /* client context */
+
+       method = (SSL_METHOD *)SSLv23_client_method();
+       if (!method) {
+               fprintf(stderr, "problem creating ssl method: %s\n",
+                       ERR_error_string(ERR_get_error(), ssl_err_buf));
+               return NULL;
+       }
+       this->ssl_client_ctx = SSL_CTX_new(method);     /* create context */
+       if (!this->ssl_client_ctx) {
+               fprintf(stderr, "problem creating ssl context: %s\n",
+                       ERR_error_string(ERR_get_error(), ssl_err_buf));
+               return NULL;
+       }
+
+
+       /* openssl init for cert verification (used with client sockets) */
+
+       if (!SSL_CTX_load_verify_locations(this->ssl_client_ctx, NULL,
+                                                   LWS_OPENSSL_CLIENT_CERTS)) {
+               fprintf(stderr, "Unable to load SSL Client certs from %s "
+                       "(set by --with-client-cert-dir= in configure) -- "
+                       " client ssl isn't going to work",
+                                                     LWS_OPENSSL_CLIENT_CERTS);
+       }
+
+       if (this->use_ssl) {
+
+               /* openssl init for server sockets */
 
-               method = (SSL_METHOD *)SSLv23_server_method();
-               if (!method) {
-                       fprintf(stderr, "problem creating ssl method: %s\n",
-                               ERR_error_string(ERR_get_error(), ssl_err_buf));
-                       return NULL;
-               }
-               ssl_ctx = SSL_CTX_new(method);  /* create context */
-               if (!ssl_ctx) {
-                       printf("problem creating ssl context: %s\n",
-                               ERR_error_string(ERR_get_error(), ssl_err_buf));
-                       return NULL;
-               }
                /* set the local certificate from CertFile */
-               n = SSL_CTX_use_certificate_file(ssl_ctx,
+               n = SSL_CTX_use_certificate_file(this->ssl_ctx,
                                        ssl_cert_filepath, SSL_FILETYPE_PEM);
                if (n != 1) {
                        fprintf(stderr, "problem getting cert '%s': %s\n",
@@ -564,7 +717,7 @@ libwebsocket_create_context(int port,
                        return NULL;
                }
                /* set the private key from KeyFile */
-               if (SSL_CTX_use_PrivateKey_file(ssl_ctx,
+               if (SSL_CTX_use_PrivateKey_file(this->ssl_ctx,
                                                ssl_private_key_filepath,
                                                       SSL_FILETYPE_PEM) != 1) {
                        fprintf(stderr, "ssl problem getting key '%s': %s\n",
@@ -573,7 +726,7 @@ libwebsocket_create_context(int port,
                        return NULL;
                }
                /* verify private key */
-               if (!SSL_CTX_check_private_key(ssl_ctx)) {
+               if (!SSL_CTX_check_private_key(this->ssl_ctx)) {
                        fprintf(stderr, "Private SSL key doesn't match cert\n");
                        return NULL;
                }
@@ -587,12 +740,6 @@ libwebsocket_create_context(int port,
        if (lws_b64_selftest())
                return NULL;
 
-
-       this = malloc(sizeof(struct libwebsocket_context));
-
-       this->protocols = protocols;
-       this->listen_port = port;
-
        /* set up our external listening socket we serve on */
 
        if (port) {
@@ -641,9 +788,6 @@ libwebsocket_create_context(int port,
        this->fds[0].fd = sockfd;
        this->fds[0].events = POLLIN;
        this->count_protocols = 0;
-#ifdef LWS_OPENSSL_SUPPORT
-       this->use_ssl = use_ssl;
-#endif
 
        if (port) {
                listen(sockfd, 5);
index 57ab481..a486221 100644 (file)
 
 enum libwebsocket_callback_reasons {
        LWS_CALLBACK_ESTABLISHED,
+       LWS_CALLBACK_CLIENT_ESTABLISHED,
        LWS_CALLBACK_CLOSED,
        LWS_CALLBACK_RECEIVE,
        LWS_CALLBACK_CLIENT_RECEIVE,
+       LWS_CALLBACK_CLIENT_WRITEABLE,
        LWS_CALLBACK_HTTP,
        LWS_CALLBACK_BROADCAST
 };
@@ -42,7 +44,9 @@ enum libwebsocket_write_protocol {
 
        LWS_WRITE_CLOSE,
        LWS_WRITE_PING,
-       LWS_WRITE_PONG
+       LWS_WRITE_PONG,
+
+       LWS_WRITE_NO_FIN = 0x40
 };
 
 struct libwebsocket;
@@ -67,15 +71,25 @@ struct libwebsocket_context;
  *     You get an opportunity to initialize user data when called back with
  *     LWS_CALLBACK_ESTABLISHED reason.
  *
- *     LWS_CALLBACK_ESTABLISHED:  after successful websocket handshake
+ *     LWS_CALLBACK_ESTABLISHED:  after the server completes a handshake with
+ *                             an incoming client
+ *
+ *      LWS_CALLBACK_CLIENT_ESTABLISHED: after your client connection completed
+ *                             a handshake with the remote server
  *
  *     LWS_CALLBACK_CLOSED: when the websocket session ends
  *
  *     LWS_CALLBACK_BROADCAST: signal to send to client (you would use
  *                             libwebsocket_write() taking care about the
  *                             special buffer requirements
- *     LWS_CALLBACK_RECEIVE: data has appeared for the server, it can be
- *                             found at *in and is len bytes long
+ *
+ *     LWS_CALLBACK_RECEIVE: data has appeared for this server endpoint from a
+ *                             remote client, it can be found at *in and is
+ *                             len bytes long
+ *
+ *     LWS_CALLBACK_CLIENT_RECEIVE: data has appeared from the server for the
+ *                             client connection, it can be found at *in and
+ *                             is len bytes long
  *
  *     LWS_CALLBACK_HTTP: an http request has come from a client that is not
  *                             asking to upgrade the connection to a websocket
@@ -85,6 +99,13 @@ struct libwebsocket_context;
  *                             @in points to the URI path requested and
  *                             libwebsockets_serve_http_file() makes it very
  *                             simple to send back a file to the client.
+ *
+ *     LWS_CALLBACK_CLIENT_WRITEABLE:  if you call
+ *             libwebsocket_callback_on_writable() on a connection, you will
+ *             get this callback coming when the connection socket is able to
+ *             accept another write packet without blocking.  If it already
+ *             was able to take another packet without blocking, you'll get
+ *             this callback at the next call to the service loop function.
  */
 extern int callback(struct libwebsocket *wsi,
                         enum libwebsocket_callback_reasons reason, void *user,
@@ -197,6 +218,17 @@ libwebsockets_broadcast(const struct libwebsocket_protocols *protocol,
 extern const struct libwebsocket_protocols *
 libwebsockets_get_protocol(struct libwebsocket *wsi);
 
+extern int
+libwebsocket_callback_on_writable(struct libwebsocket *wsi);
+
+extern int
+libwebsocket_callback_on_writable_all_protocol(
+                                const struct libwebsocket_protocols *protocol);
+
+
+extern int
+libwebsocket_rx_flow_control(struct libwebsocket *wsi, int enable);
+
 extern size_t
 libwebsockets_remaining_packet_payload(struct libwebsocket *wsi);
 
@@ -204,6 +236,7 @@ extern struct libwebsocket *
 libwebsocket_client_connect(struct libwebsocket_context *clients,
                              const char *address,
                              int port,
+                             int ssl_connection,
                              const char *path,
                              const char *host,
                              const char *origin,
index 9d9efb3..8d56221 100644 (file)
@@ -977,8 +977,6 @@ libwebsocket_04_frame_mask_generate(struct libwebsocket *wsi)
  *     packet while not burdening the user code with any protocol knowledge.
  */
 
- /* FIXME FIN bit */
-
 int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
                          size_t len, enum libwebsocket_write_protocol protocol)
 {
@@ -987,6 +985,11 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
        int post = 0;
        unsigned int shift = 7;
 
+       if (len == 0) {
+               fprintf(stderr, "zero length libwebsocket_write attempt\n");
+               return 0;
+       }
+
        if (protocol == LWS_WRITE_HTTP)
                goto send_raw;
 
@@ -999,7 +1002,7 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
        /* chrome likes this as of 30 Oct */
        /* Firefox 4.0b6 likes this as of 30 Oct */
        case 0:
-               if (protocol == LWS_WRITE_BINARY) {
+               if ((protocol & 0xf) == LWS_WRITE_BINARY) {
                        /* in binary mode we send 7-bit used length blocks */
                        pre = 1;
                        while (len & (127 << shift)) {
@@ -1031,7 +1034,7 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
 
        case 4:
 
-               switch (protocol) {
+               switch (protocol & 0xf) {
                case LWS_WRITE_TEXT:
                        n = LWS_WS_OPCODE_04__TEXT_FRAME;
                        break;
@@ -1054,12 +1057,8 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
                        return -1;
                }
 
-               /*
-                * We don't really support the metaframe concept with FIN.
-                * Just set FIN on every packet for now
-                */
-
-               n |= 1 << 7;
+               if (!(protocol & LWS_WRITE_NO_FIN))
+                       n |= 1 << 7;
 
                if (len < 126) {
                        buf[-2] = n;
@@ -1130,10 +1129,9 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
                memcpy(&buf[0 - pre], wsi->frame_masking_nonce_04, 4);
        }
 
-
 send_raw:
 #ifdef LWS_OPENSSL_SUPPORT
-       if (use_ssl) {
+       if (wsi->ssl) {
                n = SSL_write(wsi->ssl, buf - pre, len + pre + post);
                if (n < 0) {
                        fprintf(stderr, "ERROR writing to socket\n");
@@ -1149,6 +1147,7 @@ send_raw:
 #ifdef LWS_OPENSSL_SUPPORT
        }
 #endif
+
        debug("written %d bytes to client\n", (int)len);
 
        return 0;
@@ -1199,6 +1198,8 @@ int libwebsockets_serve_http_file(struct libwebsocket *wsi, const char *file,
        n = 1;
        while (n > 0) {
                n = read(fd, buf, 512);
+               if (n <= 0)
+                       continue;
                libwebsocket_write(wsi, (unsigned char *)buf, n,
                                                                LWS_WRITE_HTTP);
        }
index a263d83..dfeb373 100644 (file)
@@ -67,11 +67,6 @@ static inline void debug(const char *format, ...)
 }
 #endif
 
-#ifdef LWS_OPENSSL_SUPPORT
-extern SSL_CTX *ssl_ctx;
-extern int use_ssl;
-#endif
-
 
 #define MAX_CLIENTS 100
 #define LWS_MAX_HEADER_NAME_LENGTH 64
@@ -79,7 +74,7 @@ extern int use_ssl;
 #define LWS_INITIAL_HDR_ALLOC 256
 #define LWS_ADDITIONAL_HDR_ALLOC 64
 #define MAX_USER_RX_BUFFER 512
-#define MAX_BROADCAST_PAYLOAD 1024
+#define MAX_BROADCAST_PAYLOAD 2048
 #define LWS_MAX_PROTOCOLS 10
 
 #define MAX_WEBSOCKET_04_KEY_LEN 128
@@ -175,6 +170,8 @@ struct libwebsocket_context {
        int listen_port;
 #ifdef LWS_OPENSSL_SUPPORT
        int use_ssl;
+       SSL_CTX *ssl_ctx;
+       SSL_CTX *ssl_client_ctx;
 #endif
        struct libwebsocket_protocols *protocols;
        int count_protocols;
@@ -224,6 +221,7 @@ struct libwebsocket {
 
 #ifdef LWS_OPENSSL_SUPPORT
        SSL *ssl;
+       BIO *client_bio;
 #endif
 
        void *user_space;
index 1bec395..aea5d6a 100644 (file)
@@ -146,27 +146,31 @@ sha1_step(struct sha1_ctxt *ctxt)
 
        for (t = 0; t < 20; t++) {
                s = t & 0x0f;
-               if (t >= 16) {
-                       W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s));
-               }
+               if (t >= 16)
+                       W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
+                                                       W((s+2) & 0x0f) ^ W(s));
+
                tmp = S(5, a) + F0(b, c, d) + e + W(s) + K(t);
                e = d; d = c; c = S(30, b); b = a; a = tmp;
        }
        for (t = 20; t < 40; t++) {
                s = t & 0x0f;
-               W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s));
+               W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
+                                                       W((s+2) & 0x0f) ^ W(s));
                tmp = S(5, a) + F1(b, c, d) + e + W(s) + K(t);
                e = d; d = c; c = S(30, b); b = a; a = tmp;
        }
        for (t = 40; t < 60; t++) {
                s = t & 0x0f;
-               W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s));
+               W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
+                                                       W((s+2) & 0x0f) ^ W(s));
                tmp = S(5, a) + F2(b, c, d) + e + W(s) + K(t);
                e = d; d = c; c = S(30, b); b = a; a = tmp;
        }
        for (t = 60; t < 80; t++) {
                s = t & 0x0f;
-               W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s));
+               W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
+                                                       W((s+2) & 0x0f) ^ W(s));
                tmp = S(5, a) + F3(b, c, d) + e + W(s) + K(t);
                e = d; d = c; c = S(30, b); b = a; a = tmp;
        }
index 0016684..f649d79 100644 (file)
@@ -58,6 +58,45 @@ would call it with a timeout_ms of 0, so it returns immediately if
 nothing is pending, or as soon as it services whatever was pending.
 </blockquote>
 <hr>
+<h2>libwebsocket_callback_on_writable - Request a callback when this socket becomes able to be written to without blocking</h2>
+<i>int</i>
+<b>libwebsocket_callback_on_writable</b>
+(<i>struct libwebsocket *</i> <b>wsi</b>)
+<h3>Arguments</h3>
+<dl>
+<dt><b>wsi</b>
+<dd>Websocket connection instance to get callback for
+</dl>
+<hr>
+<h2>libwebsocket_callback_on_writable_all_protocol - Request a callback for all connections using the given protocol when it becomes possible to write to each socket without blocking in turn.</h2>
+<i>int</i>
+<b>libwebsocket_callback_on_writable_all_protocol</b>
+(<i>const struct libwebsocket_protocols *</i> <b>protocol</b>)
+<h3>Arguments</h3>
+<dl>
+<dt><b>protocol</b>
+<dd>Protocol whose connections will get callbacks
+</dl>
+<hr>
+<h2>libwebsocket_rx_flow_control - Enable and disable socket servicing for receieved packets.</h2>
+<i>int</i>
+<b>libwebsocket_rx_flow_control</b>
+(<i>struct libwebsocket *</i> <b>wsi</b>,
+<i>int</i> <b>enable</b>)
+<h3>Arguments</h3>
+<dl>
+<dt><b>wsi</b>
+<dd>Websocket connection instance to get callback for
+<dt><b>enable</b>
+<dd>0 = disable read servicing for this connection, 1 = enable
+</dl>
+<h3>Description</h3>
+<blockquote>
+<p>
+If the output side of a server process becomes choked, this allows flow
+control for the input side.
+</blockquote>
+<hr>
 <h2>libwebsocket_create_context - Create the websocket handler</h2>
 <i>struct libwebsocket_context *</i>
 <b>libwebsocket_create_context</b>
@@ -258,6 +297,44 @@ when that is the case <b>libwebsockets_remaining_packet_payload</b> will return
 Many protocols won't care becuse their packets are always small.
 </blockquote>
 <hr>
+<h2>libwebsocket_client_connect - Connect to another websocket server</h2>
+<i>struct libwebsocket *</i>
+<b>libwebsocket_client_connect</b>
+(<i>struct libwebsocket_context *</i> <b>this</b>,
+<i>const char *</i> <b>address</b>,
+<i>int</i> <b>port</b>,
+<i>int</i> <b>ssl_connection</b>,
+<i>const char *</i> <b>path</b>,
+<i>const char *</i> <b>host</b>,
+<i>const char *</i> <b>origin</b>,
+<i>const char *</i> <b>protocol</b>)
+<h3>Arguments</h3>
+<dl>
+<dt><b>this</b>
+<dd>Websocket context
+<dt><b>address</b>
+<dd>Remote server address, eg, "myserver.com"
+<dt><b>port</b>
+<dd>Port to connect to on the remote server, eg, 80
+<dt><b>ssl_connection</b>
+<dd>0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self
+signed certs
+<dt><b>path</b>
+<dd>Websocket path on server
+<dt><b>host</b>
+<dd>Hostname on server
+<dt><b>origin</b>
+<dd>Socket origin name
+<dt><b>protocol</b>
+<dd>Comma-separated list of protocols being asked for from
+the server, or just one.  The server will pick the one it
+likes best.
+</dl>
+<h3>Description</h3>
+<blockquote>
+This function creates a connection to a remote server
+</blockquote>
+<hr>
 <h2>callback - User server actions</h2>
 <i>int</i>
 <b>callback</b>
@@ -293,7 +370,13 @@ LWS_CALLBACK_ESTABLISHED reason.
 </blockquote>
 <h3>LWS_CALLBACK_ESTABLISHED</h3>
 <blockquote>
-after successful websocket handshake
+after the server completes a handshake with
+an incoming client
+</blockquote>
+<h3>LWS_CALLBACK_CLIENT_ESTABLISHED</h3>
+<blockquote>
+after your client connection completed
+a handshake with the remote server
 </blockquote>
 <h3>LWS_CALLBACK_CLOSED</h3>
 <blockquote>
@@ -307,8 +390,15 @@ special buffer requirements
 </blockquote>
 <h3>LWS_CALLBACK_RECEIVE</h3>
 <blockquote>
-data has appeared for the server, it can be
-found at *in and is len bytes long
+data has appeared for this server endpoint from a
+remote client, it can be found at *in and is
+len bytes long
+</blockquote>
+<h3>LWS_CALLBACK_CLIENT_RECEIVE</h3>
+<blockquote>
+data has appeared from the server for the
+client connection, it can be found at *in and
+is len bytes long
 </blockquote>
 <h3>LWS_CALLBACK_HTTP</h3>
 <blockquote>
@@ -321,6 +411,15 @@ which will then open the websockets connection.
 <b>libwebsockets_serve_http_file</b> makes it very
 simple to send back a file to the client.
 </blockquote>
+<h3>LWS_CALLBACK_CLIENT_WRITEABLE</h3>
+<blockquote>
+if you call
+<b>libwebsocket_callback_on_writable</b> on a connection, you will
+get this callback coming when the connection socket is able to
+accept another write packet without blocking.  If it already
+was able to take another packet without blocking, you'll get
+this callback at the next call to the service loop function.
+</blockquote>
 <hr>
 <h2>struct libwebsocket_protocols - List of protocols and handlers server supports.</h2>
 <b>struct libwebsocket_protocols</b> {<br>
index 43d3c7c..c1083a5 100644 (file)
@@ -3,6 +3,12 @@ libwebsockets_test_server_SOURCES=test-server.c
 libwebsockets_test_server_LDADD=-L../lib -lwebsockets
 libwebsockets_test_client_SOURCES=test-client.c
 libwebsockets_test_client_LDADD=-L../lib -lwebsockets
+
+
+
+libwebsockets_test_server_CFLAGS:= -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\"
+libwebsockets_test_client_CFLAGS:= -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\"
+
 #
 # cook a random test cert and key
 # notice your real cert and key will want to be 0600 permissions
index 03ad8a8..c7555af 100644 (file)
@@ -48,14 +48,24 @@ CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 am__installdirs = "$(DESTDIR)$(bindir)"
 PROGRAMS = $(bin_PROGRAMS)
-am_libwebsockets_test_client_OBJECTS = test-client.$(OBJEXT)
+am_libwebsockets_test_client_OBJECTS =  \
+       libwebsockets_test_client-test-client.$(OBJEXT)
 libwebsockets_test_client_OBJECTS =  \
        $(am_libwebsockets_test_client_OBJECTS)
 libwebsockets_test_client_DEPENDENCIES =
-am_libwebsockets_test_server_OBJECTS = test-server.$(OBJEXT)
+libwebsockets_test_client_LINK = $(LIBTOOL) --tag=CC \
+       $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+       $(libwebsockets_test_client_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+       $(LDFLAGS) -o $@
+am_libwebsockets_test_server_OBJECTS =  \
+       libwebsockets_test_server-test-server.$(OBJEXT)
 libwebsockets_test_server_OBJECTS =  \
        $(am_libwebsockets_test_server_OBJECTS)
 libwebsockets_test_server_DEPENDENCIES =
+libwebsockets_test_server_LINK = $(LIBTOOL) --tag=CC \
+       $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+       $(libwebsockets_test_server_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+       $(LDFLAGS) -o $@
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 am__depfiles_maybe = depfiles
@@ -156,6 +166,7 @@ build_cpu = @build_cpu@
 build_os = @build_os@
 build_vendor = @build_vendor@
 builddir = @builddir@
+clientcertdir = @clientcertdir@
 datadir = @datadir@
 datarootdir = @datarootdir@
 docdir = @docdir@
@@ -193,6 +204,8 @@ libwebsockets_test_server_SOURCES = test-server.c
 libwebsockets_test_server_LDADD = -L../lib -lwebsockets
 libwebsockets_test_client_SOURCES = test-client.c
 libwebsockets_test_client_LDADD = -L../lib -lwebsockets
+libwebsockets_test_server_CFLAGS := -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\"
+libwebsockets_test_client_CFLAGS := -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\"
 all: all-am
 
 .SUFFIXES:
@@ -272,10 +285,10 @@ clean-binPROGRAMS:
        rm -f $$list
 libwebsockets-test-client$(EXEEXT): $(libwebsockets_test_client_OBJECTS) $(libwebsockets_test_client_DEPENDENCIES) 
        @rm -f libwebsockets-test-client$(EXEEXT)
-       $(LINK) $(libwebsockets_test_client_OBJECTS) $(libwebsockets_test_client_LDADD) $(LIBS)
+       $(libwebsockets_test_client_LINK) $(libwebsockets_test_client_OBJECTS) $(libwebsockets_test_client_LDADD) $(LIBS)
 libwebsockets-test-server$(EXEEXT): $(libwebsockets_test_server_OBJECTS) $(libwebsockets_test_server_DEPENDENCIES) 
        @rm -f libwebsockets-test-server$(EXEEXT)
-       $(LINK) $(libwebsockets_test_server_OBJECTS) $(libwebsockets_test_server_LDADD) $(LIBS)
+       $(libwebsockets_test_server_LINK) $(libwebsockets_test_server_OBJECTS) $(libwebsockets_test_server_LDADD) $(LIBS)
 
 mostlyclean-compile:
        -rm -f *.$(OBJEXT)
@@ -283,8 +296,8 @@ mostlyclean-compile:
 distclean-compile:
        -rm -f *.tab.c
 
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-client.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_test_client-test-client.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_test_server-test-server.Po@am__quote@
 
 .c.o:
 @am__fastdepCC_TRUE@   $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -307,6 +320,34 @@ distclean-compile:
 @AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@  $(LTCOMPILE) -c -o $@ $<
 
+libwebsockets_test_client-test-client.o: test-client.c
+@am__fastdepCC_TRUE@   $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_client_CFLAGS) $(CFLAGS) -MT libwebsockets_test_client-test-client.o -MD -MP -MF $(DEPDIR)/libwebsockets_test_client-test-client.Tpo -c -o libwebsockets_test_client-test-client.o `test -f 'test-client.c' || echo '$(srcdir)/'`test-client.c
+@am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/libwebsockets_test_client-test-client.Tpo $(DEPDIR)/libwebsockets_test_client-test-client.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='test-client.c' object='libwebsockets_test_client-test-client.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_client_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_client-test-client.o `test -f 'test-client.c' || echo '$(srcdir)/'`test-client.c
+
+libwebsockets_test_client-test-client.obj: test-client.c
+@am__fastdepCC_TRUE@   $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_client_CFLAGS) $(CFLAGS) -MT libwebsockets_test_client-test-client.obj -MD -MP -MF $(DEPDIR)/libwebsockets_test_client-test-client.Tpo -c -o libwebsockets_test_client-test-client.obj `if test -f 'test-client.c'; then $(CYGPATH_W) 'test-client.c'; else $(CYGPATH_W) '$(srcdir)/test-client.c'; fi`
+@am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/libwebsockets_test_client-test-client.Tpo $(DEPDIR)/libwebsockets_test_client-test-client.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='test-client.c' object='libwebsockets_test_client-test-client.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_client_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_client-test-client.obj `if test -f 'test-client.c'; then $(CYGPATH_W) 'test-client.c'; else $(CYGPATH_W) '$(srcdir)/test-client.c'; fi`
+
+libwebsockets_test_server-test-server.o: test-server.c
+@am__fastdepCC_TRUE@   $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_CFLAGS) $(CFLAGS) -MT libwebsockets_test_server-test-server.o -MD -MP -MF $(DEPDIR)/libwebsockets_test_server-test-server.Tpo -c -o libwebsockets_test_server-test-server.o `test -f 'test-server.c' || echo '$(srcdir)/'`test-server.c
+@am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/libwebsockets_test_server-test-server.Tpo $(DEPDIR)/libwebsockets_test_server-test-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='test-server.c' object='libwebsockets_test_server-test-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_server-test-server.o `test -f 'test-server.c' || echo '$(srcdir)/'`test-server.c
+
+libwebsockets_test_server-test-server.obj: test-server.c
+@am__fastdepCC_TRUE@   $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_CFLAGS) $(CFLAGS) -MT libwebsockets_test_server-test-server.obj -MD -MP -MF $(DEPDIR)/libwebsockets_test_server-test-server.Tpo -c -o libwebsockets_test_server-test-server.obj `if test -f 'test-server.c'; then $(CYGPATH_W) 'test-server.c'; else $(CYGPATH_W) '$(srcdir)/test-server.c'; fi`
+@am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/libwebsockets_test_server-test-server.Tpo $(DEPDIR)/libwebsockets_test_server-test-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='test-server.c' object='libwebsockets_test_server-test-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_server-test-server.obj `if test -f 'test-server.c'; then $(CYGPATH_W) 'test-server.c'; else $(CYGPATH_W) '$(srcdir)/test-server.c'; fi`
+
 mostlyclean-libtool:
        -rm -f *.lo
 
@@ -514,6 +555,7 @@ uninstall-am: uninstall-binPROGRAMS
        mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
        tags uninstall uninstall-am uninstall-binPROGRAMS
 
+
 #
 # cook a random test cert and key
 # notice your real cert and key will want to be 0600 permissions
index 3243ccd..3636951 100644 (file)
  * server just it simplifies the demo).
  *
  *  dumb-increment-protocol:  we connect to the server and print the number
- *                             we are given
+ *                             we are given
  *
  *  lws-mirror-protocol: draws random circles, which are mirrored on to every
- *                             client (see them being drawn in every browser
- *                             session also using the test server)
+ *                             client (see them being drawn in every browser
+ *                             session also using the test server)
  */
 
 enum demo_protocols {
@@ -62,7 +62,8 @@ callback_dumb_increment(struct libwebsocket *wsi,
        switch (reason) {
 
        case LWS_CALLBACK_CLIENT_RECEIVE:
-               fprintf(stderr, "rx %d '%s'\n", len, in);
+               ((char *)in)[len] = '\0';
+               fprintf(stderr, "rx %d '%s'\n", (int)len, (char *)in);
                break;
 
        default:
@@ -75,148 +76,49 @@ callback_dumb_increment(struct libwebsocket *wsi,
 
 /* lws-mirror_protocol */
 
-/* "how to draw a circle" */
-
-struct coord {
-       int x;
-       int y;
-};
-
-static struct coord circle[] = {
-
-{ 0, 240 },
-{ 12, 239 },
-{ 25, 238 },
-{ 37, 237 },
-{ 49, 234 },
-{ 62, 231 },
-{ 74, 228 },
-{ 86, 224 },
-{ 97, 219 },
-{ 108, 213 },
-{ 120, 207 },
-{ 130, 201 },
-{ 141, 194 },
-{ 151, 186 },
-{ 160, 178 },
-{ 169, 169 },
-{ 178, 160 },
-{ 186, 151 },
-{ 194, 141 },
-{ 201, 130 },
-{ 207, 120 },
-{ 213, 108 },
-{ 219, 97 },
-{ 224, 86 },
-{ 228, 74 },
-{ 231, 62 },
-{ 234, 49 },
-{ 237, 37 },
-{ 238, 25 },
-{ 239, 12 },
-{ 240, 0 },
-{ 239, -12 },
-{ 238, -25 },
-{ 237, -37 },
-{ 234, -49 },
-{ 231, -62 },
-{ 228, -74 },
-{ 224, -86 },
-{ 219, -97 },
-{ 213, -108 },
-{ 207, -120 },
-{ 201, -130 },
-{ 194, -141 },
-{ 186, -151 },
-{ 178, -160 },
-{ 169, -169 },
-{ 160, -178 },
-{ 151, -186 },
-{ 141, -194 },
-{ 130, -201 },
-{ 120, -207 },
-{ 108, -213 },
-{ 97, -219 },
-{ 86, -224 },
-{ 74, -228 },
-{ 62, -231 },
-{ 49, -234 },
-{ 37, -237 },
-{ 25, -238 },
-{ 12, -239 },
-{ 0, -240 },
-{ -12, -239 },
-{ -25, -238 },
-{ -37, -237 },
-{ -49, -234 },
-{ -62, -231 },
-{ -74, -228 },
-{ -86, -224 },
-{ -97, -219 },
-{ -108, -213 },
-{ -119, -207 },
-{ -130, -201 },
-{ -141, -194 },
-{ -151, -186 },
-{ -160, -178 },
-{ -169, -169 },
-{ -178, -160 },
-{ -186, -151 },
-{ -194, -141 },
-{ -201, -130 },
-{ -207, -120 },
-{ -213, -108 },
-{ -219, -97 },
-{ -224, -86 },
-{ -228, -74 },
-{ -231, -62 },
-{ -234, -49 },
-{ -237, -37 },
-{ -238, -25 },
-{ -239, -12 },
-{ -240, 0 },
-{ -239, 12 },
-{ -238, 25 },
-{ -237, 37 },
-{ -234, 49 },
-{ -231, 62 },
-{ -228, 74 },
-{ -224, 86 },
-{ -219, 97 },
-{ -213, 108 },
-{ -207, 120 },
-{ -201, 130 },
-{ -194, 141 },
-{ -186, 151 },
-{ -178, 160 },
-{ -169, 169 },
-{ -160, 178 },
-{ -151, 186 },
-{ -141, 194 },
-{ -130, 201 },
-{ -119, 207 },
-{ -108, 213 },
-{ -97, 219 },
-{ -86, 224 },
-{ -74, 228 },
-{ -62, 231 },
-{ -49, 234 },
-{ -37, 237 },
-{ -25, 238 },
-{ -12, 239 },
-{ 0, 240 },
-       
-};
 
 static int
 callback_lws_mirror(struct libwebsocket *wsi,
                        enum libwebsocket_callback_reasons reason,
                                               void *user, void *in, size_t len)
 {
+       unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 4096 +
+                                                 LWS_SEND_BUFFER_POST_PADDING];
+       int l;
+
        switch (reason) {
 
+       case LWS_CALLBACK_CLIENT_ESTABLISHED:
+
+               /*
+                * start the ball rolling,
+                * LWS_CALLBACK_CLIENT_WRITEABLE will come immediately
+                */
+
+               libwebsocket_callback_on_writable(wsi);
+               break;
+
        case LWS_CALLBACK_CLIENT_RECEIVE:
-//             fprintf(stderr, "rx %d '%s'\n", len, in);
+/*             fprintf(stderr, "rx %d '%s'\n", (int)len, (char *)in); */
+               break;
+
+       case LWS_CALLBACK_CLIENT_WRITEABLE:
+
+               l = sprintf((char *)&buf[LWS_SEND_BUFFER_PRE_PADDING],
+                                       "c #%06X %d %d %d;",
+                                       (int)random() & 0xffffff,
+                                       (int)random() % 500,
+                                       (int)random() % 250,
+                                       (int)random() % 24);
+
+               libwebsocket_write(wsi,
+                       &buf[LWS_SEND_BUFFER_PRE_PADDING], l, LWS_WRITE_TEXT);
+
+               /* get notified as soon as we can write again */
+
+               libwebsocket_callback_on_writable(wsi);
+
+               usleep(200);
                break;
 
        default:
@@ -245,9 +147,9 @@ static struct libwebsocket_protocols protocols[] = {
 };
 
 static struct option options[] = {
-       { "help",       no_argument, NULL, 'h' },
-       { "port",       required_argument, NULL, 'p' },
-       { "ssl",        no_argument, NULL, 's' },
+       { "help",       no_argument,            NULL, 'h' },
+       { "port",       required_argument,      NULL, 'p' },
+       { "ssl",        no_argument,            NULL, 's' },
        { NULL, 0, 0, 0 }
 };
 
@@ -258,19 +160,10 @@ int main(int argc, char **argv)
        int port = 7681;
        int use_ssl = 0;
        struct libwebsocket_context *context;
-       const char * address = argv[1];
+       const char *address = argv[1];
        struct libwebsocket *wsi_dumb;
        struct libwebsocket *wsi_mirror;
-       unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1024 +
-                                                 LWS_SEND_BUFFER_POST_PADDING];
-       int len;
-       int i = 0;
-       int xofs;
-       int yofs;
-       int oldx;
-       int oldy;
-       int scale;
-       int colour;
+
 
        fprintf(stderr, "libwebsockets test client\n"
                        "(C) Copyright 2010 Andy Green <andy@warmcat.com> "
@@ -287,7 +180,7 @@ int main(int argc, char **argv)
                        continue;
                switch (n) {
                case 's':
-                       use_ssl = 1;
+                       use_ssl = 2; /* 2 = allow selfsigned */
                        break;
                case 'p':
                        port = atoi(optarg);
@@ -315,8 +208,8 @@ int main(int argc, char **argv)
 
        /* create a client websocket using dumb increment protocol */
 
-       wsi_dumb = libwebsocket_client_connect(context, address, port, "/",
-                                      "http://host", "origin",
+       wsi_dumb = libwebsocket_client_connect(context, address, port, use_ssl,
+                                       "/", "http://host", "origin",
                                       protocols[PROTOCOL_DUMB_INCREMENT].name);
 
        if (wsi_dumb == NULL) {
@@ -326,8 +219,8 @@ int main(int argc, char **argv)
 
        /* create a client websocket using mirror protocol */
 
-       wsi_mirror = libwebsocket_client_connect(context, address, port, "/",
-                                      "http://host", "origin",
+       wsi_mirror = libwebsocket_client_connect(context, address, port,
+                                       use_ssl,  "/", "http://host", "origin",
                                       protocols[PROTOCOL_LWS_MIRROR].name);
 
        if (wsi_mirror == NULL) {
@@ -343,39 +236,8 @@ int main(int argc, char **argv)
         */
 
        n = 0;
-       while (n >= 0) {
-
-               usleep(10000);
-
-               if (i == sizeof circle / sizeof circle[0])
-                       i = 0;
-
-               if (i == 0) {
-                       xofs = random() % 500;
-                       yofs = random() % 250;
-                       scale = random() % 24;
-                       if (!scale)
-                               scale = 1;
-
-                       oldx = xofs + (circle[i].x / scale);
-                       oldy = yofs + (circle[i].y / scale);
-                       colour = random() & 0xffffff;
-               }
-
-               len = sprintf(&buf[LWS_SEND_BUFFER_PRE_PADDING],
-                       "d #%06X %d %d %d %d", colour, oldx, oldy,
-                               xofs + (circle[i].x / scale),
-                               yofs + (circle[i].y / scale));
-               oldx = xofs + (circle[i].x / scale);
-               oldy = yofs + (circle[i].y / scale);
-               i++;
-
-               libwebsocket_write(wsi_mirror,
-                       &buf[LWS_SEND_BUFFER_PRE_PADDING], len, LWS_WRITE_TEXT);
-
-
-               n = libwebsocket_service(context, 0);
-       }
+       while (n >= 0)
+               n = libwebsocket_service(context, 1000);
 
        libwebsocket_client_close(wsi_dumb);
        libwebsocket_client_close(wsi_mirror);
index 832a45a..4f4512f 100644 (file)
@@ -24,6 +24,7 @@
 #include <unistd.h>
 #include <getopt.h>
 #include <string.h>
+#include <sys/time.h>
 
 #include "../lib/libwebsockets.h"
 
  *
  *  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.
+ *                             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
+ *                             using this protocol, including the sender
  */
 
 enum demo_protocols {
@@ -55,7 +56,7 @@ enum demo_protocols {
 };
 
 
-#define LOCAL_RESOURCE_PATH "/usr/share/libwebsockets-test-server"
+#define LOCAL_RESOURCE_PATH DATADIR"/libwebsockets-test-server"
 
 /* this protocol server (always the first one) just knows how to do HTTP */
 
@@ -65,7 +66,7 @@ static int callback_http(struct libwebsocket *wsi,
 {
        switch (reason) {
        case LWS_CALLBACK_HTTP:
-               fprintf(stderr, "serving HTTP URI %s\n", in);
+               fprintf(stderr, "serving HTTP URI %s\n", (char *)in);
 
                if (in && strcmp(in, "/favicon.ico") == 0) {
                        if (libwebsockets_serve_http_file(wsi,
@@ -108,9 +109,9 @@ callback_dumb_increment(struct libwebsocket *wsi,
                                               void *user, void *in, size_t len)
 {
        int n;
-       char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
+       unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
                                                  LWS_SEND_BUFFER_POST_PADDING];
-       char *p = (char *)&buf[LWS_SEND_BUFFER_PRE_PADDING];
+       unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
        struct per_session_data__dumb_increment *pss = user;
 
        switch (reason) {
@@ -126,7 +127,7 @@ callback_dumb_increment(struct libwebsocket *wsi,
         */
 
        case LWS_CALLBACK_BROADCAST:
-               n = sprintf(p, "%d", pss->number++);
+               n = sprintf((char *)p, "%d", pss->number++);
                n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
                if (n < 0) {
                        fprintf(stderr, "ERROR writing to socket");
@@ -135,7 +136,7 @@ callback_dumb_increment(struct libwebsocket *wsi,
                break;
 
        case LWS_CALLBACK_RECEIVE:
-               fprintf(stderr, "rx %d\n", len);
+               fprintf(stderr, "rx %d\n", (int)len);
                if (len < 6)
                        break;
                if (strcmp(in, "reset\n") == 0)
@@ -152,33 +153,92 @@ callback_dumb_increment(struct libwebsocket *wsi,
 
 /* lws-mirror_protocol */
 
+#define MAX_MESSAGE_QUEUE 64
+
+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 int
 callback_lws_mirror(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:
+               pss->ringbuffer_tail = ringbuffer_head;
+               pss->wsi = wsi;
+               break;
+
+       case LWS_CALLBACK_CLIENT_WRITEABLE:
+               if (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) {
+                               fprintf(stderr, "ERROR writing to socket");
+                               exit(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) < (MAX_MESSAGE_QUEUE - 15))
+                               libwebsocket_rx_flow_control(wsi, 1);
+
+                       libwebsocket_callback_on_writable(wsi);
+
+               }
+               break;
+
        case LWS_CALLBACK_BROADCAST:
                n = libwebsocket_write(wsi, in, len, LWS_WRITE_TEXT);
+               if (n < 0)
+                       fprintf(stderr, "mirror write failed\n");
                break;
 
        case LWS_CALLBACK_RECEIVE:
-               /*
-                * copy the incoming packet to all other protocol users
-                *
-                * This demonstrates how easy it is to broadcast from inside
-                * a callback.
-                * 
-                * How this works is it calls back to the callback for all
-                * connected sockets using this protocol with
-                * LWS_CALLBACK_BROADCAST reason.  Our handler for that above
-                * writes the data down the socket.
-                */
-               libwebsockets_broadcast(libwebsockets_get_protocol(wsi),
-                                                                      in, len);
+
+               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) > (MAX_MESSAGE_QUEUE - 10))
+                       libwebsocket_rx_flow_control(wsi, 0);
+
+               libwebsocket_callback_on_writable_all_protocol(
+                                              libwebsockets_get_protocol(wsi));
                break;
 
        default:
@@ -206,6 +266,8 @@ static struct libwebsocket_protocols protocols[] = {
        [PROTOCOL_LWS_MIRROR] = {
                .name = "lws-mirror-protocol",
                .callback = callback_lws_mirror,
+               .per_session_data_size =
+                               sizeof(struct per_session_data__lws_mirror),
        },
        [DEMO_PROTOCOL_COUNT] = {  /* end of list */
                .callback = NULL
@@ -213,9 +275,9 @@ static struct libwebsocket_protocols protocols[] = {
 };
 
 static struct option options[] = {
-       { "help",       no_argument, NULL, 'h' },
-       { "port",       required_argument, NULL, 'p' },
-       { "ssl",        no_argument, NULL, 's' },
+       { "help",       no_argument,            NULL, 'h' },
+       { "port",       required_argument,      NULL, 'p' },
+       { "ssl",        no_argument,            NULL, 's' },
        { NULL, 0, 0, 0 }
 };
 
@@ -231,6 +293,9 @@ int main(int argc, char **argv)
        int port = 7681;
        int use_ssl = 0;
        struct libwebsocket_context *context;
+#ifdef LWS_NO_FORK
+       unsigned int oldus = 0;
+#endif
 
        fprintf(stderr, "libwebsockets test server\n"
                        "(C) Copyright 2010-2011 Andy Green <andy@warmcat.com> "
@@ -275,13 +340,14 @@ int main(int argc, char **argv)
        fprintf(stderr, " Using no-fork service loop\n");
 
        while (1) {
-               
-               usleep(50000);
+               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.
                 *
@@ -291,8 +357,12 @@ int main(int argc, char **argv)
                 * We take care of pre-and-post padding allocation.
                 */
 
-               libwebsockets_broadcast(&protocols[PROTOCOL_DUMB_INCREMENT],
+               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
@@ -304,7 +374,7 @@ int main(int argc, char **argv)
                 * immediately and quickly.
                 */
 
-               libwebsocket_service(context, 0);
+               libwebsocket_service(context, 50);
        }
 
 #else
@@ -327,13 +397,13 @@ int main(int argc, char **argv)
        }
 
        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.
                 *
index 466310a..5f030f3 100644 (file)
@@ -7,7 +7,8 @@
 
 <body>
 <h2>libwebsockets "dumb-increment-protocol" test applet</h2>
-The incrementing number is coming from the server.
+The incrementing number is coming from the server and is individual for
+each connection to the server... try opening a second browser window.
 Click the button to send the server a websocket message to
 reset the number.<br><br>
 
@@ -27,6 +28,8 @@ well.
 The lws-mirror protocol doesn't interpret what is being sent to it, it just
 re-sends it to every other websocket it has a connection with using that
 protocol, including the guy who sent the packet.
+<p>libwebsockets-test-client spams circles on to this shared canvas when
+run.</p>
 <br><br>
 
 <table>
@@ -121,13 +124,25 @@ function reset() {
                } 
 
                socket_lm.onmessage =function got_packet(msg) {
-                       i = msg.data.split(' ');
-                       if (i[0] == 'd') {
-                               ctx.strokeStyle = i[1];
-                               ctx.beginPath();
-                               ctx.moveTo(+(i[2]), +(i[3]));
-                               ctx.lineTo(+(i[4]), +(i[5]));
-                               ctx.stroke();
+                       j = msg.data.split(';');
+                       f = 0;
+                       while (f < j.length - 1) {
+                               i = j[f].split(' ');
+                               if (i[0] == 'd') {
+                                       ctx.strokeStyle = i[1];
+                                       ctx.beginPath();
+                                       ctx.moveTo(+(i[2]), +(i[3]));
+                                       ctx.lineTo(+(i[4]), +(i[5]));
+                                       ctx.stroke();
+                               }
+                               if (i[0] == 'c') {
+                                       ctx.strokeStyle = i[1];
+                                       ctx.beginPath();
+                                       ctx.arc(+(i[2]), +(i[3]), +(i[4]), 0, Math.PI*2, true); 
+                                       ctx.stroke();
+                               }
+
+                               f++;
                        }
                }
 
@@ -192,7 +207,7 @@ function ev_mousemove (ev) {
                last_y = y;
                return;
        }
-       socket_lm.send("d " + color + " " + last_x + " " + last_y + " " + x + ' ' + y);
+       socket_lm.send("d " + color + " " + last_x + " " + last_y + " " + x + ' ' + y + ';');
 
        last_x = x;
        last_y = y;