introduce-test-ping-app.patch

Signed-off-by: Andy Green <andy@warmcat.com>
diff --git a/README-test-server b/README-test-server
index f10520e..cb9e6b5 100644
--- a/README-test-server
+++ b/README-test-server
@@ -37,6 +37,11 @@
 			     look for trust certificates to validate
 			     the remote certificate against.
 
+--enable-noping		Don't try to build the ping test app
+			It needs some unixy environment that
+			may choke in other build contexts, this
+			lets you cleanly stop it being built
+
 Testing server with a browser
 -----------------------------
 
@@ -107,6 +112,53 @@
 certificate that it doesn't have a trusted CA cert for.
 
 
+Using the websocket ping utility
+--------------------------------
+
+libwebsockets-test-ping connects as a client to a remote
+websocket server using 04 protocol and pings it like the
+normal unix ping utility.
+
+$ libwebsockets-test-ping localhost                
+handshake OK for protocol lws-mirror-protocol
+Websocket PING localhost.localdomain (127.0.0.1) 64 bytes of data.
+64 bytes from localhost: req=1 time=0.1ms
+64 bytes from localhost: req=2 time=0.1ms
+64 bytes from localhost: req=3 time=0.1ms
+64 bytes from localhost: req=4 time=0.2ms
+64 bytes from localhost: req=5 time=0.1ms
+64 bytes from localhost: req=6 time=0.2ms
+64 bytes from localhost: req=7 time=0.2ms
+64 bytes from localhost: req=8 time=0.1ms
+^C
+--- localhost.localdomain websocket ping statistics ---
+8 packets transmitted, 8 received, 0% packet loss, time 7458ms
+rtt min/avg/max = 0.110/0.185/0.218 ms
+$
+
+By default it sends 64 byte payload packets using the 04
+PING packet opcode type.  You can change the payload size
+using the -s= flag, up to a maximum of 125 mandated by the
+04 standard.
+
+Using the lws-mirror protocol that is provided by the test
+server, libwebsockets-test-ping can also use larger payload
+sizes up to 4096 is BINARY packets; lws-mirror will copy
+them back to the client and they appear as a PONG.  Use the
+-m flag to select this operation.
+
+The default interval between pings is 1s, you can use the -i=
+flag to set this, including fractions like -i=0.01 for 10ms
+interval.
+
+Before you can even use the PING opcode that is part of the
+standard, you must complete a handshake with a specified
+protocol.  By default lws-mirror-protocol is used which is
+supported by the test server.  But if you are using it on
+another server, you can specify the protcol to handshake with
+by --protocol=protocolname
+
+
 Websocket version supported
 ---------------------------
 
diff --git a/configure b/configure
index e301a98..c39b140 100755
--- a/configure
+++ b/configure
@@ -616,6 +616,8 @@
 am__EXEEXT_TRUE
 LTLIBOBJS
 LIBOBJS
+NOPING_FALSE
+NOPING_TRUE
 clientcertdir
 LIBCRYPTO_FALSE
 LIBCRYPTO_TRUE
@@ -741,6 +743,7 @@
 enable_nofork
 enable_libcrypto
 with_client_cert_dir
+enable_noping
 '
       ac_precious_vars='build_alias
 host_alias
@@ -1386,6 +1389,7 @@
   --enable-openssl  Enables https support and needs openssl libs
   --enable-nofork  Disables fork-related options
   --enable-libcrypto  Use libcrypto MD5 and SHA1 implementations
+  --enable-noping  Do not build ping test app, which has some unixy stuff in sources
 
 Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
@@ -12307,6 +12311,26 @@
 
 
 
+#
+#
+#
+# Check whether --enable-noping was given.
+if test "${enable_noping+set}" = set; then :
+  enableval=$enable_noping;  noping=yes
+
+fi
+
+
+ if test x$noping = xyes; then
+  NOPING_TRUE=
+  NOPING_FALSE='#'
+else
+  NOPING_TRUE='#'
+  NOPING_FALSE=
+fi
+
+
+
 
 # Checks for header files.
 for ac_header in fcntl.h netinet/in.h stdlib.h string.h sys/socket.h unistd.h
