introduce-test-ping-app.patch

Signed-off-by: Andy Green <andy@warmcat.com>
This commit is contained in:
Andy Green 2011-01-27 20:06:03 +00:00
parent f1d2113d6e
commit a6cbece1ac
12 changed files with 653 additions and 10 deletions

View file

@ -37,6 +37,11 @@ There are a couple of other possible configure options
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 @@ 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.
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
---------------------------

28
configure vendored
View file

@ -616,6 +616,8 @@ ac_subst_vars='am__EXEEXT_FALSE
am__EXEEXT_TRUE
LTLIBOBJS
LIBOBJS
NOPING_FALSE
NOPING_TRUE
clientcertdir
LIBCRYPTO_FALSE
LIBCRYPTO_TRUE
@ -741,6 +743,7 @@ enable_openssl
enable_nofork
enable_libcrypto
with_client_cert_dir
enable_noping
'
ac_precious_vars='build_alias
host_alias
@ -1386,6 +1389,7 @@ Optional Features:
--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 @@ fi
#
#
#
# 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 @@ if test -z "${LIBCRYPTO_TRUE}" && test -z "${LIBCRYPTO_FALSE}"; then
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

View file

@ -73,6 +73,17 @@ AC_SUBST([clientcertdir])
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])

View file

@ -514,6 +514,21 @@ libwebsocket_callback_on_writable_all_protocol(
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.

View file

@ -30,6 +30,7 @@ enum libwebsocket_callback_reasons {
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 @@ struct libwebsocket_context;
* 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 @@ extern int
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);

View file

@ -560,7 +560,9 @@ spill:
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 libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c)
{
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 @@ spill:
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 @@ spill:
*/
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);

View file

@ -73,7 +73,7 @@ static inline void debug(const char *format, ...)
#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

View file

@ -78,6 +78,21 @@ nothing is pending, or as soon as it services whatever was pending.
<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 @@ 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_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

View file

@ -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 @@ rm -rf $RPM_BUILD_ROOT
/%{_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,-)

View file

@ -5,10 +5,18 @@ 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@\"
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

View file

@ -35,7 +35,8 @@ POST_UNINSTALL = :
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 @@ mkinstalldirs = $(install_sh) -d
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 @@ 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_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 @@ LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
--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_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@\"
@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 @@ clean-binPROGRAMS:
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 @@ distclean-compile:
-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 @@ libwebsockets_test_client-test-client.obj: test-client.c
@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

462
test-server/test-ping.c Normal file
View file

@ -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;
}