-SUBDIRS=lib test-server
+SUBDIRS=lib test-server
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
+clientcertdir = @clientcertdir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUBDIRS = lib test-server
+SUBDIRS = lib test-server
all: config.h
$(MAKE) $(AM_MAKEFLAGS) all-recursive
to disable the built-in ones and force use
of the libcrypto (part of openssl) ones.
+--with-client-cert-dir=dir tells the client ssl support where to
+ look for trust certificates to validate
+ the remote certificate against.
+
Testing server with a browser
-----------------------------
script in there on the browser to open a websocket connection.
Incrementing numbers should appear in the browser display.
-Using SSL
----------
-
-The client side operation does not support SSL yet, but the
-server side does.
+Using SSL on the server side
+----------------------------
To test it using SSL/WSS, just run the test server with
test-server.c is all that is needed to use libwebsockets for
serving both the script html over http and websockets.
+
Forkless operation
------------------
configure option --nofork and simply call libwebsocket_service()
from your own main loop as shown in the test app sources.
+
Testing websocket client support
--------------------------------
same time you will be able to see the circles being drawn.
+Testing SSL on the client side
+------------------------------
+
+To test SSL/WSS client action, just run the client test with
+
+$ libwebsockets-test-client localhost --ssl
+
+By default the client test applet is set to accept selfsigned
+certificates used by the test server, this is indicated by the
+use_ssl var being set to 2. Set it to 1 to reject any server
+certificate that it doesn't have a trusted CA cert for.
+
+
Websocket version supported
---------------------------
am__EXEEXT_TRUE
LTLIBOBJS
LIBOBJS
+clientcertdir
LIBCRYPTO_FALSE
LIBCRYPTO_TRUE
CPP
enable_openssl
enable_nofork
enable_libcrypto
+with_client_cert_dir
'
ac_precious_vars='build_alias
host_alias
--disable-libtool-lock avoid locking (might break parallel builds)
--enable-openssl Enables https support and needs openssl libs
--enable-nofork Disables fork-related options
- --enable-libcrypto Use libcrypto MD5 and SHA1 implemntations
+ --enable-libcrypto Use libcrypto MD5 and SHA1 implementations
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
--with-gnu-ld assume the C compiler uses GNU ld [default=no]
--with-sysroot=DIR Search for dependent libraries within DIR
(or the compiler's sysroot if not specified).
+ --with-client-cert-dir directory containing client certs, defaults to
+ /etc/pki/tls/certs/
Some influential environment variables:
CC C compiler command
+
#
#
#
+#
+#
+#
+
+# Check whether --with-client-cert-dir was given.
+if test "${with_client_cert_dir+set}" = set; then :
+ withval=$with_client_cert_dir; clientcertdir=$withval
+else
+ clientcertdir=/etc/pki/tls/certs/
+
+fi
+
+
+
+
+
+
# Checks for header files.
for ac_header in fcntl.h netinet/in.h stdlib.h string.h sys/socket.h unistd.h
AC_PROG_MAKE_SET
AC_CONFIG_MACRO_DIR([m4])
+
#
#
#
#
#
AC_ARG_ENABLE(libcrypto,
- [ --enable-libcrypto Use libcrypto MD5 and SHA1 implemntations],
+ [ --enable-libcrypto Use libcrypto MD5 and SHA1 implementations],
[ libcrypto=yes
])
AM_CONDITIONAL(LIBCRYPTO, test x$libcrypto = xyes)
+#
+#
+#
+AC_ARG_WITH([client-cert-dir],
+[AS_HELP_STRING([--with-client-cert-dir],[directory containing client certs, defaults to /etc/pki/tls/certs/])],
+[clientcertdir=$withval],
+[clientcertdir=/etc/pki/tls/certs/]
+)
+AC_SUBST([clientcertdir])
+
+AC_SUBST([CFLAGS])
+
+
# Checks for header files.
AC_CHECK_HEADERS([fcntl.h netinet/in.h stdlib.h string.h sys/socket.h unistd.h])
dist_libwebsockets_la_SOURCES += md5.c sha-1.c
endif
-libwebsockets_la_CFLAGS=-Wall -Werror -std=gnu99 -pedantic -rdynamic -fPIC -c
+libwebsockets_la_CFLAGS:=-rdynamic -fPIC -Wall -Werror -std=gnu99 -pedantic -c -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\"
libwebsockets_la_LDFLAGS=-version-info 0:2
all-local:
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
+clientcertdir = @clientcertdir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dist_libwebsockets_la_SOURCES = libwebsockets.c handshake.c parsers.c \
libwebsockets.h base64-decode.c client-handshake.c \
private-libwebsockets.h $(am__append_1)
-libwebsockets_la_CFLAGS = -Wall -Werror -std=gnu99 -pedantic -rdynamic -fPIC -c
+libwebsockets_la_CFLAGS := -rdynamic -fPIC -Wall -Werror -std=gnu99 -pedantic -c -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\"
libwebsockets_la_LDFLAGS = -version-info 0:2
all: all-am
void
strtolower(char *s)
{
- while (*s)
- *s++ = tolower(*s);
+ while (*s) {
+ *s = tolower(*s);
+ s++;
+ }
}
void
/* shut down reasonably cleanly */
#ifdef LWS_OPENSSL_SUPPORT
- if (use_ssl) {
+ if (wsi->ssl) {
n = SSL_get_fd(wsi->ssl);
SSL_shutdown(wsi->ssl);
close(n);
#endif
}
+
+/**
+ * libwebsocket_client_connect() - Connect to another websocket server
+ * @this: Websocket context
+ * @address: Remote server address, eg, "myserver.com"
+ * @port: Port to connect to on the remote server, eg, 80
+ * @ssl_connection: 0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self
+ * signed certs
+ * @path: Websocket path on server
+ * @host: Hostname on server
+ * @origin: Socket origin name
+ * @protocol: Comma-separated list of protocols being asked for from
+ * the server, or just one. The server will pick the one it
+ * likes best.
+ *
+ * This function creates a connection to a remote server
+ */
+
struct libwebsocket *
-libwebsocket_client_connect(struct libwebsocket_context *clients,
+libwebsocket_client_connect(struct libwebsocket_context *this,
const char *address,
int port,
+ int ssl_connection,
const char *path,
const char *host,
const char *origin,
int okay = 0;
struct libwebsocket *wsi;
int n;
+#ifdef LWS_OPENSSL_SUPPORT
+ char ssl_err_buf[512];
+#else
+ if (ssl_connection) {
+ fprintf(stderr, "libwebsockets not configured for ssl\n");
+ return NULL;
+ }
+#endif
wsi = malloc(sizeof(struct libwebsocket));
- if (wsi == NULL)
+ if (wsi == NULL) {
+ fprintf(stderr, "Out of memort allocing new connection\n");
return NULL;
+ }
- clients->wsi[clients->fds_count] = wsi;
+ this->wsi[this->fds_count] = wsi;
wsi->ietf_spec_revision = 4;
wsi->name_buffer_pos = 0;
if (connect(wsi->sock, (struct sockaddr *)&server_addr,
sizeof(struct sockaddr)) == -1) {
- fprintf(stderr, "Connect failed");
+ fprintf(stderr, "Connect failed\n");
goto bail1;
}
+#ifdef LWS_OPENSSL_SUPPORT
+ if (ssl_connection) {
+
+ wsi->ssl = SSL_new(this->ssl_client_ctx);
+ wsi->client_bio = BIO_new_socket(wsi->sock, BIO_NOCLOSE);
+ SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
+
+ if (SSL_connect(wsi->ssl) <= 0) {
+ fprintf(stderr, "SSL connect error %s\n",
+ ERR_error_string(ERR_get_error(), ssl_err_buf));
+ goto bail2;
+ }
+
+ n = SSL_get_verify_result(wsi->ssl);
+ if (n != X509_V_OK) {
+ if (n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT &&
+ ssl_connection == 2)
+ goto cert_okay;
+
+ fprintf(stderr, "server's cert didn't look good %d\n", n);
+ goto bail2;
+ }
+ } else
+ wsi->ssl = NULL;
+cert_okay:
+#endif
/*
* create the random key
*/
/* send our request to the server */
- n = send(wsi->sock, pkt, p - pkt, 0);
+#ifdef LWS_OPENSSL_SUPPORT
+ if (ssl_connection)
+ n = SSL_write(wsi->ssl, pkt, p - pkt);
+ else
+#endif
+ n = send(wsi->sock, pkt, p - pkt, 0);
+
+ if (n < 0) {
+ fprintf(stderr, "ERROR writing to client socket\n");
+ goto bail2;
+ }
wsi->parser_state = WSI_TOKEN_NAME_PART;
* Sec-WebSocket-Protocol: chat
*/
- len = recv(wsi->sock, pkt, sizeof pkt, 0);
+#ifdef LWS_OPENSSL_SUPPORT
+ if (ssl_connection)
+ len = SSL_read(wsi->ssl, pkt, sizeof pkt);
+ else
+#endif
+ len = recv(wsi->sock, pkt, sizeof pkt, 0);
+
if (len < 0) {
fprintf(stderr, "libwebsocket_client_handshake read error\n");
goto bail2;
if (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len) {
/* no protocol name to work from, default to first protocol */
- wsi->protocol = &clients->protocols[0];
+ wsi->protocol = &this->protocols[0];
goto check_accept;
}
*/
n = 0;
wsi->protocol = NULL;
- while (clients->protocols[n].callback) {
+ while (this->protocols[n].callback) {
if (strcmp(wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
- clients->protocols[n].name) == 0)
- wsi->protocol = &clients->protocols[n];
+ this->protocols[n].name) == 0)
+ wsi->protocol = &this->protocols[n];
n++;
}
/* okay he is good to go */
- clients->fds[clients->fds_count].fd = wsi->sock;
- clients->fds[clients->fds_count].revents = 0;
- clients->fds[clients->fds_count++].events = POLLIN;
+ this->fds[this->fds_count].fd = wsi->sock;
+ this->fds[this->fds_count].revents = 0;
+ this->fds[this->fds_count++].events = POLLIN;
wsi->state = WSI_STATE_ESTABLISHED;
wsi->client_mode = 1;
fprintf(stderr, "handshake OK for protocol %s\n", wsi->protocol->name);
+ /* call him back to inform him he is up */
+
+ wsi->protocol->callback(wsi,
+ LWS_CALLBACK_CLIENT_ESTABLISHED,
+ wsi->user_space,
+ NULL, 0);
return wsi;
strcpy(p, wsi->utf8_token[WSI_TOKEN_ORIGIN].token);
p += wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len;
#ifdef LWS_OPENSSL_SUPPORT
- if (use_ssl) {
+ if (wsi->ssl) {
strcpy(p, "\x0d\x0aSec-WebSocket-Location: wss://");
p += strlen("\x0d\x0aSec-WebSocket-Location: wss://");
} else {
#include "private-libwebsockets.h"
-#ifdef LWS_OPENSSL_SUPPORT
-SSL_CTX *ssl_ctx;
-int use_ssl;
-#endif
-
void
libwebsocket_close_and_free_session(struct libwebsocket *wsi)
{
/* fprintf(stderr, "closing fd=%d\n", wsi->sock); */
#ifdef LWS_OPENSSL_SUPPORT
- if (use_ssl) {
+ if (wsi->ssl) {
n = SSL_get_fd(wsi->ssl);
SSL_shutdown(wsi->ssl);
close(n);
goto nuke_this;
}
+ /* the guy requested a callback when it was OK to write */
+
+ if ((unsigned long)this->wsi[client] > LWS_MAX_PROTOCOLS &&
+ this->fds[client].revents & POLLOUT) {
+
+ this->fds[client].events &= ~POLLOUT;
+
+ this->wsi[client]->protocol->callback(this->wsi[client],
+ LWS_CALLBACK_CLIENT_WRITEABLE,
+ this->wsi[client]->user_space,
+ NULL, 0);
+ }
+
/* any incoming data ready? */
if (!(this->fds[client].revents & POLLIN))
}
#ifdef LWS_OPENSSL_SUPPORT
- if (this->use_ssl)
+ if (this->wsi[client]->ssl)
n = SSL_read(this->wsi[client]->ssl, buf, sizeof buf);
else
#endif
libwebsocket_close_and_free_session(this->wsi[client]);
#ifdef LWS_OPENSSL_SUPPORT
- if (ssl_ctx)
- SSL_CTX_free(ssl_ctx);
+ if (this->ssl_ctx)
+ SSL_CTX_free(this->ssl_ctx);
#endif
if (this)
}
#ifdef LWS_OPENSSL_SUPPORT
+ this->wsi[this->fds_count]->ssl = NULL;
+
if (this->use_ssl) {
- this->wsi[this->fds_count]->ssl = SSL_new(ssl_ctx);
+ this->wsi[this->fds_count]->ssl =
+ SSL_new(this->ssl_ctx);
if (this->wsi[this->fds_count]->ssl == NULL) {
fprintf(stderr, "SSL_new failed: %s\n",
ERR_error_string(SSL_get_error(
close(this->fds[0].fd);
#ifdef LWS_OPENSSL_SUPPORT
- SSL_CTX_free(ssl_ctx);
+ if (this->ssl_ctx)
+ SSL_CTX_free(this->ssl_ctx);
#endif
if (this)
return 1;
}
+/**
+ * libwebsocket_callback_on_writable() - Request a callback when this socket
+ * becomes able to be written to without
+ * blocking
+ *
+ * @wsi: Websocket connection instance to get callback for
+ */
+
+int
+libwebsocket_callback_on_writable(struct libwebsocket *wsi)
+{
+ struct libwebsocket_context *this = wsi->protocol->owning_server;
+ int n;
+
+ for (n = this->count_protocols + 1; n < this->fds_count; n++)
+ if (this->wsi[n] == wsi) {
+ this->fds[n].events |= POLLOUT;
+ return 0;
+ }
+
+ fprintf(stderr, "libwebsocket_callback_on_writable "
+ "didn't find socket\n");
+ return 1;
+}
+
+/**
+ * libwebsocket_callback_on_writable_all_protocol() - Request a callback for
+ * all connections using the given protocol when it
+ * becomes possible to write to each socket without
+ * blocking in turn.
+ *
+ * @protocol: Protocol whose connections will get callbacks
+ */
+
+int
+libwebsocket_callback_on_writable_all_protocol(
+ const struct libwebsocket_protocols *protocol)
+{
+ struct libwebsocket_context *this = protocol->owning_server;
+ int n;
+
+ for (n = this->count_protocols + 1; n < this->fds_count; n++)
+ if ((unsigned long)this->wsi[n] > LWS_MAX_PROTOCOLS)
+ if (this->wsi[n]->protocol == protocol)
+ this->fds[n].events |= POLLOUT;
+
+ return 0;
+}
+
+/**
+ * libwebsocket_rx_flow_control() - Enable and disable socket servicing for
+ * receieved packets.
+ *
+ * If the output side of a server process becomes choked, this allows flow
+ * control for the input side.
+ *
+ * @wsi: Websocket connection instance to get callback for
+ * @enable: 0 = disable read servicing for this connection, 1 = enable
+ */
+
+int
+libwebsocket_rx_flow_control(struct libwebsocket *wsi, int enable)
+{
+ struct libwebsocket_context *this = wsi->protocol->owning_server;
+ int n;
+
+ for (n = this->count_protocols + 1; n < this->fds_count; n++)
+ if (this->wsi[n] == wsi) {
+ if (enable)
+ this->fds[n].events |= POLLIN;
+ else
+ this->fds[n].events &= ~POLLIN;
+
+ return 0;
+ }
+
+ fprintf(stderr, "libwebsocket_callback_on_writable "
+ "unable to find socket\n");
+ return 1;
+}
+
+static void sigpipe_handler(int x)
+{
+}
+
/**
* libwebsocket_create_context() - Create the websocket handler
#ifdef LWS_OPENSSL_SUPPORT
SSL_METHOD *method;
char ssl_err_buf[512];
+#endif
- use_ssl = ssl_cert_filepath != NULL && ssl_private_key_filepath != NULL;
- if (use_ssl)
- fprintf(stderr, " Compiled with SSL support, using it\n");
- else
- fprintf(stderr, " Compiled with SSL support, not using it\n");
-
-#else
- if (ssl_cert_filepath != NULL && ssl_private_key_filepath != NULL) {
- fprintf(stderr, " Not compiled for OpenSSl support!\n");
+ this = malloc(sizeof(struct libwebsocket_context));
+ if (!this) {
+ fprintf(stderr, "No memory for websocket context\n");
return NULL;
}
- fprintf(stderr, " Compiled without SSL support, serving unencrypted\n");
+ this->protocols = protocols;
+ this->listen_port = port;
+
+ if (port) {
+
+#ifdef LWS_OPENSSL_SUPPORT
+ this->use_ssl = ssl_cert_filepath != NULL &&
+ ssl_private_key_filepath != NULL;
+ if (this->use_ssl)
+ fprintf(stderr, " Compiled with SSL support, "
+ "using it\n");
+ else
+ fprintf(stderr, " Compiled with SSL support, "
+ "not using it\n");
+
+#else
+ if (ssl_cert_filepath != NULL &&
+ ssl_private_key_filepath != NULL) {
+ fprintf(stderr, " Not compiled for OpenSSl support!\n");
+ return NULL;
+ }
+ fprintf(stderr, " Compiled without SSL support, "
+ "serving unencrypted\n");
#endif
+ }
+
+ /* ignore SIGPIPE */
+
+ signal(SIGPIPE, sigpipe_handler);
+
#ifdef LWS_OPENSSL_SUPPORT
- if (use_ssl) {
- SSL_library_init();
- OpenSSL_add_all_algorithms();
- SSL_load_error_strings();
+ /* basic openssl init */
- /*
- * Firefox insists on SSLv23 not SSLv3
- * Konq disables SSLv2 by default now, SSLv23 works
- */
+ SSL_library_init();
+
+ OpenSSL_add_all_algorithms();
+ SSL_load_error_strings();
+
+ /*
+ * Firefox insists on SSLv23 not SSLv3
+ * Konq disables SSLv2 by default now, SSLv23 works
+ */
+
+ method = (SSL_METHOD *)SSLv23_server_method();
+ if (!method) {
+ fprintf(stderr, "problem creating ssl method: %s\n",
+ ERR_error_string(ERR_get_error(), ssl_err_buf));
+ return NULL;
+ }
+ this->ssl_ctx = SSL_CTX_new(method); /* create context */
+ if (!this->ssl_ctx) {
+ fprintf(stderr, "problem creating ssl context: %s\n",
+ ERR_error_string(ERR_get_error(), ssl_err_buf));
+ return NULL;
+ }
+
+ /* client context */
+
+ method = (SSL_METHOD *)SSLv23_client_method();
+ if (!method) {
+ fprintf(stderr, "problem creating ssl method: %s\n",
+ ERR_error_string(ERR_get_error(), ssl_err_buf));
+ return NULL;
+ }
+ this->ssl_client_ctx = SSL_CTX_new(method); /* create context */
+ if (!this->ssl_client_ctx) {
+ fprintf(stderr, "problem creating ssl context: %s\n",
+ ERR_error_string(ERR_get_error(), ssl_err_buf));
+ return NULL;
+ }
+
+
+ /* openssl init for cert verification (used with client sockets) */
+
+ if (!SSL_CTX_load_verify_locations(this->ssl_client_ctx, NULL,
+ LWS_OPENSSL_CLIENT_CERTS)) {
+ fprintf(stderr, "Unable to load SSL Client certs from %s "
+ "(set by --with-client-cert-dir= in configure) -- "
+ " client ssl isn't going to work",
+ LWS_OPENSSL_CLIENT_CERTS);
+ }
+
+ if (this->use_ssl) {
+
+ /* openssl init for server sockets */
- method = (SSL_METHOD *)SSLv23_server_method();
- if (!method) {
- fprintf(stderr, "problem creating ssl method: %s\n",
- ERR_error_string(ERR_get_error(), ssl_err_buf));
- return NULL;
- }
- ssl_ctx = SSL_CTX_new(method); /* create context */
- if (!ssl_ctx) {
- printf("problem creating ssl context: %s\n",
- ERR_error_string(ERR_get_error(), ssl_err_buf));
- return NULL;
- }
/* set the local certificate from CertFile */
- n = SSL_CTX_use_certificate_file(ssl_ctx,
+ n = SSL_CTX_use_certificate_file(this->ssl_ctx,
ssl_cert_filepath, SSL_FILETYPE_PEM);
if (n != 1) {
fprintf(stderr, "problem getting cert '%s': %s\n",
return NULL;
}
/* set the private key from KeyFile */
- if (SSL_CTX_use_PrivateKey_file(ssl_ctx,
+ if (SSL_CTX_use_PrivateKey_file(this->ssl_ctx,
ssl_private_key_filepath,
SSL_FILETYPE_PEM) != 1) {
fprintf(stderr, "ssl problem getting key '%s': %s\n",
return NULL;
}
/* verify private key */
- if (!SSL_CTX_check_private_key(ssl_ctx)) {
+ if (!SSL_CTX_check_private_key(this->ssl_ctx)) {
fprintf(stderr, "Private SSL key doesn't match cert\n");
return NULL;
}
if (lws_b64_selftest())
return NULL;
-
- this = malloc(sizeof(struct libwebsocket_context));
-
- this->protocols = protocols;
- this->listen_port = port;
-
/* set up our external listening socket we serve on */
if (port) {
this->fds[0].fd = sockfd;
this->fds[0].events = POLLIN;
this->count_protocols = 0;
-#ifdef LWS_OPENSSL_SUPPORT
- this->use_ssl = use_ssl;
-#endif
if (port) {
listen(sockfd, 5);
enum libwebsocket_callback_reasons {
LWS_CALLBACK_ESTABLISHED,
+ LWS_CALLBACK_CLIENT_ESTABLISHED,
LWS_CALLBACK_CLOSED,
LWS_CALLBACK_RECEIVE,
LWS_CALLBACK_CLIENT_RECEIVE,
+ LWS_CALLBACK_CLIENT_WRITEABLE,
LWS_CALLBACK_HTTP,
LWS_CALLBACK_BROADCAST
};
LWS_WRITE_CLOSE,
LWS_WRITE_PING,
- LWS_WRITE_PONG
+ LWS_WRITE_PONG,
+
+ LWS_WRITE_NO_FIN = 0x40
};
struct libwebsocket;
* You get an opportunity to initialize user data when called back with
* LWS_CALLBACK_ESTABLISHED reason.
*
- * LWS_CALLBACK_ESTABLISHED: after successful websocket handshake
+ * LWS_CALLBACK_ESTABLISHED: after the server completes a handshake with
+ * an incoming client
+ *
+ * LWS_CALLBACK_CLIENT_ESTABLISHED: after your client connection completed
+ * a handshake with the remote server
*
* LWS_CALLBACK_CLOSED: when the websocket session ends
*
* LWS_CALLBACK_BROADCAST: signal to send to client (you would use
* libwebsocket_write() taking care about the
* special buffer requirements
- * LWS_CALLBACK_RECEIVE: data has appeared for the server, it can be
- * found at *in and is len bytes long
+ *
+ * LWS_CALLBACK_RECEIVE: data has appeared for this server endpoint from a
+ * remote client, it can be found at *in and is
+ * len bytes long
+ *
+ * LWS_CALLBACK_CLIENT_RECEIVE: data has appeared from the server for the
+ * client connection, it can be found at *in and
+ * is len bytes long
*
* LWS_CALLBACK_HTTP: an http request has come from a client that is not
* asking to upgrade the connection to a websocket
* @in points to the URI path requested and
* libwebsockets_serve_http_file() makes it very
* simple to send back a file to the client.
+ *
+ * LWS_CALLBACK_CLIENT_WRITEABLE: if you call
+ * libwebsocket_callback_on_writable() on a connection, you will
+ * get this callback coming when the connection socket is able to
+ * accept another write packet without blocking. If it already
+ * was able to take another packet without blocking, you'll get
+ * this callback at the next call to the service loop function.
*/
extern int callback(struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason, void *user,
extern const struct libwebsocket_protocols *
libwebsockets_get_protocol(struct libwebsocket *wsi);
+extern int
+libwebsocket_callback_on_writable(struct libwebsocket *wsi);
+
+extern int
+libwebsocket_callback_on_writable_all_protocol(
+ const struct libwebsocket_protocols *protocol);
+
+
+extern int
+libwebsocket_rx_flow_control(struct libwebsocket *wsi, int enable);
+
extern size_t
libwebsockets_remaining_packet_payload(struct libwebsocket *wsi);
libwebsocket_client_connect(struct libwebsocket_context *clients,
const char *address,
int port,
+ int ssl_connection,
const char *path,
const char *host,
const char *origin,
* packet while not burdening the user code with any protocol knowledge.
*/
- /* FIXME FIN bit */
-
int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
size_t len, enum libwebsocket_write_protocol protocol)
{
int post = 0;
unsigned int shift = 7;
+ if (len == 0) {
+ fprintf(stderr, "zero length libwebsocket_write attempt\n");
+ return 0;
+ }
+
if (protocol == LWS_WRITE_HTTP)
goto send_raw;
/* chrome likes this as of 30 Oct */
/* Firefox 4.0b6 likes this as of 30 Oct */
case 0:
- if (protocol == LWS_WRITE_BINARY) {
+ if ((protocol & 0xf) == LWS_WRITE_BINARY) {
/* in binary mode we send 7-bit used length blocks */
pre = 1;
while (len & (127 << shift)) {
case 4:
- switch (protocol) {
+ switch (protocol & 0xf) {
case LWS_WRITE_TEXT:
n = LWS_WS_OPCODE_04__TEXT_FRAME;
break;
return -1;
}
- /*
- * We don't really support the metaframe concept with FIN.
- * Just set FIN on every packet for now
- */
-
- n |= 1 << 7;
+ if (!(protocol & LWS_WRITE_NO_FIN))
+ n |= 1 << 7;
if (len < 126) {
buf[-2] = n;
memcpy(&buf[0 - pre], wsi->frame_masking_nonce_04, 4);
}
-
send_raw:
#ifdef LWS_OPENSSL_SUPPORT
- if (use_ssl) {
+ if (wsi->ssl) {
n = SSL_write(wsi->ssl, buf - pre, len + pre + post);
if (n < 0) {
fprintf(stderr, "ERROR writing to socket\n");
#ifdef LWS_OPENSSL_SUPPORT
}
#endif
+
debug("written %d bytes to client\n", (int)len);
return 0;
n = 1;
while (n > 0) {
n = read(fd, buf, 512);
+ if (n <= 0)
+ continue;
libwebsocket_write(wsi, (unsigned char *)buf, n,
LWS_WRITE_HTTP);
}
}
#endif
-#ifdef LWS_OPENSSL_SUPPORT
-extern SSL_CTX *ssl_ctx;
-extern int use_ssl;
-#endif
-
#define MAX_CLIENTS 100
#define LWS_MAX_HEADER_NAME_LENGTH 64
#define LWS_INITIAL_HDR_ALLOC 256
#define LWS_ADDITIONAL_HDR_ALLOC 64
#define MAX_USER_RX_BUFFER 512
-#define MAX_BROADCAST_PAYLOAD 1024
+#define MAX_BROADCAST_PAYLOAD 2048
#define LWS_MAX_PROTOCOLS 10
#define MAX_WEBSOCKET_04_KEY_LEN 128
int listen_port;
#ifdef LWS_OPENSSL_SUPPORT
int use_ssl;
+ SSL_CTX *ssl_ctx;
+ SSL_CTX *ssl_client_ctx;
#endif
struct libwebsocket_protocols *protocols;
int count_protocols;
#ifdef LWS_OPENSSL_SUPPORT
SSL *ssl;
+ BIO *client_bio;
#endif
void *user_space;
for (t = 0; t < 20; t++) {
s = t & 0x0f;
- if (t >= 16) {
- W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s));
- }
+ if (t >= 16)
+ W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
+ W((s+2) & 0x0f) ^ W(s));
+
tmp = S(5, a) + F0(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
for (t = 20; t < 40; t++) {
s = t & 0x0f;
- W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s));
+ W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
+ W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F1(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
for (t = 40; t < 60; t++) {
s = t & 0x0f;
- W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s));
+ W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
+ W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F2(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
for (t = 60; t < 80; t++) {
s = t & 0x0f;
- W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s));
+ W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
+ W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F3(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
nothing is pending, or as soon as it services whatever was pending.
</blockquote>
<hr>
+<h2>libwebsocket_callback_on_writable - Request a callback when this socket becomes able to be written to without blocking</h2>
+<i>int</i>
+<b>libwebsocket_callback_on_writable</b>
+(<i>struct libwebsocket *</i> <b>wsi</b>)
+<h3>Arguments</h3>
+<dl>
+<dt><b>wsi</b>
+<dd>Websocket connection instance to get callback for
+</dl>
+<hr>
+<h2>libwebsocket_callback_on_writable_all_protocol - Request a callback for all connections using the given protocol when it becomes possible to write to each socket without blocking in turn.</h2>
+<i>int</i>
+<b>libwebsocket_callback_on_writable_all_protocol</b>
+(<i>const struct libwebsocket_protocols *</i> <b>protocol</b>)
+<h3>Arguments</h3>
+<dl>
+<dt><b>protocol</b>
+<dd>Protocol whose connections will get callbacks
+</dl>
+<hr>
+<h2>libwebsocket_rx_flow_control - Enable and disable socket servicing for receieved packets.</h2>
+<i>int</i>
+<b>libwebsocket_rx_flow_control</b>
+(<i>struct libwebsocket *</i> <b>wsi</b>,
+<i>int</i> <b>enable</b>)
+<h3>Arguments</h3>
+<dl>
+<dt><b>wsi</b>
+<dd>Websocket connection instance to get callback for
+<dt><b>enable</b>
+<dd>0 = disable read servicing for this connection, 1 = enable
+</dl>
+<h3>Description</h3>
+<blockquote>
+<p>
+If the output side of a server process becomes choked, this allows flow
+control for the input side.
+</blockquote>
+<hr>
<h2>libwebsocket_create_context - Create the websocket handler</h2>
<i>struct libwebsocket_context *</i>
<b>libwebsocket_create_context</b>
Many protocols won't care becuse their packets are always small.
</blockquote>
<hr>
+<h2>libwebsocket_client_connect - Connect to another websocket server</h2>
+<i>struct libwebsocket *</i>
+<b>libwebsocket_client_connect</b>
+(<i>struct libwebsocket_context *</i> <b>this</b>,
+<i>const char *</i> <b>address</b>,
+<i>int</i> <b>port</b>,
+<i>int</i> <b>ssl_connection</b>,
+<i>const char *</i> <b>path</b>,
+<i>const char *</i> <b>host</b>,
+<i>const char *</i> <b>origin</b>,
+<i>const char *</i> <b>protocol</b>)
+<h3>Arguments</h3>
+<dl>
+<dt><b>this</b>
+<dd>Websocket context
+<dt><b>address</b>
+<dd>Remote server address, eg, "myserver.com"
+<dt><b>port</b>
+<dd>Port to connect to on the remote server, eg, 80
+<dt><b>ssl_connection</b>
+<dd>0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self
+signed certs
+<dt><b>path</b>
+<dd>Websocket path on server
+<dt><b>host</b>
+<dd>Hostname on server
+<dt><b>origin</b>
+<dd>Socket origin name
+<dt><b>protocol</b>
+<dd>Comma-separated list of protocols being asked for from
+the server, or just one. The server will pick the one it
+likes best.
+</dl>
+<h3>Description</h3>
+<blockquote>
+This function creates a connection to a remote server
+</blockquote>
+<hr>
<h2>callback - User server actions</h2>
<i>int</i>
<b>callback</b>
</blockquote>
<h3>LWS_CALLBACK_ESTABLISHED</h3>
<blockquote>
-after successful websocket handshake
+after the server completes a handshake with
+an incoming client
+</blockquote>
+<h3>LWS_CALLBACK_CLIENT_ESTABLISHED</h3>
+<blockquote>
+after your client connection completed
+a handshake with the remote server
</blockquote>
<h3>LWS_CALLBACK_CLOSED</h3>
<blockquote>
</blockquote>
<h3>LWS_CALLBACK_RECEIVE</h3>
<blockquote>
-data has appeared for the server, it can be
-found at *in and is len bytes long
+data has appeared for this server endpoint from a
+remote client, it can be found at *in and is
+len bytes long
+</blockquote>
+<h3>LWS_CALLBACK_CLIENT_RECEIVE</h3>
+<blockquote>
+data has appeared from the server for the
+client connection, it can be found at *in and
+is len bytes long
</blockquote>
<h3>LWS_CALLBACK_HTTP</h3>
<blockquote>
<b>libwebsockets_serve_http_file</b> makes it very
simple to send back a file to the client.
</blockquote>
+<h3>LWS_CALLBACK_CLIENT_WRITEABLE</h3>
+<blockquote>
+if you call
+<b>libwebsocket_callback_on_writable</b> on a connection, you will
+get this callback coming when the connection socket is able to
+accept another write packet without blocking. If it already
+was able to take another packet without blocking, you'll get
+this callback at the next call to the service loop function.
+</blockquote>
<hr>
<h2>struct libwebsocket_protocols - List of protocols and handlers server supports.</h2>
<b>struct libwebsocket_protocols</b> {<br>
libwebsockets_test_server_LDADD=-L../lib -lwebsockets
libwebsockets_test_client_SOURCES=test-client.c
libwebsockets_test_client_LDADD=-L../lib -lwebsockets
+
+
+
+libwebsockets_test_server_CFLAGS:= -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\"
+libwebsockets_test_client_CFLAGS:= -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\"
+
#
# cook a random test cert and key
# notice your real cert and key will want to be 0600 permissions
CONFIG_CLEAN_VPATH_FILES =
am__installdirs = "$(DESTDIR)$(bindir)"
PROGRAMS = $(bin_PROGRAMS)
-am_libwebsockets_test_client_OBJECTS = test-client.$(OBJEXT)
+am_libwebsockets_test_client_OBJECTS = \
+ libwebsockets_test_client-test-client.$(OBJEXT)
libwebsockets_test_client_OBJECTS = \
$(am_libwebsockets_test_client_OBJECTS)
libwebsockets_test_client_DEPENDENCIES =
-am_libwebsockets_test_server_OBJECTS = test-server.$(OBJEXT)
+libwebsockets_test_client_LINK = $(LIBTOOL) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(libwebsockets_test_client_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+am_libwebsockets_test_server_OBJECTS = \
+ libwebsockets_test_server-test-server.$(OBJEXT)
libwebsockets_test_server_OBJECTS = \
$(am_libwebsockets_test_server_OBJECTS)
libwebsockets_test_server_DEPENDENCIES =
+libwebsockets_test_server_LINK = $(LIBTOOL) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(libwebsockets_test_server_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
+clientcertdir = @clientcertdir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
libwebsockets_test_server_LDADD = -L../lib -lwebsockets
libwebsockets_test_client_SOURCES = test-client.c
libwebsockets_test_client_LDADD = -L../lib -lwebsockets
+libwebsockets_test_server_CFLAGS := -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\"
+libwebsockets_test_client_CFLAGS := -Wall -Werror -std=gnu99 -pedantic -DDATADIR=\"@datadir@\" -DLWS_OPENSSL_CLIENT_CERTS=\"@clientcertdir@\"
all: all-am
.SUFFIXES:
rm -f $$list
libwebsockets-test-client$(EXEEXT): $(libwebsockets_test_client_OBJECTS) $(libwebsockets_test_client_DEPENDENCIES)
@rm -f libwebsockets-test-client$(EXEEXT)
- $(LINK) $(libwebsockets_test_client_OBJECTS) $(libwebsockets_test_client_LDADD) $(LIBS)
+ $(libwebsockets_test_client_LINK) $(libwebsockets_test_client_OBJECTS) $(libwebsockets_test_client_LDADD) $(LIBS)
libwebsockets-test-server$(EXEEXT): $(libwebsockets_test_server_OBJECTS) $(libwebsockets_test_server_DEPENDENCIES)
@rm -f libwebsockets-test-server$(EXEEXT)
- $(LINK) $(libwebsockets_test_server_OBJECTS) $(libwebsockets_test_server_LDADD) $(LIBS)
+ $(libwebsockets_test_server_LINK) $(libwebsockets_test_server_OBJECTS) $(libwebsockets_test_server_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-client.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_test_client-test-client.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_test_server-test-server.Po@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+libwebsockets_test_client-test-client.o: test-client.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_client_CFLAGS) $(CFLAGS) -MT libwebsockets_test_client-test-client.o -MD -MP -MF $(DEPDIR)/libwebsockets_test_client-test-client.Tpo -c -o libwebsockets_test_client-test-client.o `test -f 'test-client.c' || echo '$(srcdir)/'`test-client.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libwebsockets_test_client-test-client.Tpo $(DEPDIR)/libwebsockets_test_client-test-client.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-client.c' object='libwebsockets_test_client-test-client.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_client_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_client-test-client.o `test -f 'test-client.c' || echo '$(srcdir)/'`test-client.c
+
+libwebsockets_test_client-test-client.obj: test-client.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_client_CFLAGS) $(CFLAGS) -MT libwebsockets_test_client-test-client.obj -MD -MP -MF $(DEPDIR)/libwebsockets_test_client-test-client.Tpo -c -o libwebsockets_test_client-test-client.obj `if test -f 'test-client.c'; then $(CYGPATH_W) 'test-client.c'; else $(CYGPATH_W) '$(srcdir)/test-client.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libwebsockets_test_client-test-client.Tpo $(DEPDIR)/libwebsockets_test_client-test-client.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-client.c' object='libwebsockets_test_client-test-client.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_client_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_client-test-client.obj `if test -f 'test-client.c'; then $(CYGPATH_W) 'test-client.c'; else $(CYGPATH_W) '$(srcdir)/test-client.c'; fi`
+
+libwebsockets_test_server-test-server.o: test-server.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_CFLAGS) $(CFLAGS) -MT libwebsockets_test_server-test-server.o -MD -MP -MF $(DEPDIR)/libwebsockets_test_server-test-server.Tpo -c -o libwebsockets_test_server-test-server.o `test -f 'test-server.c' || echo '$(srcdir)/'`test-server.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libwebsockets_test_server-test-server.Tpo $(DEPDIR)/libwebsockets_test_server-test-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-server.c' object='libwebsockets_test_server-test-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_server-test-server.o `test -f 'test-server.c' || echo '$(srcdir)/'`test-server.c
+
+libwebsockets_test_server-test-server.obj: test-server.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_CFLAGS) $(CFLAGS) -MT libwebsockets_test_server-test-server.obj -MD -MP -MF $(DEPDIR)/libwebsockets_test_server-test-server.Tpo -c -o libwebsockets_test_server-test-server.obj `if test -f 'test-server.c'; then $(CYGPATH_W) 'test-server.c'; else $(CYGPATH_W) '$(srcdir)/test-server.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libwebsockets_test_server-test-server.Tpo $(DEPDIR)/libwebsockets_test_server-test-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-server.c' object='libwebsockets_test_server-test-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_test_server_CFLAGS) $(CFLAGS) -c -o libwebsockets_test_server-test-server.obj `if test -f 'test-server.c'; then $(CYGPATH_W) 'test-server.c'; else $(CYGPATH_W) '$(srcdir)/test-server.c'; fi`
+
mostlyclean-libtool:
-rm -f *.lo
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
tags uninstall uninstall-am uninstall-binPROGRAMS
+
#
# cook a random test cert and key
# notice your real cert and key will want to be 0600 permissions
* server just it simplifies the demo).
*
* dumb-increment-protocol: we connect to the server and print the number
- * we are given
+ * we are given
*
* lws-mirror-protocol: draws random circles, which are mirrored on to every
- * client (see them being drawn in every browser
- * session also using the test server)
+ * client (see them being drawn in every browser
+ * session also using the test server)
*/
enum demo_protocols {
switch (reason) {
case LWS_CALLBACK_CLIENT_RECEIVE:
- fprintf(stderr, "rx %d '%s'\n", len, in);
+ ((char *)in)[len] = '\0';
+ fprintf(stderr, "rx %d '%s'\n", (int)len, (char *)in);
break;
default:
/* lws-mirror_protocol */
-/* "how to draw a circle" */
-
-struct coord {
- int x;
- int y;
-};
-
-static struct coord circle[] = {
-
-{ 0, 240 },
-{ 12, 239 },
-{ 25, 238 },
-{ 37, 237 },
-{ 49, 234 },
-{ 62, 231 },
-{ 74, 228 },
-{ 86, 224 },
-{ 97, 219 },
-{ 108, 213 },
-{ 120, 207 },
-{ 130, 201 },
-{ 141, 194 },
-{ 151, 186 },
-{ 160, 178 },
-{ 169, 169 },
-{ 178, 160 },
-{ 186, 151 },
-{ 194, 141 },
-{ 201, 130 },
-{ 207, 120 },
-{ 213, 108 },
-{ 219, 97 },
-{ 224, 86 },
-{ 228, 74 },
-{ 231, 62 },
-{ 234, 49 },
-{ 237, 37 },
-{ 238, 25 },
-{ 239, 12 },
-{ 240, 0 },
-{ 239, -12 },
-{ 238, -25 },
-{ 237, -37 },
-{ 234, -49 },
-{ 231, -62 },
-{ 228, -74 },
-{ 224, -86 },
-{ 219, -97 },
-{ 213, -108 },
-{ 207, -120 },
-{ 201, -130 },
-{ 194, -141 },
-{ 186, -151 },
-{ 178, -160 },
-{ 169, -169 },
-{ 160, -178 },
-{ 151, -186 },
-{ 141, -194 },
-{ 130, -201 },
-{ 120, -207 },
-{ 108, -213 },
-{ 97, -219 },
-{ 86, -224 },
-{ 74, -228 },
-{ 62, -231 },
-{ 49, -234 },
-{ 37, -237 },
-{ 25, -238 },
-{ 12, -239 },
-{ 0, -240 },
-{ -12, -239 },
-{ -25, -238 },
-{ -37, -237 },
-{ -49, -234 },
-{ -62, -231 },
-{ -74, -228 },
-{ -86, -224 },
-{ -97, -219 },
-{ -108, -213 },
-{ -119, -207 },
-{ -130, -201 },
-{ -141, -194 },
-{ -151, -186 },
-{ -160, -178 },
-{ -169, -169 },
-{ -178, -160 },
-{ -186, -151 },
-{ -194, -141 },
-{ -201, -130 },
-{ -207, -120 },
-{ -213, -108 },
-{ -219, -97 },
-{ -224, -86 },
-{ -228, -74 },
-{ -231, -62 },
-{ -234, -49 },
-{ -237, -37 },
-{ -238, -25 },
-{ -239, -12 },
-{ -240, 0 },
-{ -239, 12 },
-{ -238, 25 },
-{ -237, 37 },
-{ -234, 49 },
-{ -231, 62 },
-{ -228, 74 },
-{ -224, 86 },
-{ -219, 97 },
-{ -213, 108 },
-{ -207, 120 },
-{ -201, 130 },
-{ -194, 141 },
-{ -186, 151 },
-{ -178, 160 },
-{ -169, 169 },
-{ -160, 178 },
-{ -151, 186 },
-{ -141, 194 },
-{ -130, 201 },
-{ -119, 207 },
-{ -108, 213 },
-{ -97, 219 },
-{ -86, 224 },
-{ -74, 228 },
-{ -62, 231 },
-{ -49, 234 },
-{ -37, 237 },
-{ -25, 238 },
-{ -12, 239 },
-{ 0, 240 },
-
-};
static int
callback_lws_mirror(struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason,
void *user, void *in, size_t len)
{
+ unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 4096 +
+ LWS_SEND_BUFFER_POST_PADDING];
+ int l;
+
switch (reason) {
+ case LWS_CALLBACK_CLIENT_ESTABLISHED:
+
+ /*
+ * start the ball rolling,
+ * LWS_CALLBACK_CLIENT_WRITEABLE will come immediately
+ */
+
+ libwebsocket_callback_on_writable(wsi);
+ break;
+
case LWS_CALLBACK_CLIENT_RECEIVE:
-// fprintf(stderr, "rx %d '%s'\n", len, in);
+/* fprintf(stderr, "rx %d '%s'\n", (int)len, (char *)in); */
+ break;
+
+ case LWS_CALLBACK_CLIENT_WRITEABLE:
+
+ l = sprintf((char *)&buf[LWS_SEND_BUFFER_PRE_PADDING],
+ "c #%06X %d %d %d;",
+ (int)random() & 0xffffff,
+ (int)random() % 500,
+ (int)random() % 250,
+ (int)random() % 24);
+
+ libwebsocket_write(wsi,
+ &buf[LWS_SEND_BUFFER_PRE_PADDING], l, LWS_WRITE_TEXT);
+
+ /* get notified as soon as we can write again */
+
+ libwebsocket_callback_on_writable(wsi);
+
+ usleep(200);
break;
default:
};
static struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "port", required_argument, NULL, 'p' },
- { "ssl", no_argument, NULL, 's' },
+ { "help", no_argument, NULL, 'h' },
+ { "port", required_argument, NULL, 'p' },
+ { "ssl", no_argument, NULL, 's' },
{ NULL, 0, 0, 0 }
};
int port = 7681;
int use_ssl = 0;
struct libwebsocket_context *context;
- const char * address = argv[1];
+ const char *address = argv[1];
struct libwebsocket *wsi_dumb;
struct libwebsocket *wsi_mirror;
- unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1024 +
- LWS_SEND_BUFFER_POST_PADDING];
- int len;
- int i = 0;
- int xofs;
- int yofs;
- int oldx;
- int oldy;
- int scale;
- int colour;
+
fprintf(stderr, "libwebsockets test client\n"
"(C) Copyright 2010 Andy Green <andy@warmcat.com> "
continue;
switch (n) {
case 's':
- use_ssl = 1;
+ use_ssl = 2; /* 2 = allow selfsigned */
break;
case 'p':
port = atoi(optarg);
/* create a client websocket using dumb increment protocol */
- wsi_dumb = libwebsocket_client_connect(context, address, port, "/",
- "http://host", "origin",
+ wsi_dumb = libwebsocket_client_connect(context, address, port, use_ssl,
+ "/", "http://host", "origin",
protocols[PROTOCOL_DUMB_INCREMENT].name);
if (wsi_dumb == NULL) {
/* create a client websocket using mirror protocol */
- wsi_mirror = libwebsocket_client_connect(context, address, port, "/",
- "http://host", "origin",
+ wsi_mirror = libwebsocket_client_connect(context, address, port,
+ use_ssl, "/", "http://host", "origin",
protocols[PROTOCOL_LWS_MIRROR].name);
if (wsi_mirror == NULL) {
*/
n = 0;
- while (n >= 0) {
-
- usleep(10000);
-
- if (i == sizeof circle / sizeof circle[0])
- i = 0;
-
- if (i == 0) {
- xofs = random() % 500;
- yofs = random() % 250;
- scale = random() % 24;
- if (!scale)
- scale = 1;
-
- oldx = xofs + (circle[i].x / scale);
- oldy = yofs + (circle[i].y / scale);
- colour = random() & 0xffffff;
- }
-
- len = sprintf(&buf[LWS_SEND_BUFFER_PRE_PADDING],
- "d #%06X %d %d %d %d", colour, oldx, oldy,
- xofs + (circle[i].x / scale),
- yofs + (circle[i].y / scale));
- oldx = xofs + (circle[i].x / scale);
- oldy = yofs + (circle[i].y / scale);
- i++;
-
- libwebsocket_write(wsi_mirror,
- &buf[LWS_SEND_BUFFER_PRE_PADDING], len, LWS_WRITE_TEXT);
-
-
- n = libwebsocket_service(context, 0);
- }
+ while (n >= 0)
+ n = libwebsocket_service(context, 1000);
libwebsocket_client_close(wsi_dumb);
libwebsocket_client_close(wsi_mirror);
#include <unistd.h>
#include <getopt.h>
#include <string.h>
+#include <sys/time.h>
#include "../lib/libwebsockets.h"
*
* dumb-increment-protocol: once the socket is opened, an incrementing
* ascii string is sent down it every 50ms.
- * If you send "reset\n" on the websocket, then
- * the incrementing number is reset to 0.
+ * If you send "reset\n" on the websocket, then
+ * the incrementing number is reset to 0.
*
* lws-mirror-protocol: copies any received packet to every connection also
- * using this protocol, including the sender
+ * using this protocol, including the sender
*/
enum demo_protocols {
};
-#define LOCAL_RESOURCE_PATH "/usr/share/libwebsockets-test-server"
+#define LOCAL_RESOURCE_PATH DATADIR"/libwebsockets-test-server"
/* this protocol server (always the first one) just knows how to do HTTP */
{
switch (reason) {
case LWS_CALLBACK_HTTP:
- fprintf(stderr, "serving HTTP URI %s\n", in);
+ fprintf(stderr, "serving HTTP URI %s\n", (char *)in);
if (in && strcmp(in, "/favicon.ico") == 0) {
if (libwebsockets_serve_http_file(wsi,
void *user, void *in, size_t len)
{
int n;
- char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
+ unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
LWS_SEND_BUFFER_POST_PADDING];
- char *p = (char *)&buf[LWS_SEND_BUFFER_PRE_PADDING];
+ unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
struct per_session_data__dumb_increment *pss = user;
switch (reason) {
*/
case LWS_CALLBACK_BROADCAST:
- n = sprintf(p, "%d", pss->number++);
+ n = sprintf((char *)p, "%d", pss->number++);
n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
if (n < 0) {
fprintf(stderr, "ERROR writing to socket");
break;
case LWS_CALLBACK_RECEIVE:
- fprintf(stderr, "rx %d\n", len);
+ fprintf(stderr, "rx %d\n", (int)len);
if (len < 6)
break;
if (strcmp(in, "reset\n") == 0)
/* lws-mirror_protocol */
+#define MAX_MESSAGE_QUEUE 64
+
+struct per_session_data__lws_mirror {
+ struct libwebsocket *wsi;
+ int ringbuffer_tail;
+};
+
+struct a_message {
+ void *payload;
+ size_t len;
+};
+
+static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
+static int ringbuffer_head;
+
+
static int
callback_lws_mirror(struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason,
void *user, void *in, size_t len)
{
int n;
+ struct per_session_data__lws_mirror *pss = user;
switch (reason) {
+ case LWS_CALLBACK_ESTABLISHED:
+ pss->ringbuffer_tail = ringbuffer_head;
+ pss->wsi = wsi;
+ break;
+
+ case LWS_CALLBACK_CLIENT_WRITEABLE:
+ if (pss->ringbuffer_tail != ringbuffer_head) {
+
+ n = libwebsocket_write(wsi, (unsigned char *)
+ ringbuffer[pss->ringbuffer_tail].payload +
+ LWS_SEND_BUFFER_PRE_PADDING,
+ ringbuffer[pss->ringbuffer_tail].len,
+ LWS_WRITE_TEXT);
+ if (n < 0) {
+ fprintf(stderr, "ERROR writing to socket");
+ exit(1);
+ }
+
+ if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
+ pss->ringbuffer_tail = 0;
+ else
+ pss->ringbuffer_tail++;
+
+ if (((ringbuffer_head - pss->ringbuffer_tail) %
+ MAX_MESSAGE_QUEUE) < (MAX_MESSAGE_QUEUE - 15))
+ libwebsocket_rx_flow_control(wsi, 1);
+
+ libwebsocket_callback_on_writable(wsi);
+
+ }
+ break;
+
case LWS_CALLBACK_BROADCAST:
n = libwebsocket_write(wsi, in, len, LWS_WRITE_TEXT);
+ if (n < 0)
+ fprintf(stderr, "mirror write failed\n");
break;
case LWS_CALLBACK_RECEIVE:
- /*
- * copy the incoming packet to all other protocol users
- *
- * This demonstrates how easy it is to broadcast from inside
- * a callback.
- *
- * How this works is it calls back to the callback for all
- * connected sockets using this protocol with
- * LWS_CALLBACK_BROADCAST reason. Our handler for that above
- * writes the data down the socket.
- */
- libwebsockets_broadcast(libwebsockets_get_protocol(wsi),
- in, len);
+
+ if (ringbuffer[ringbuffer_head].payload)
+ free(ringbuffer[ringbuffer_head].payload);
+
+ ringbuffer[ringbuffer_head].payload =
+ malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
+ LWS_SEND_BUFFER_POST_PADDING);
+ ringbuffer[ringbuffer_head].len = len;
+ memcpy((char *)ringbuffer[ringbuffer_head].payload +
+ LWS_SEND_BUFFER_PRE_PADDING, in, len);
+ if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
+ ringbuffer_head = 0;
+ else
+ ringbuffer_head++;
+
+ if (((ringbuffer_head - pss->ringbuffer_tail) %
+ MAX_MESSAGE_QUEUE) > (MAX_MESSAGE_QUEUE - 10))
+ libwebsocket_rx_flow_control(wsi, 0);
+
+ libwebsocket_callback_on_writable_all_protocol(
+ libwebsockets_get_protocol(wsi));
break;
default:
[PROTOCOL_LWS_MIRROR] = {
.name = "lws-mirror-protocol",
.callback = callback_lws_mirror,
+ .per_session_data_size =
+ sizeof(struct per_session_data__lws_mirror),
},
[DEMO_PROTOCOL_COUNT] = { /* end of list */
.callback = NULL
};
static struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "port", required_argument, NULL, 'p' },
- { "ssl", no_argument, NULL, 's' },
+ { "help", no_argument, NULL, 'h' },
+ { "port", required_argument, NULL, 'p' },
+ { "ssl", no_argument, NULL, 's' },
{ NULL, 0, 0, 0 }
};
int port = 7681;
int use_ssl = 0;
struct libwebsocket_context *context;
+#ifdef LWS_NO_FORK
+ unsigned int oldus = 0;
+#endif
fprintf(stderr, "libwebsockets test server\n"
"(C) Copyright 2010-2011 Andy Green <andy@warmcat.com> "
fprintf(stderr, " Using no-fork service loop\n");
while (1) {
-
- usleep(50000);
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
/*
* This broadcasts to all dumb-increment-protocol connections
* at 20Hz.
- *
+ *
* We're just sending a character 'x', in these examples the
* callbacks send their own per-connection content.
*
* We take care of pre-and-post padding allocation.
*/
- libwebsockets_broadcast(&protocols[PROTOCOL_DUMB_INCREMENT],
+ if (((unsigned int)tv.tv_usec - oldus) > 50000) {
+ libwebsockets_broadcast(
+ &protocols[PROTOCOL_DUMB_INCREMENT],
&buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
+ oldus = tv.tv_usec;
+ }
/*
* This example server does not fork or create a thread for
* immediately and quickly.
*/
- libwebsocket_service(context, 0);
+ libwebsocket_service(context, 50);
}
#else
}
while (1) {
-
+
usleep(50000);
/*
* This broadcasts to all dumb-increment-protocol connections
* at 20Hz.
- *
+ *
* We're just sending a character 'x', in these examples the
* callbacks send their own per-connection content.
*
<body>
<h2>libwebsockets "dumb-increment-protocol" test applet</h2>
-The incrementing number is coming from the server.
+The incrementing number is coming from the server and is individual for
+each connection to the server... try opening a second browser window.
Click the button to send the server a websocket message to
reset the number.<br><br>
The lws-mirror protocol doesn't interpret what is being sent to it, it just
re-sends it to every other websocket it has a connection with using that
protocol, including the guy who sent the packet.
+<p>libwebsockets-test-client spams circles on to this shared canvas when
+run.</p>
<br><br>
<table>
}
socket_lm.onmessage =function got_packet(msg) {
- i = msg.data.split(' ');
- if (i[0] == 'd') {
- ctx.strokeStyle = i[1];
- ctx.beginPath();
- ctx.moveTo(+(i[2]), +(i[3]));
- ctx.lineTo(+(i[4]), +(i[5]));
- ctx.stroke();
+ j = msg.data.split(';');
+ f = 0;
+ while (f < j.length - 1) {
+ i = j[f].split(' ');
+ if (i[0] == 'd') {
+ ctx.strokeStyle = i[1];
+ ctx.beginPath();
+ ctx.moveTo(+(i[2]), +(i[3]));
+ ctx.lineTo(+(i[4]), +(i[5]));
+ ctx.stroke();
+ }
+ if (i[0] == 'c') {
+ ctx.strokeStyle = i[1];
+ ctx.beginPath();
+ ctx.arc(+(i[2]), +(i[3]), +(i[4]), 0, Math.PI*2, true);
+ ctx.stroke();
+ }
+
+ f++;
}
}
last_y = y;
return;
}
- socket_lm.send("d " + color + " " + last_x + " " + last_y + " " + x + ' ' + y);
+ socket_lm.send("d " + color + " " + last_x + " " + last_y + " " + x + ' ' + y + ';');
last_x = x;
last_y = y;