@@ -12621,6 +12645,10 @@
   as_fn_error $? "conditional \"LIBCRYPTO\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${NOPING_TRUE}" && test -z "${NOPING_FALSE}"; then
+  as_fn_error $? "conditional \"NOPING\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 
 : "${CONFIG_STATUS=./config.status}"
 ac_write_fail=0
diff --git a/configure.ac b/configure.ac
index 251a49d..c7d91b0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -73,6 +73,17 @@
 AC_SUBST([CFLAGS])
 
 
+#
+#
+#
+AC_ARG_ENABLE(noping,
+ [  --enable-noping  Do not build ping test app, which has some unixy stuff in sources],
+ [ noping=yes
+ ])
+
+AM_CONDITIONAL(NOPING, test x$noping = xyes)
+
+
 
 # Checks for header files.
 AC_CHECK_HEADERS([fcntl.h netinet/in.h stdlib.h string.h sys/socket.h unistd.h])
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 8d46209..e6db157 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -514,6 +514,21 @@
 	return 0;
 }
 
+
+/**
+ * libwebsocket_get_socket_fd() - returns the socket file descriptor
+ *
+ * You will not need this unless you are doing something special
+ *
+ * @wsi:	Websocket connection instance
+ */
+
+int
+libwebsocket_get_socket_fd(struct libwebsocket *wsi)
+{
+	return wsi->sock;
+}
+
 /**
  * libwebsocket_rx_flow_control() - Enable and disable socket servicing for
  *				receieved packets.
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index a486221..ac28abf 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -30,6 +30,7 @@
 	LWS_CALLBACK_CLOSED,
 	LWS_CALLBACK_RECEIVE,
 	LWS_CALLBACK_CLIENT_RECEIVE,
+	LWS_CALLBACK_CLIENT_RECEIVE_PONG,
 	LWS_CALLBACK_CLIENT_WRITEABLE,
 	LWS_CALLBACK_HTTP,
 	LWS_CALLBACK_BROADCAST
@@ -87,6 +88,10 @@
  *				remote client, it can be found at *in and is
  *				len bytes long
  *
+ *	LWS_CALLBACK_CLIENT_RECEIVE_PONG: if you elected to see PONG packets,
+ *				they appear with this callback reason.  PONG
+ *				packets only exist in 04+ protocol
+ *
  *	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
@@ -225,6 +230,8 @@
 libwebsocket_callback_on_writable_all_protocol(
 				 const struct libwebsocket_protocols *protocol);
 
+extern int
+libwebsocket_get_socket_fd(struct libwebsocket *wsi);
 
 extern int
 libwebsocket_rx_flow_control(struct libwebsocket *wsi, int enable);
diff --git a/lib/parsers.c b/lib/parsers.c
index d023e9b..ef68382 100644
--- a/lib/parsers.c
+++ b/lib/parsers.c
@@ -560,7 +560,9 @@
 			n = libwebsocket_write(wsi, (unsigned char *)
 			    &wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
 				    wsi->rx_user_buffer_head, LWS_WRITE_PONG);
-			break;
+			/* ... then just drop it */
+			wsi->rx_user_buffer_head = 0;
+			return 0;
 
 		case LWS_WS_OPCODE_04__PONG:
 			/* keep the statistics... */
