expose-event-loop.patch
authorAndy Green <andy@warmcat.com>
Wed, 19 Jan 2011 13:11:55 +0000 (13:11 +0000)
committerAndy Green <andy@warmcat.com>
Wed, 19 Jan 2011 13:11:55 +0000 (13:11 +0000)
Signed-off-by: Andy Green <andy@warmcat.com>
lib/libwebsockets.c
lib/libwebsockets.h
lib/private-libwebsockets.h
libwebsockets-api-doc.html
test-server/test-server.c

index 742afa7..1ba4410 100644 (file)
@@ -193,6 +193,189 @@ nuke_this:
 }
 
 
+int
+libwebsocket_service(struct libwebsocket_context *this, int timeout_ms)
+{
+       int n;
+       int client;
+       unsigned int clilen;
+       struct sockaddr_in cli_addr;
+       int fd;
+
+       /* stay dead once we are dead */
+
+       if (this == NULL)
+               return 1;
+
+       n = poll(this->fds, this->fds_count, timeout_ms);
+
+       if (n < 0 || this->fds[0].revents & (POLLERR | POLLHUP)) {
+               fprintf(stderr, "Listen Socket dead\n");
+               goto fatal;
+       }
+       if (n == 0) /* poll timeout */
+               return 0;
+
+       /* handle accept on listening socket? */
+
+       for (client = 0; client < this->count_protocols + 1; client++) {
+
+               if (!this->fds[client].revents & POLLIN)
+                       continue;
+
+               /* listen socket got an unencrypted connection... */
+
+               clilen = sizeof(cli_addr);
+               fd  = accept(this->fds[client].fd,
+                            (struct sockaddr *)&cli_addr, &clilen);
+               if (fd < 0) {
+                       fprintf(stderr, "ERROR on accept");
+                       continue;
+               }
+
+               if (this->fds_count >= MAX_CLIENTS) {
+                       fprintf(stderr, "too busy");
+                       close(fd);
+                       continue;
+               }
+
+               if (client) {
+                       /*
+                        * accepting a connection to broadcast socket
+                        * set wsi to be protocol index not pointer
+                        */
+
+                       this->wsi[this->fds_count] =
+                             (struct libwebsocket *)(long)(client - 1);
+
+                       goto fill_in_fds;
+               }
+
+               /* accepting connection to main listener */
+
+               this->wsi[this->fds_count] =
+                                   malloc(sizeof(struct libwebsocket));
+               if (!this->wsi[this->fds_count]) {
+                       fprintf(stderr, "Out of memory for new connection\n");
+                       continue;
+               }
+
+#ifdef LWS_OPENSSL_SUPPORT
+               if (this->use_ssl) {
+
+                       this->wsi[this->fds_count]->ssl = SSL_new(ssl_ctx);
+                       if (this->wsi[this->fds_count]->ssl == NULL) {
+                               fprintf(stderr, "SSL_new failed: %s\n",
+                                   ERR_error_string(SSL_get_error(
+                                   this->wsi[this->fds_count]->ssl, 0),
+                                                                NULL));
+                               free(this->wsi[this->fds_count]);
+                               continue;
+                       }
+
+                       SSL_set_fd(this->wsi[this->fds_count]->ssl, fd);
+
+                       n = SSL_accept(this->wsi[this->fds_count]->ssl);
+                       if (n != 1) {
+                               /*
+                                * browsers seem to probe with various
+                                * ssl params which fail then retry
+                                * and succeed
+                                */
+                               debug("SSL_accept failed skt %u: %s\n",
+                                     fd,
+                                     ERR_error_string(SSL_get_error(
+                                     this->wsi[this->fds_count]->ssl,
+                                                            n), NULL));
+                               SSL_free(
+                                      this->wsi[this->fds_count]->ssl);
+                               free(this->wsi[this->fds_count]);
+                               continue;
+                       }
+                       debug("accepted new SSL conn  "
+                             "port %u on fd=%d SSL ver %s\n",
+                               ntohs(cli_addr.sin_port), fd,
+                                 SSL_get_version(this->wsi[
+                                               this->fds_count]->ssl));
+
+               } else
+#endif
+                       debug("accepted new conn  port %u on fd=%d\n",
+                                         ntohs(cli_addr.sin_port), fd);
+
+               /* intialize the instance struct */
+
+               this->wsi[this->fds_count]->sock = fd;
+               this->wsi[this->fds_count]->state = WSI_STATE_HTTP;
+               this->wsi[this->fds_count]->name_buffer_pos = 0;
+
+               for (n = 0; n < WSI_TOKEN_COUNT; n++) {
+                       this->wsi[this->fds_count]->
+                                            utf8_token[n].token = NULL;
+                       this->wsi[this->fds_count]->
+                                           utf8_token[n].token_len = 0;
+               }
+
+               /*
+                * these can only be set once the protocol is known
+                * we set an unestablished connection's protocol pointer
+                * to the start of the supported list, so it can look
+                * for matching ones during the handshake
+                */
+               this->wsi[this->fds_count]->protocol = this->protocols;
+               this->wsi[this->fds_count]->user_space = NULL;
+
+               /*
+                * Default protocol is 76 / 00
+                * After 76, there's a header specified to inform which
+                * draft the client wants, when that's seen we modify
+                * the individual connection's spec revision accordingly
+                */
+               this->wsi[this->fds_count]->ietf_spec_revision = 0;
+
+fill_in_fds:
+
+               /*
+                * make sure NO events are seen yet on this new socket
+                * (otherwise we inherit old fds[client].revents from
+                * previous socket there and die mysteriously! )
+                */
+               this->fds[this->fds_count].revents = 0;
+
+               this->fds[this->fds_count].events = POLLIN;
+               this->fds[this->fds_count++].fd = fd;
+
+       }
+
+       /* service anything incoming on websocket connection */
+
+       libwebsocket_poll_connections(this);
+
+       /* this round is done */
+
+       return 0;
+
+fatal:
+
+       /* close listening skt and per-protocol broadcast sockets */
+       for (client = 0; client < this->fds_count; client++)
+               close(this->fds[0].fd);
+
+#ifdef LWS_OPENSSL_SUPPORT
+       SSL_CTX_free(ssl_ctx);
+#endif
+       kill(0, SIGTERM);
+
+       if (this)
+               free(this);
+
+       this = NULL;
+
+       /* inform caller we are dead */
+
+       return 1;
+}
+
 
 /**
  * libwebsocket_create_server() - Create the listening websockets server
@@ -212,10 +395,11 @@ nuke_this:
  *     This function creates the listening socket and takes care
  *     of all initialization in one step.
  *
- *     After initialization, it forks a thread that will sits in a service loop
- *     and returns to the caller.  The actual service actions are performed by
- *     user code in a per-protocol callback from the appropriate one selected
- *     by the client from the list in @protocols.
+ *     After initialization, it returns a struct libwebsocket_context * that
+ *     represents this server.  After calling, user code needs to take care
+ *     of calling libwebsocket_service() with the context pointer to get the
+ *     server's sockets serviced.  This can be done in the same process context
+ *     or a forked process, or another thread,
  *
  *     The protocol callback functions are called for a handful of events
  *     including http requests coming in, websocket connections becoming
@@ -234,17 +418,16 @@ nuke_this:
  *     one place; they're all handled in the user callback.
  */
 
