expose-event-loop.patch

Signed-off-by: Andy Green <andy@warmcat.com>
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 742afa7..1ba4410 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -193,6 +193,189 @@
 }
 
 
+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 @@
  *	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 @@
  *	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 @@
 #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 @@
 		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 @@
 			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 @@
 			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 @@
 	/* 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 @@
 	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 @@
 		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 @@
 		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 @@
 		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
-	 */
+/**
+ * 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
+ */
 
-	n = fork();
-	if (n < 0) {
-		fprintf(stderr, "Failed to fork websocket poll loop\n");
-		return -1;
-	}
-	if (n) {
-		/* original process context */
+int
+libwebsockets_fork_service_loop(struct libwebsocket_context *this)
+{
+	int client;
+	int fd;
+	struct sockaddr_in cli_addr;
+	int n;
 
-		/*
-		 * before we return to caller, we set up per-protocol
-		 * broadcast sockets connected to the server ready to use
-		 */
+	if (fork())
+		return 0;
 
-		/* give server fork a chance to start up */
-		usleep(500000);
-
-		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;
-			}
-
-			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 @@
 
 	/* 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_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 @@
 
 	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
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index af37413..ba18cc9 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -134,11 +134,18 @@
 	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!
  *
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 3edc57a..327e09c 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -151,6 +151,8 @@
 	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 @@
 #ifdef LWS_OPENSSL_SUPPORT
 	int use_ssl;
 #endif
+	struct libwebsocket_protocols *protocols;
 	int count_protocols;
 };
 
diff --git a/libwebsockets-api-doc.html b/libwebsockets-api-doc.html
index 068db9c..77aea3e 100644
--- a/libwebsockets-api-doc.html
+++ b/libwebsockets-api-doc.html
@@ -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 @@
 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 @@
 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 @@
 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>,
diff --git a/test-server/test-server.c b/test-server/test-server.c
index de2cf1d..3365c0f 100644
--- a/test-server/test-server.c
+++ b/test-server/test-server.c
@@ -230,6 +230,7 @@
 						  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 @@
 	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 @@
 
 		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;