@@ -599,6 +601,7 @@
 {
 	int n;
 	unsigned char buf[20 + 4];
+	int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
 
 	switch (wsi->lws_rx_parse_state) {
 	case LWS_RXPS_NEW:
@@ -875,9 +878,10 @@
 		case LWS_WS_OPCODE_04__PONG:
 			/* keep the statistics... */
 			wsi->pings_vs_pongs--;
-			/* ... then just drop it */
-			wsi->rx_user_buffer_head = 0;
-			return 0;
+
+			/* issue it */
+			callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG;
+			break;
 
 		default:
 			break;
@@ -890,8 +894,7 @@
 		 */
 
 		if (wsi->protocol->callback)
-			wsi->protocol->callback(wsi,
-						LWS_CALLBACK_CLIENT_RECEIVE,
+			wsi->protocol->callback(wsi, callback_action,
 						wsi->user_space,
 			  &wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
 						      wsi->rx_user_buffer_head);
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index dfeb373..c3bce43 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -73,7 +73,7 @@
 #define LWS_MAX_HEADER_LEN 4096
 #define LWS_INITIAL_HDR_ALLOC 256
 #define LWS_ADDITIONAL_HDR_ALLOC 64
-#define MAX_USER_RX_BUFFER 512
+#define MAX_USER_RX_BUFFER 4096
 #define MAX_BROADCAST_PAYLOAD 2048
 #define LWS_MAX_PROTOCOLS 10
 
diff --git a/libwebsockets-api-doc.html b/libwebsockets-api-doc.html
index f649d79..d070343 100644
--- a/libwebsockets-api-doc.html
+++ b/libwebsockets-api-doc.html
@@ -78,6 +78,21 @@
 <dd>Protocol whose connections will get callbacks
 </dl>
 <hr>
+<h2>libwebsocket_get_socket_fd - returns the socket file descriptor</h2>
+<i>int</i>
+<b>libwebsocket_get_socket_fd</b>
+(<i>struct libwebsocket *</i> <b>wsi</b>)
+<h3>Arguments</h3>
+<dl>
+<dt><b>wsi</b>
+<dd>Websocket connection instance
+</dl>
+<h3>Description</h3>
+<blockquote>
+<p>
+You will not need this unless you are doing something special
+</blockquote>
+<hr>
 <h2>libwebsocket_rx_flow_control - Enable and disable socket servicing for receieved packets.</h2>
 <i>int</i>
 <b>libwebsocket_rx_flow_control</b>
@@ -394,6 +409,12 @@
 remote client, it can be found at *in and is
 len bytes long
 </blockquote>
+<h3>LWS_CALLBACK_CLIENT_RECEIVE_PONG</h3>
+<blockquote>
+if you elected to see PONG packets,
+they appear with this callback reason.  PONG
+packets only exist in 04+ protocol
+</blockquote>
 <h3>LWS_CALLBACK_CLIENT_RECEIVE</h3>
 <blockquote>
 data has appeared from the server for the
diff --git a/libwebsockets.spec b/libwebsockets.spec
index 31a770f..07215c7 100644
--- a/libwebsockets.spec
+++ b/libwebsockets.spec
@@ -1,6 +1,6 @@
 Name: libwebsockets
 Version: 0.1
-Release: 44.gmaster_f55830db%{?dist}
+Release: 45.gmaster_f1d2113d%{?dist}
 Summary: Websocket Server Library
 
 Group: System
@@ -50,6 +50,7 @@
 /%{_libdir}/libwebsockets.la
 %attr(755,root,root) /usr/share/libwebsockets-test-server
 %attr(755,root,root) /usr/share/libwebsockets-test-client
+%attr(755,root,root) /usr/share/libwebsockets-test-ping
 %doc
 %files devel
 %defattr(-,root,root,-)
diff --git a/test-server/Makefile.am b/test-server/Makefile.am
index c1083a5..c94d313 100644
--- a/test-server/Makefile.am
+++ b/test-server/Makefile.am
@@ -5,10 +5,18 @@
 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@\"
 
+if NOPING
+else
+bin_PROGRAMS+=libwebsockets-test-ping
+libwebsockets_test_ping_SOURCES=test-ping.c
+libwebsockets_test_ping_LDADD=-L../lib -lwebsockets
+libwebsockets_test_ping_CFLAGS:= -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\"
+endif
+
+
 #
 # cook a random test cert and key
 # notice your real cert and key will want to be 0600 permissions
diff --git a/test-server/Makefile.in b/test-server/Makefile.in
index c7555af..d6f0da6 100644
--- a/test-server/Makefile.in
+++ b/test-server/Makefile.in
@@ -35,7 +35,8 @@
 build_triplet = @build@
 host_triplet = @host@
 bin_PROGRAMS = libwebsockets-test-server$(EXEEXT) \
-	libwebsockets-test-client$(EXEEXT)
+	libwebsockets-test-client$(EXEEXT) $(am__EXEEXT_1)
+@NOPING_FALSE@am__append_1 = libwebsockets-test-ping
 subdir = test-server
 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
@@ -46,6 +47,7 @@
 CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
+@NOPING_FALSE@am__EXEEXT_1 = libwebsockets-test-ping$(EXEEXT)
 am__installdirs = "$(DESTDIR)$(bindir)"
 PROGRAMS = $(bin_PROGRAMS)
 am_libwebsockets_test_client_OBJECTS =  \
@@ -57,6 +59,16 @@
 	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
 	$(libwebsockets_test_client_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
 	$(LDFLAGS) -o $@
+am__libwebsockets_test_ping_SOURCES_DIST = test-ping.c
+@NOPING_FALSE@am_libwebsockets_test_ping_OBJECTS =  \
+@NOPING_FALSE@	libwebsockets_test_ping-test-ping.$(OBJEXT)
+libwebsockets_test_ping_OBJECTS =  \
+	$(am_libwebsockets_test_ping_OBJECTS)
+libwebsockets_test_ping_DEPENDENCIES =
+libwebsockets_test_ping_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(libwebsockets_test_ping_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+	$(LDFLAGS) -o $@
 am_libwebsockets_test_server_OBJECTS =  \
 	libwebsockets_test_server-test-server.$(OBJEXT)
 libwebsockets_test_server_OBJECTS =  \
@@ -80,8 +92,10 @@
 	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
 	$(LDFLAGS) -o $@
 SOURCES = $(libwebsockets_test_client_SOURCES) \
+	$(libwebsockets_test_ping_SOURCES) \
 	$(libwebsockets_test_server_SOURCES)
 DIST_SOURCES = $(libwebsockets_test_client_SOURCES) \
+	$(am__libwebsockets_test_ping_SOURCES_DIST) \
 	$(libwebsockets_test_server_SOURCES)
 ETAGS = etags
 CTAGS = ctags
@@ -206,6 +220,9 @@
 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@\"
+@NOPING_FALSE@libwebsockets_test_ping_SOURCES = test-ping.c
+@NOPING_FALSE@libwebsockets_test_ping_LDADD = -L../lib -lwebsockets
+@NOPING_FALSE@libwebsockets_test_ping_CFLAGS := -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\"
 all: all-am
 
 .SUFFIXES:
@@ -286,6 +303,9 @@
 libwebsockets-test-client$(EXEEXT): $(libwebsockets_test_client_OBJECTS) $(libwebsockets_test_client_DEPENDENCIES) 
 	@rm -f libwebsockets-test-client$(EXEEXT)
 	$(libwebsockets_test_client_LINK) $(libwebsockets_test_client_OBJECTS) $(libwebsockets_test_client_LDADD) $(LIBS)
+libwebsockets-test-ping$(EXEEXT): $(libwebsockets_test_ping_OBJECTS) $(libwebsockets_test_ping_DEPENDENCIES) 
+	@rm -f libwebsockets-test-ping$(EXEEXT)
+	$(libwebsockets_test_ping_LINK) $(libwebsockets_test_ping_OBJECTS) $(libwebsockets_test_ping_LDADD) $(LIBS)
 libwebsockets-test-server$(EXEEXT): $(libwebsockets_test_server_OBJECTS) $(libwebsockets_test_server_DEPENDENCIES) 
 	@rm -f libwebsockets-test-server$(EXEEXT)
 	$(libwebsockets_test_server_LINK) $(libwebsockets_test_server_OBJECTS) $(libwebsockets_test_server_LDADD) $(LIBS)
@@ -297,6 +317,7 @@
 	-rm -f *.tab.c
 
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_test_client-test-client.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_test_ping-test-ping.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_test_server-test-server.Po@am__quote@
 
 .c.o:
@@ -334,6 +355,20 @@
 @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_ping-test-ping.o: test-ping.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_ping_CFLAGS) $(CFLAGS) -MT libwebsockets_test_ping-test-ping.o -MD -MP -MF $(DEPDIR)/libwebsockets_test_ping-test-ping.Tpo -c -o libwebsockets_test_ping-test-ping.o `test -f 'test-ping.c' || echo '$(srcdir)/'`test-ping.c
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libwebsockets_test_ping-test-ping.Tpo $(DEPDIR)/libwebsockets_test_ping-test-ping.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-ping.c' object='libwebsockets_test_ping-test-ping.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_ping_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_ping-test-ping.o `test -f 'test-ping.c' || echo '$(srcdir)/'`test-ping.c
+
+libwebsockets_test_ping-test-ping.obj: test-ping.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_ping_CFLAGS) $(CFLAGS) -MT libwebsockets_test_ping-test-ping.obj -MD -MP -MF $(DEPDIR)/libwebsockets_test_ping-test-ping.Tpo -c -o libwebsockets_test_ping-test-ping.obj `if test -f 'test-ping.c'; then $(CYGPATH_W) 'test-ping.c'; else $(CYGPATH_W) '$(srcdir)/test-ping.c'; fi`
+@am__fastdepCC_TRUE@	$(am__mv) $(DEPDIR)/libwebsockets_test_ping-test-ping.Tpo $(DEPDIR)/libwebsockets_test_ping-test-ping.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='test-ping.c' object='libwebsockets_test_ping-test-ping.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_ping_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_ping-test-ping.obj `if test -f 'test-ping.c'; then $(CYGPATH_W) 'test-ping.c'; else $(CYGPATH_W) '$(srcdir)/test-ping.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
diff --git a/test-server/test-ping.c b/test-server/test-ping.c
new file mode 100644
index 0000000..6911923
--- /dev/null
+++ b/test-server/test-ping.c
@@ -0,0 +1,462 @@
+/*
+ * libwebsockets-test-ping - libwebsockets floodping
+ *
+ * Copyright (C) 2011 Andy Green <andy@warmcat.com>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <sys/ioctl.h>
+
+#include "../lib/libwebsockets.h"
+#include <poll.h>
+
+/*
+ * this is specified in the 04 standard, control frames can only have small
+ * payload length styles
+ */
+#define MAX_PING_PAYLOAD 125
+#define MAX_MIRROR_PAYLOAD 4096
+
+static unsigned int interval_us = 1000000;
+static unsigned int size = 64;
+static int flood = 0;
+static const char *address;
+static unsigned char pingbuf[LWS_SEND_BUFFER_PRE_PADDING + MAX_MIRROR_PAYLOAD +
+						  LWS_SEND_BUFFER_POST_PADDING];
+static unsigned long oldus = 0;
+static unsigned long ping_index = 1;
+static char *hname = "(unknown)";
+static unsigned long rx_count = 0;
+static unsigned long started;
+
+static unsigned long rtt_min = 100000000;
+static unsigned long rtt_max = 0;
+static unsigned long rtt_avg = 0;
+static int screen_width = 80;
+static int use_mirror = 0;
+
+struct ping {
+	unsigned long issue_timestamp;
+	unsigned long index;
+	unsigned int seen;
+};
+
+#define PING_RINGBUFFER_SIZE 256
+
+struct ping ringbuffer[PING_RINGBUFFER_SIZE];
+int ringbuffer_head;
+int ringbuffer_tail;
+
+/*
+ * uses the ping pong protocol features to provide an equivalent for the
+ * ping utility for 04+ websockets
+ */
+
+enum demo_protocols {
+
+	PROTOCOL_LWS_MIRROR,
+
+	/* always last */
+	DEMO_PROTOCOL_COUNT
+};
+
+
+static int
+callback_lws_mirror(struct libwebsocket *wsi,
+			enum libwebsocket_callback_reasons reason,
+					       void *user, void *in, size_t len)
+{
+	struct timeval tv;
+	unsigned char *p;
+	int shift;
+	unsigned long l;
+	unsigned long iv;
+	int n;
+	int match = 0;
+
+	switch (reason) {
+	case LWS_CALLBACK_CLIENT_ESTABLISHED:
+
+		/*
+		 * start the ball rolling,
+		 * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
+		 */
+
+		libwebsocket_callback_on_writable(wsi);
+		break;
+
+	case LWS_CALLBACK_CLIENT_RECEIVE:
+	case LWS_CALLBACK_CLIENT_RECEIVE_PONG:
+		gettimeofday(&tv, NULL);
+		iv = (tv.tv_sec * 1000000) + tv.tv_usec;
+
+		rx_count++;
+
+		shift = 56;
+		p = in;
+		l = 0;
+
+		while (shift >= 0) {
+			l |= (*p++) << shift;
+			shift -= 8;
+		}
+
+		/* find it in the ringbuffer, look backwards from head */
+		n = ringbuffer_head;
+		while (!match) {
+
+			if (ringbuffer[n].index == l) {
+				ringbuffer[n].seen++;
+				match = 1;
+				continue;
+			}
+
+			if (n == ringbuffer_tail) {
+				match = -1;
+				continue;
+			}
+			
+			if (n == 0)
+				n = PING_RINGBUFFER_SIZE - 1;
+			else
+				n--;		
+		}
+
+		if (match < 1) {
+			
+			if (!flood)
+				fprintf(stderr, "%d bytes from %s: req=%ld "
+					"time=(unknown)\n", (int)len, address, l);
+			else
+				fprintf(stderr, "\b \b");
+
+			break;
+		}
+
+		if (ringbuffer[n].seen > 1)
+			fprintf(stderr, "DUP! ");
+
+		if ((iv - ringbuffer[n].issue_timestamp) < rtt_min)
+			rtt_min = iv - ringbuffer[n].issue_timestamp;
+
+		if ((iv - ringbuffer[n].issue_timestamp) > rtt_max)
+			rtt_max = iv - ringbuffer[n].issue_timestamp;
+
+		rtt_avg += iv - ringbuffer[n].issue_timestamp;
+
+	
+		if (!flood)
+			fprintf(stderr, "%d bytes from %s: req=%ld "
+					"time=%lu.%lums\n", (int)len, address, l,
+					(iv - ringbuffer[n].issue_timestamp) / 1000,
+					((iv - ringbuffer[n].issue_timestamp) / 100) % 10
+					);
+		else
+			fprintf(stderr, "\b \b");
+		break;
+
+	case LWS_CALLBACK_CLIENT_WRITEABLE:
+
+		shift = 56;
+		p = &pingbuf[LWS_SEND_BUFFER_PRE_PADDING];
+
+		while (shift >= 0) {
+			*p++ = ping_index >> shift;
+			shift -= 8;
+		}
+	
+		gettimeofday(&tv, NULL);
+		
+		ringbuffer[ringbuffer_head].issue_timestamp =
+					     (tv.tv_sec * 1000000) + tv.tv_usec;
+		ringbuffer[ringbuffer_head].index = ping_index++;
+		ringbuffer[ringbuffer_head].seen = 0;
+
+		if (ringbuffer_head == PING_RINGBUFFER_SIZE - 1)
+			ringbuffer_head = 0;
+		else
+			ringbuffer_head++;
+
+		/* snip any re-used tail so we keep the whole buffer length */
+
+		if (ringbuffer_tail == ringbuffer_head) {
+			if (ringbuffer_tail == PING_RINGBUFFER_SIZE - 1)
+				ringbuffer_tail = 0;
+			else
+				ringbuffer_tail++;
+		}
+
+		if (use_mirror)
+			libwebsocket_write(wsi,
+				&pingbuf[LWS_SEND_BUFFER_PRE_PADDING],
+							size, LWS_WRITE_BINARY);
+		else
+			libwebsocket_write(wsi,
+				&pingbuf[LWS_SEND_BUFFER_PRE_PADDING],
+							  size, LWS_WRITE_PING);
+
+		if (flood && (ping_index - rx_count) < (screen_width - 1))
+			fprintf(stderr, ".");
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+
+/* list of supported protocols and callbacks */
+
+static struct libwebsocket_protocols protocols[] = {
+
+	[PROTOCOL_LWS_MIRROR] = {
+		.name = "lws-mirror-protocol",
+		.callback = callback_lws_mirror,
+	},
+	[DEMO_PROTOCOL_COUNT] = {  /* end of list */
+		.callback = NULL
+	}
+};
+
+static struct option options[] = {
+	{ "help",	no_argument,		NULL, 'h' },
+	{ "port",	required_argument,	NULL, 'p' },
+	{ "ssl",	no_argument,		NULL, 't' },
+	{ "interval",	required_argument,	NULL, 'i' },
+	{ "size",	required_argument,	NULL, 's' },
+	{ "protocol",	required_argument,	NULL, 'n' },
+	{ "flood",	no_argument,		NULL, 'f' },
+	{ "mirror",	no_argument,		NULL, 'm' },
+	{ NULL, 0, 0, 0 }
+};
+
+
+static void
+signal_handler(int sig, siginfo_t *si, void *v)
+{
+	struct timeval tv;
+	unsigned long l;
+
+	gettimeofday(&tv, NULL);
+	l = (tv.tv_sec * 1000000) + tv.tv_usec;
+
+	fprintf(stderr, "\n--- %s websocket ping statistics ---\n"
+		"%lu packets transmitted, %lu received, %lu%% packet loss, time %ldms\n"
+		"rtt min/avg/max = %0.3f/%0.3f/%0.3f ms\n",
+		hname, ping_index - 1, rx_count,
+		(((ping_index - 1) - rx_count) * 100) / (ping_index - 1),
+		(l - started) / 1000,
+		((double)rtt_min) / 1000.0,
+		((double)rtt_avg / rx_count) / 1000.0,
+		((double)rtt_max) / 1000.0
+	);
+
+	exit(0);
+}
+
+
+int main(int argc, char **argv)
+{
+	int n = 0;
+	int port = 7681;
+	int use_ssl = 0;
+	struct libwebsocket_context *context;
+	struct libwebsocket *wsi_mirror;
+	char protocol_name[256];
+	unsigned int len;
+	struct sockaddr_in sin;
+	struct hostent *host;
+	struct hostent *host1;
+	char ip[30];
+	char *p;
+	struct sigaction sa;
+	struct timeval tv;
+	struct winsize w;
+                
+	if (argc < 2)
+		goto usage;
+
+	address = argv[1];
+	optind++;
+
+	while (n >= 0) {
+		n = getopt_long(argc, argv, "hmfts:n:i:p:", options, NULL);
+		if (n < 0)
+			continue;
+		switch (n) {
+		case 'm':
+			use_mirror = 1;
+			break;
+		case 't':
+			use_ssl = 2; /* 2 = allow selfsigned */
+			break;
+		case 'p':
+			port = atoi(optarg);
+			break;
+		case 'n':
+			strncpy(protocol_name, optarg, sizeof protocol_name);
+			protocol_name[(sizeof protocol_name) -1] = '\0';
+			protocols[PROTOCOL_LWS_MIRROR].name = protocol_name;
+			break;
+		case 'i':
+			interval_us = 1000000.0 * atof(optarg);
+			break;
+		case 's':
+			size = atoi(optarg);
+			break;
+		case 'f':
+			flood = 1;
+			break;
+		case 'h':
+			goto usage;
+		}
+	}
+
+	if (!use_mirror) {
+		if (size > MAX_PING_PAYLOAD) {
+			fprintf(stderr, "Max ping opcode payload size %d\n",
+							      MAX_PING_PAYLOAD);
+			return 1;
+		}
+	} else {
+		if (size > MAX_MIRROR_PAYLOAD) {
+			fprintf(stderr, "Max mirror payload size %d\n",
+							    MAX_MIRROR_PAYLOAD);
+			return 1;
+		}
+	}
+
+
+        if (isatty(STDOUT_FILENO))
+                if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1)
+                        if (w.ws_col > 0)
+                                screen_width = w.ws_col;
+
+	context = libwebsocket_create_context(CONTEXT_PORT_NO_LISTEN,
+						 protocols, NULL, NULL, -1, -1);
+	if (context == NULL) {
+		fprintf(stderr, "Creating libwebsocket context failed\n");
+		return 1;
+	}
+
+	/* create a client websocket using dumb increment protocol */
+
+	wsi_mirror = libwebsocket_client_connect(context, address, port, use_ssl,
+					"/", "http://host", "origin",
+				       protocols[PROTOCOL_LWS_MIRROR].name);
+
+	if (wsi_mirror == NULL) {
+		fprintf(stderr, "libwebsocket connect failed\n");
+		return -1;
+	}
+
+	strcpy(ip, "(unknown)");
+	len = sizeof sin;
+	if (getpeername(libwebsocket_get_socket_fd(wsi_mirror),
+					    (struct sockaddr *) &sin, &len) < 0)
+		perror("getpeername");
+	else {
+		host = gethostbyaddr((char *) &sin.sin_addr,
+				    sizeof sin.sin_addr,
+				    AF_INET);
+		if (host == NULL)
+			perror("gethostbyaddr");
+		else {
+			hname = host->h_name;
+
+			host1 = gethostbyname(hname);
+			if (host1 != NULL) {
+				p = (char *)host1;
+				n = 0;
+				while (p != NULL) {
+					p = host1->h_addr_list[n++];
+					if (p == NULL)
+						continue;
+					if (host1->h_addrtype != AF_INET)
+						continue;
+
+					sprintf(ip, "%d.%d.%d.%d",
+							p[0], p[1], p[2], p[3]);
+					p = NULL;
+				}
+			}
+		}
+	}
+
+	fprintf(stderr, "Websocket PING %s (%s) %d bytes of data.\n",
+							       hname, ip, size);
+
+	/* set the ^C handler */
+
+	sa.sa_sigaction = signal_handler;
+	sa.sa_flags = SA_SIGINFO;
+	sigemptyset(&sa.sa_mask);
+	sigaction(SIGINT, &sa, NULL);
+
+	gettimeofday(&tv, NULL);
+	started = (tv.tv_sec * 1000000) + tv.tv_usec;
+
+	/* service loop */
+
+	n = 0;
+	while (n >= 0) {
+		unsigned long l;
+
+		gettimeofday(&tv, NULL);
+
+		l = (tv.tv_sec * 1000000) + tv.tv_usec;
+		if ((l - oldus) > interval_us) {
+			libwebsocket_callback_on_writable(wsi_mirror);
+			oldus = l;
+		}
+
+		if (!interval_us)
+			n = libwebsocket_service(context, 0);
+		else
+			n = libwebsocket_service(context, 1);
+	}
+
+	libwebsocket_client_close(wsi_mirror);
+	libwebsocket_context_destroy(context);
+
+	return 0;
+
+usage:
+	fprintf(stderr, "Usage: libwebsockets-test-ping "
+					     "<server address> [--port=<p>] "
+					     "[--ssl] [--interval=<float sec>] "
+					     "[--size=<bytes>] "
+					     "[--protocol=<protocolname>] "
+					     "[--mirror] "
+					     "\n");
+	return 1;
+}