-int libwebsocket_create_server(int port,
+struct libwebsocket_context *
+libwebsocket_create_server(int port,
                               struct libwebsocket_protocols *protocols,
                               const char *ssl_cert_filepath,
                               const char *ssl_private_key_filepath,
                               int gid, int uid)
 {
        int n;
-       int client;
        int sockfd;
        int fd;
-       unsigned int clilen;
        struct sockaddr_in serv_addr, cli_addr;
        int opt = 1;
        struct libwebsocket_context *this = NULL;
@@ -263,7 +446,7 @@ int libwebsocket_create_server(int port,
 #else
        if (ssl_cert_filepath != NULL && ssl_private_key_filepath != NULL) {
                fprintf(stderr, " Not compiled for OpenSSl support!\n");
-               return -1;
+               return NULL;
        }
        fprintf(stderr, " Compiled without SSL support, serving unencrypted\n");
 #endif
@@ -284,13 +467,13 @@ int libwebsocket_create_server(int port,
                if (!method) {
                        fprintf(stderr, "problem creating ssl method: %s\n",
                                ERR_error_string(ERR_get_error(), ssl_err_buf));
-                       return -1;
+                       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 -1;
+                       return NULL;
                }
                /* set the local certificate from CertFile */
                n = SSL_CTX_use_certificate_file(ssl_ctx,
@@ -299,7 +482,7 @@ int libwebsocket_create_server(int port,
                        fprintf(stderr, "problem getting cert '%s': %s\n",
                                ssl_cert_filepath,
                                ERR_error_string(ERR_get_error(), ssl_err_buf));
-                       return -1;
+                       return NULL;
                }
                /* set the private key from KeyFile */
                if (SSL_CTX_use_PrivateKey_file(ssl_ctx,
@@ -308,12 +491,12 @@ int libwebsocket_create_server(int port,
                        fprintf(stderr, "ssl problem getting key '%s': %s\n",
                                                ssl_private_key_filepath,
                                ERR_error_string(ERR_get_error(), ssl_err_buf));
-                       return -1;
+                       return NULL;
                }
                /* verify private key */
                if (!SSL_CTX_check_private_key(ssl_ctx)) {
                        fprintf(stderr, "Private SSL key doesn't match cert\n");
-                       return -1;
+                       return NULL;
                }
 
                /* SSL is happy and has a cert it's content with */
@@ -323,17 +506,19 @@ int libwebsocket_create_server(int port,
        /* selftest */
 
        if (lws_b64_selftest())
-               return -1;
+               return NULL;
 
 
        this = malloc(sizeof(struct libwebsocket_context));
 
+       this->protocols = protocols;
+
        /* set up our external listening socket we serve on */
 
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) {
                fprintf(stderr, "ERROR opening socket");
-               return -1;
+               return NULL;
        }
 
        /* allow us to restart even if old sockets in TIME_WAIT */
@@ -348,7 +533,7 @@ int libwebsocket_create_server(int port,
        if (n < 0) {
                fprintf(stderr, "ERROR on binding to port %d (%d %d)\n",
                                                                port, n, errno);
-               return -1;
+               return NULL;
        }
 
        /* drop any root privs for this process */
@@ -390,7 +575,7 @@ int libwebsocket_create_server(int port,
                fd = socket(AF_INET, SOCK_STREAM, 0);
                if (fd < 0) {
                        fprintf(stderr, "ERROR opening socket");
-                       return -1;
+                       return NULL;
                }
 
                /* allow us to restart even if old sockets in TIME_WAIT */
@@ -405,14 +590,14 @@ int libwebsocket_create_server(int port,
                if (n < 0) {
                        fprintf(stderr, "ERROR on binding to port %d (%d %d)\n",
                                                                port, n, errno);
-                       return -1;
+                       return NULL;
                }
 
                slen = sizeof cli_addr;
                n = getsockname(fd, (struct sockaddr *)&cli_addr, &slen);
                if (n < 0) {
                        fprintf(stderr, "getsockname failed\n");
-                       return -1;
+                       return NULL;
                }
                protocols[this->count_protocols].broadcast_socket_port =
                                                       ntohs(cli_addr.sin_port);
@@ -429,53 +614,50 @@ int libwebsocket_create_server(int port,
                this->fds_count++;
        }
 
+       return this;
+}
 
-       /*
-        * We will enter out poll and service loop now, just before that
-        * fork and return to caller for the main thread of execution
-        */
-
-       n = fork();
-       if (n < 0) {
-               fprintf(stderr, "Failed to fork websocket poll loop\n");
-               return -1;
-       }
-       if (n) {
-               /* original process context */
-
-               /*
-                * before we return to caller, we set up per-protocol
-                * broadcast sockets connected to the server ready to use
-                */
+/**
+ * libwebsockets_fork_service_loop() - Optional helper function forks off
+ *                               a process for the websocket server loop.
+ *                             You don't have to use this but if not, you
+ *                             have to make sure you are calling
+ *                             libwebsocket_service periodically to service
+ *                             the websocket traffic
+ * @this:      server context returned by creation function
+ */
 
-               /* give server fork a chance to start up */
-               usleep(500000);
+int
+libwebsockets_fork_service_loop(struct libwebsocket_context *this)
+{
+       int client;
+       int fd;
+       struct sockaddr_in cli_addr;
+       int n;
 
-               for (client = 1; client < this->count_protocols + 1; client++) {
-                       fd = socket(AF_INET, SOCK_STREAM, 0);
-                       if (fd < 0) {
-                               fprintf(stderr, "Unable to create socket\n");
-                               return -1;
-                       }
-                       cli_addr.sin_family = AF_INET;
-                       cli_addr.sin_port = htons(
-                                  protocols[client - 1].broadcast_socket_port);
-                       cli_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
-                       n = connect(fd, (struct sockaddr *)&cli_addr,
-                                                              sizeof cli_addr);
-                       if (n < 0) {
-                               fprintf(stderr, "Unable to connect to "
-                                               "broadcast socket %d, %s\n",
-                                               client, strerror(errno));
-                               return -1;
-                       }
+       if (fork())
+               return 0;
 
-                       protocols[client - 1].broadcast_socket_user_fd = fd;
+       for (client = 1; client < this->count_protocols + 1; client++) {
+               fd = socket(AF_INET, SOCK_STREAM, 0);
+               if (fd < 0) {
+                       fprintf(stderr, "Unable to create socket\n");
+                       return -1;
+               }
+               cli_addr.sin_family = AF_INET;
+               cli_addr.sin_port = htons(
+                          this->protocols[client - 1].broadcast_socket_port);
+               cli_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+               n = connect(fd, (struct sockaddr *)&cli_addr,
+                                                      sizeof cli_addr);
+               if (n < 0) {
+                       fprintf(stderr, "Unable to connect to "
+                                       "broadcast socket %d, %s\n",
+                                       client, strerror(errno));
+                       return -1;
                }
 
-               fprintf(stderr, "libwebsocket poll process forked\n");
-
-               return 0;
+               this->protocols[client - 1].broadcast_socket_user_fd = fd;
        }
 
        /* we want a SIGHUP when our parent goes down */
@@ -483,166 +665,9 @@ int libwebsocket_create_server(int port,
 
        /* in this forked process, sit and service websocket connections */
 
-       while (1) {
-
-               n = poll(this->fds, this->fds_count, 1000);
-
-               if (n < 0 || this->fds[0].revents & (POLLERR | POLLHUP)) {
-                       fprintf(stderr, "Listen Socket dead\n");
-                       goto fatal;
-               }
-               if (n == 0) /* poll timeout */
-                       continue;
-
-               /* handle accept on listening socket? */
-
-               for (client = 0; client < this->count_protocols + 1; client++) {
-
-                       if (!this->fds[client].revents & POLLIN)
-                               continue;
-
-                       /* listen socket got an unencrypted connection... */
-
-                       clilen = sizeof(cli_addr);
-                       fd  = accept(this->fds[client].fd,
-                                    (struct sockaddr *)&cli_addr, &clilen);
-                       if (fd < 0) {
-                               fprintf(stderr, "ERROR on accept");
-                               continue;
-                       }
-
-                       if (this->fds_count >= MAX_CLIENTS) {
-                               fprintf(stderr, "too busy");
-                               close(fd);
-                               continue;
-                       }
-
-                       if (client) {
-                               /*
-                                * accepting a connection to broadcast socket
-                                * set wsi to be protocol index not pointer
-                                */
-
-                               this->wsi[this->fds_count] =
-                                     (struct libwebsocket *)(long)(client - 1);
-
-                               goto fill_in_fds;
-                       }
-
-                       /* accepting connection to main listener */
-
-                       this->wsi[this->fds_count] =
-                                           malloc(sizeof(struct libwebsocket));
-                       if (!this->wsi[this->fds_count])
-                               return -1;
-
-       #ifdef LWS_OPENSSL_SUPPORT
-                       if (this->use_ssl) {
-
-                               this->wsi[this->fds_count]->ssl =
-                                                              SSL_new(ssl_ctx);
-                               if (this->wsi[this->fds_count]->ssl == NULL) {
-                                       fprintf(stderr, "SSL_new failed: %s\n",
-                                           ERR_error_string(SSL_get_error(
-                                           this->wsi[this->fds_count]->ssl, 0),
-                                                                        NULL));
-                                       free(this->wsi[this->fds_count]);
-                                       continue;
-                               }
-
-                               SSL_set_fd(this->wsi[this->fds_count]->ssl, fd);
-
-                               n = SSL_accept(this->wsi[this->fds_count]->ssl);
-                               if (n != 1) {
-                                       /*
-                                        * browsers seem to probe with various
-                                        * ssl params which fail then retry
-                                        * and succeed
-                                        */
-                                       debug("SSL_accept failed skt %u: %s\n",
-                                             fd,
-                                             ERR_error_string(SSL_get_error(
-                                             this->wsi[this->fds_count]->ssl,
-                                                                    n), NULL));
-                                       SSL_free(
-                                              this->wsi[this->fds_count]->ssl);
-                                       free(this->wsi[this->fds_count]);
-                                       continue;
-                               }
-                               debug("accepted new SSL conn  "
-                                     "port %u on fd=%d SSL ver %s\n",
-                                       ntohs(cli_addr.sin_port), fd,
-                                         SSL_get_version(this->wsi[
-                                                       this->fds_count]->ssl));
-
-                       } else
-       #endif
-                               debug("accepted new conn  port %u on fd=%d\n",
-                                                 ntohs(cli_addr.sin_port), fd);
-
-                       /* intialize the instance struct */
-
-                       this->wsi[this->fds_count]->sock = fd;
-                       this->wsi[this->fds_count]->state = WSI_STATE_HTTP;
-                       this->wsi[this->fds_count]->name_buffer_pos = 0;
-
-                       for (n = 0; n < WSI_TOKEN_COUNT; n++) {
-                               this->wsi[this->fds_count]->
-                                                    utf8_token[n].token = NULL;
-                               this->wsi[this->fds_count]->
-                                                   utf8_token[n].token_len = 0;
-                       }
-
-                       /*
-                        * these can only be set once the protocol is known
-                        * we set an unestablished connection's protocol pointer
-                        * to the start of the supported list, so it can look
-                        * for matching ones during the handshake
-                        */
-                       this->wsi[this->fds_count]->protocol = protocols;
-                       this->wsi[this->fds_count]->user_space = NULL;
-
-                       /*
-                        * Default protocol is 76 / 00
-                        * After 76, there's a header specified to inform which
-                        * draft the client wants, when that's seen we modify
-                        * the individual connection's spec revision accordingly
-                        */
-                       this->wsi[this->fds_count]->ietf_spec_revision = 0;
-
-fill_in_fds:
-
-                       /*
-                        * make sure NO events are seen yet on this new socket
-                        * (otherwise we inherit old fds[client].revents from
-                        * previous socket there and die mysteriously! )
-                        */
-                       this->fds[this->fds_count].revents = 0;
-
-                       this->fds[this->fds_count].events = POLLIN;
-                       this->fds[this->fds_count++].fd = fd;
-
-               }
-
-
-               /* service anything incoming on websocket connection */
-
-               libwebsocket_poll_connections(this);
-       }
-
-fatal:
-
-       /* close listening skt and per-protocol broadcast sockets */
-       for (client = 0; client < this->fds_count; client++)
-               close(this->fds[0].fd);
-
-#ifdef LWS_OPENSSL_SUPPORT
-       SSL_CTX_free(ssl_ctx);
-#endif
-       kill(0, SIGTERM);
-
-       if (this)
-               free(this);
+       while (1)
+               if (libwebsocket_service(this, 1000))
+                       return -1;
 
        return 0;
 }
@@ -664,7 +689,7 @@ libwebsockets_get_protocol(struct libwebsocket *wsi)
 }
 
 /**
- * libwebsockets_broadcast() - Sends a buffer to rthe callback for all active
+ * libwebsockets_broadcast() - Sends a buffer to the callback for all active
  *                               connections of the given protocol.
  * @protocol:  pointer to the protocol you will broadcast to all members of
  * @buf:  buffer containing the data to be broadcase.  NOTE: this has to be
@@ -694,7 +719,8 @@ libwebsockets_broadcast(const struct libwebsocket_protocols *protocol,
 
        if (!protocol->broadcast_socket_user_fd) {
                /*
-                * we are being called from poll thread context
+                * We are either running unforked / flat, or we are being
+                * called from poll thread context
                 * eg, from a callback.  In that case don't use sockets for
                 * broadcast IPC (since we can't open a socket connection to
                 * a socket listening on our own thread) but directly do the
index af37413..ba18cc9 100644 (file)
@@ -134,11 +134,18 @@ struct libwebsocket_protocols {
        int protocol_index;
 };
 
-extern int libwebsocket_create_server(int port,
+extern struct libwebsocket_context *
+libwebsocket_create_server(int port,
                  struct libwebsocket_protocols *protocols,
                  const char *ssl_cert_filepath,
                  const char *ssl_private_key_filepath, int gid, int uid);
 
+extern int
+libwebsockets_fork_service_loop(struct libwebsocket_context *this);
+
+extern int
+libwebsocket_service(struct libwebsocket_context *this, int timeout_ms);
+
 /*
  * IMPORTANT NOTICE!
  *
index 3edc57a..327e09c 100644 (file)
@@ -151,6 +151,8 @@ struct lws_tokens {
        int token_len;
 };
 
+struct libwebsocket_protocols;
+
 struct libwebsocket_context {
        struct libwebsocket *wsi[MAX_CLIENTS + 1];
        struct pollfd fds[MAX_CLIENTS + 1];
@@ -158,6 +160,7 @@ struct libwebsocket_context {
 #ifdef LWS_OPENSSL_SUPPORT
        int use_ssl;
 #endif
+       struct libwebsocket_protocols *protocols;
        int count_protocols;
 };
 
index 068db9c..77aea3e 100644 (file)
@@ -1,5 +1,5 @@
 <h2>libwebsocket_create_server - Create the listening websockets server</h2>
-<i>int</i>
+<i>struct libwebsocket_context *</i>
 <b>libwebsocket_create_server</b>
 (<i>int</i> <b>port</b>,
 <i>struct libwebsocket_protocols *</i> <b>protocols</b>,
@@ -33,10 +33,11 @@ else ignored
 This function creates the listening socket and takes care
 of all initialization in one step.
 <p>
-After initialization, it forks a thread that will sits in a service loop
-and returns to the caller.  The actual service actions are performed by
-user code in a per-protocol callback from the appropriate one selected
-by the client from the list in <tt><b>protocols</b></tt>.
+After initialization, it returns a struct libwebsocket_context * that
+represents this server.  After calling, user code needs to take care
+of calling <b>libwebsocket_service</b> with the context pointer to get the
+server's sockets serviced.  This can be done in the same process context
+or a forked process, or another thread,
 <p>
 The protocol callback functions are called for a handful of events
 including http requests coming in, websocket connections becoming
@@ -55,6 +56,16 @@ images or whatever over http and dynamic data over websockets all in
 one place; they're all handled in the user callback.
 </blockquote>
 <hr>
+<h2>libwebsockets_fork_service_loop - Optional helper function forks off a process for the websocket server loop. You don't have to use this but if not, you have to make sure you are calling libwebsocket_service periodically to service the websocket traffic</h2>
+<i>int</i>
+<b>libwebsockets_fork_service_loop</b>
+(<i>struct libwebsocket_context *</i> <b>this</b>)
+<h3>Arguments</h3>
+<dl>
+<dt><b>this</b>
+<dd>server context returned by creation function
+</dl>
+<hr>
 <h2>libwebsockets_get_protocol - Returns a protocol pointer from a websocket connection.</h2>
 <i>const struct libwebsocket_protocols *</i>
 <b>libwebsockets_get_protocol</b>
@@ -71,7 +82,7 @@ This is useful to get the protocol to broadcast back to from inside
 the callback.
 </blockquote>
 <hr>
-<h2>libwebsockets_broadcast - Sends a buffer to rthe callback for all active connections of the given protocol.</h2>
+<h2>libwebsockets_broadcast - Sends a buffer to the callback for all active connections of the given protocol.</h2>
 <i>int</i>
 <b>libwebsockets_broadcast</b>
 (<i>const struct libwebsocket_protocols *</i> <b>protocol</b>,
index de2cf1d..3365c0f 100644 (file)
@@ -230,6 +230,7 @@ int main(int argc, char **argv)
                                                  LWS_SEND_BUFFER_POST_PADDING];
        int port = 7681;
        int use_ssl = 0;
+       struct libwebsocket_context *server;
 
        fprintf(stderr, "libwebsockets test server\n"
                        "(C) Copyright 2010 Andy Green <andy@warmcat.com> "
@@ -256,8 +257,9 @@ int main(int argc, char **argv)
        if (!use_ssl)
                cert_path = key_path = NULL;
 
-       if (libwebsocket_create_server(port, protocols, cert_path, key_path,
-                                                                 -1, -1) < 0) {
+       server = libwebsocket_create_server(port, protocols, cert_path,
+                                                             key_path, -1, -1);
+       if (server == NULL) {
                fprintf(stderr, "libwebsocket init failed\n");
                return -1;
        }
@@ -288,6 +290,20 @@ int main(int argc, char **argv)
 
                libwebsockets_broadcast(&protocols[PROTOCOL_DUMB_INCREMENT],
                                        &buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
+
+               /*
+                * 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".
+                *
+                * There's an optional call libwebsockets_fork_service_loop()
+                * we could have used before this while loop, then the
+                * websockets would have been serviced in a forked process
+                * and we would not have to do the call below inside our loop.
+                */
+
+               libwebsocket_service(server, 0);
        }
 
        return 0;