should be enough to get a test server listening on port 7861.
-Testing
--------
+Testing server with a browser
+-----------------------------
If you point your browser (eg, Chrome) to
Using SSL
---------
+The client side operation does not support SSL yet, but the
+server side does.
+
To test it using SSL/WSS, just run the test server with
$ libwebsockets-test-server --ssl
configure option --nofork and simply call libwebsocket_service()
from your own main loop as shown in the test app sources.
+Testing websocket client support
+--------------------------------
+
+If you run the test server as described above, you can also
+connect to it using the test client as well as a browser.
+
+$ libwebsockets-test-client localhost
+
+will by default connect to the test server on localhost:7681
+and print the dumb increment number from the server at the
+same time as drawing random circles in the mirror protocol;
+if you connect to the test server using a browser at the
+same time you will be able to see the circles being drawn.
+
Websocket version supported
---------------------------
-Right now this is tested and working on websockets protocol 76/00
-Untested code is in for 04 support, there is no browser support
-available yet to test it with. Libwebsockets should autoselect
-between the supported versions according to what the browser
-asks for.
+The websocket client code is 04 version, the server supports
+both 00/76 in text mode and 04 dynamically per-connection
+depending on the version of the client / browser.
-2011-01-20 Andy Green <andy@warmcat.com>
+2011-01-22 Andy Green <andy@warmcat.com>
parsers.c \
libwebsockets.h \
base64-decode.c \
+ client-handshake.c \
private-libwebsockets.h
libwebsockets_la_CFLAGS=-Wall -Werror -std=gnu99 -pedantic -rdynamic -fPIC -c
libwebsockets_la_LDFLAGS=-version-info 0:2 -lcrypto
../scripts/kernel-doc -html \
libwebsockets.c \
parsers.c \
+ client-handshake.c \
libwebsockets.h \
> ../libwebsockets-api-doc.html
libwebsockets_la_LIBADD =
dist_libwebsockets_la_OBJECTS = libwebsockets_la-libwebsockets.lo \
libwebsockets_la-handshake.lo libwebsockets_la-parsers.lo \
- libwebsockets_la-base64-decode.lo
+ libwebsockets_la-base64-decode.lo \
+ libwebsockets_la-client-handshake.lo
libwebsockets_la_OBJECTS = $(dist_libwebsockets_la_OBJECTS)
libwebsockets_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(libwebsockets_la_CFLAGS) \
parsers.c \
libwebsockets.h \
base64-decode.c \
+ client-handshake.c \
private-libwebsockets.h
libwebsockets_la_CFLAGS = -Wall -Werror -std=gnu99 -pedantic -rdynamic -fPIC -c
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-base64-decode.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-client-handshake.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-handshake.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-libwebsockets.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-parsers.Plo@am__quote@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -c -o libwebsockets_la-base64-decode.lo `test -f 'base64-decode.c' || echo '$(srcdir)/'`base64-decode.c
+libwebsockets_la-client-handshake.lo: client-handshake.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -MT libwebsockets_la-client-handshake.lo -MD -MP -MF $(DEPDIR)/libwebsockets_la-client-handshake.Tpo -c -o libwebsockets_la-client-handshake.lo `test -f 'client-handshake.c' || echo '$(srcdir)/'`client-handshake.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libwebsockets_la-client-handshake.Tpo $(DEPDIR)/libwebsockets_la-client-handshake.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='client-handshake.c' object='libwebsockets_la-client-handshake.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -c -o libwebsockets_la-client-handshake.lo `test -f 'client-handshake.c' || echo '$(srcdir)/'`client-handshake.c
+
mostlyclean-libtool:
-rm -f *.lo
../scripts/kernel-doc -html \
libwebsockets.c \
parsers.c \
+ client-handshake.c \
libwebsockets.h \
> ../libwebsockets-api-doc.html
"$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
int
-lws_b64_encode_string(const char *in, char *out, int out_size)
+lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
{
unsigned char triple[3];
int i;
int line = 0;
int done = 0;
- while (*in) {
+ while (in_len) {
len = 0;
for (i = 0; i < 3; i++) {
- if (*in) {
+ if (in_len) {
triple[i] = *in++;
len++;
+ in_len--;
} else
triple[i] = 0;
}
for (test = 0; test < sizeof plaintext / sizeof(plaintext[0]); test++) {
buf[sizeof(buf) - 1] = '\0';
- n = lws_b64_encode_string(plaintext[test], buf, sizeof buf);
+ n = lws_b64_encode_string(plaintext[test],
+ strlen(plaintext[test]), buf, sizeof buf);
if (n != strlen(coded[test]) || strcmp(buf, coded[test])) {
fprintf(stderr, "Failed lws_b64 encode selftest "
"%d result '%s' %d\n", test, buf, n);
--- /dev/null
+#include "private-libwebsockets.h"
+#include <netdb.h>
+
+
+/*
+ * In-place str to lower case
+ */
+
+void
+strtolower(char *s)
+{
+ while (*s)
+ *s++ = tolower(*s);
+}
+
+void
+libwebsocket_client_close(struct libwebsocket *wsi)
+{
+ int n = wsi->state;
+ struct libwebsocket_context *clients;
+
+ /* mark the WSI as dead and let the callback know */
+
+ wsi->state = WSI_STATE_DEAD_SOCKET;
+
+ if (wsi->protocol) {
+ if (wsi->protocol->callback && n == WSI_STATE_ESTABLISHED)
+ wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED,
+ wsi->user_space, NULL, 0);
+
+ /* remove it from the client polling list */
+ clients = wsi->protocol->owning_server;
+ if (clients)
+ for (n = 0; n < clients->fds_count; n++) {
+ if (clients->wsi[n] != wsi)
+ continue;
+ while (n < clients->fds_count - 1) {
+ clients->fds[n] = clients->fds[n + 1];
+ clients->wsi[n] = clients->wsi[n + 1];
+ }
+ /* we only have to deal with one */
+ n = clients->fds_count;
+ }
+
+ }
+
+ /* clean out any parsing allocations */
+
+ for (n = 0; n < WSI_TOKEN_COUNT; n++)
+ if (wsi->utf8_token[n].token)
+ free(wsi->utf8_token[n].token);
+
+ /* shut down reasonably cleanly */
+
+#ifdef LWS_OPENSSL_SUPPORT
+ if (use_ssl) {
+ n = SSL_get_fd(wsi->ssl);
+ SSL_shutdown(wsi->ssl);
+ close(n);
+ SSL_free(wsi->ssl);
+ } else {
+#endif
+ shutdown(wsi->sock, SHUT_RDWR);
+ close(wsi->sock);
+#ifdef LWS_OPENSSL_SUPPORT
+ }
+#endif
+}
+
+struct libwebsocket *
+libwebsocket_client_connect(struct libwebsocket_context *clients,
+ const char *address,
+ int port,
+ const char *path,
+ const char *host,
+ const char *origin,
+ const char *protocol)
+{
+ struct hostent *server_hostent;
+ struct sockaddr_in server_addr;
+ char buf[150];
+ char key_b64[150];
+ char hash[20];
+ int fd;
+ struct pollfd pfd;
+ static const char magic_websocket_guid[] =
+ "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+ static const char magic_websocket_04_masking_guid[] =
+ "61AC5F19-FBBA-4540-B96F-6561F1AB40A8";
+ char pkt[1024];
+ char *p = &pkt[0];
+ const char * pc;
+ int len;
+ int okay = 0;
+ struct libwebsocket *wsi;
+ int n;
+
+ wsi = malloc(sizeof (struct libwebsocket));
+ if (wsi == NULL)
+ return NULL;
+
+ clients->wsi[clients->fds_count] = wsi;
+
+ wsi->ietf_spec_revision = 4;
+ wsi->name_buffer_pos = 0;
+ wsi->user_space = NULL;
+ wsi->state = WSI_STATE_CLIENT_UNCONNECTED;
+ wsi->pings_vs_pongs = 0;
+
+ for (n = 0; n < WSI_TOKEN_COUNT; n++) {
+ wsi->utf8_token[n].token = NULL;
+ wsi->utf8_token[n].token_len = 0;
+ }
+
+ /*
+ * prepare the actual connection
+ */
+
+ server_hostent = gethostbyname(address);
+ if (server_hostent == NULL) {
+ fprintf(stderr, "Unable to get host name from %s\n", address);
+ goto bail1;
+ }
+
+ wsi->sock = socket(AF_INET, SOCK_STREAM, 0);
+
+ if (wsi->sock < 0) {
+ fprintf(stderr, "Unable to open socket\n");
+ goto bail1;
+ }
+
+
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(port);
+ server_addr.sin_addr = *((struct in_addr *)server_hostent->h_addr);
+ bzero(&server_addr.sin_zero, 8);
+
+ if (connect(wsi->sock, (struct sockaddr *)&server_addr,
+ sizeof(struct sockaddr)) == -1) {
+ fprintf(stderr, "Connect failed");
+ goto bail1;
+ }
+
+ /*
+ * create the random key
+ */
+
+ fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
+ if (fd < 1) {
+ fprintf(stderr, "Unable to open random device %s\n",
+ SYSTEM_RANDOM_FILEPATH);
+ goto bail2;
+ }
+ n = read(fd, hash, 16);
+ if (n != 16) {
+ fprintf(stderr, "Unable to read from random device %s\n",
+ SYSTEM_RANDOM_FILEPATH);
+ close(fd);
+ goto bail2;
+ }
+ close(fd);
+
+ lws_b64_encode_string(hash, 16, key_b64, sizeof key_b64);
+
+ /*
+ * 04 example client handshake
+ *
+ * GET /chat HTTP/1.1
+ * Host: server.example.com
+ * Upgrade: websocket
+ * Connection: Upgrade
+ * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+ * Sec-WebSocket-Origin: http://example.com
+ * Sec-WebSocket-Protocol: chat, superchat
+ * Sec-WebSocket-Version: 4
+ */
+
+ p += sprintf(p, "GET %s HTTP/1.1\x0d\x0a", path);
+ p += sprintf(p, "Host: %s\x0d\x0a", host);
+ p += sprintf(p, "Upgrade: websocket\x0d\x0a");
+ p += sprintf(p, "Connection: Upgrade\x0d\x0aSec-WebSocket-Key: ");
+ strcpy(p, key_b64);
+ p += strlen(key_b64);
+ p += sprintf(p, "\x0d\x0aSec-WebSocket-Origin: %s\x0d\x0a", origin);
+ if (protocol != NULL)
+ p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a", protocol);
+ p += sprintf(p, "Sec-WebSocket-Version: 4\x0d\x0a\x0d\x0a");
+
+
+ /* prepare the expected server accept response */
+
+ strcpy(buf, key_b64);
+ strcpy(&buf[strlen(buf)], magic_websocket_guid);
+
+ SHA1((unsigned char *)buf, strlen(buf), (unsigned char *)hash);
+
+ lws_b64_encode_string(hash, 20, wsi->initial_handshake_hash_base64,
+ sizeof wsi->initial_handshake_hash_base64);
+
+ /* send our request to the server */
+
+ n = send(wsi->sock, pkt, p - pkt, 0);
+
+ wsi->parser_state = WSI_TOKEN_NAME_PART;
+
+ pfd.fd = wsi->sock;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+
+ n = poll(&pfd, 1, 5000);
+ if (n < 0) {
+ fprintf(stderr, "libwebsocket_client_handshake socket error "
+ "while waiting for handshake response");
+ goto bail2;
+ }
+ if (n == 0) {
+ fprintf(stderr, "libwebsocket_client_handshake timeout "
+ "while waiting for handshake response");
+ goto bail2;
+ }
+
+ /* interpret the server response */
+
+ /*
+ * HTTP/1.1 101 Switching Protocols
+ * Upgrade: websocket
+ * Connection: Upgrade
+ * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
+ * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
+ * Sec-WebSocket-Protocol: chat
+ */
+
+ len = recv(wsi->sock, pkt, sizeof pkt, 0);
+ if (len < 0) {
+ fprintf(stderr, "libwebsocket_client_handshake read error\n");
+ goto bail2;
+ }
+
+ p = pkt;
+ for (n = 0; n < len; n++)
+ libwebsocket_parse(wsi, *p++);
+
+ if (wsi->parser_state != WSI_PARSING_COMPLETE) {
+ fprintf(stderr, "libwebsocket_client_handshake server response"
+ " failed parsing\n");
+ goto bail2;
+ }
+
+ /*
+ * well, what the server sent looked reasonable for syntax.
+ * Now let's confirm it sent all the necessary headers
+ */
+
+ if (!wsi->utf8_token[WSI_TOKEN_HTTP].token_len ||
+ !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
+ !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len ||
+ !wsi->utf8_token[WSI_TOKEN_ACCEPT].token_len ||
+ !wsi->utf8_token[WSI_TOKEN_NONCE].token_len ||
+ (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len &&
+ protocol != NULL)) {
+ fprintf(stderr, "libwebsocket_client_handshake "
+ "missing required header\n");
+ goto bail2;
+ }
+
+ /*
+ * Everything seems to be there, now take a closer look at what is in
+ * each header
+ */
+
+ strtolower(wsi->utf8_token[WSI_TOKEN_HTTP].token);
+ if (strcmp(wsi->utf8_token[WSI_TOKEN_HTTP].token,
+ "101 switching protocols")) {
+ fprintf(stderr, "libwebsocket_client_handshake server sent bad"
+ " HTTP response '%s'\n",
+ wsi->utf8_token[WSI_TOKEN_HTTP].token);
+ goto bail2;
+ }
+
+ strtolower(wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
+ if (strcmp(wsi->utf8_token[WSI_TOKEN_UPGRADE].token, "websocket")) {
+ fprintf(stderr, "libwebsocket_client_handshake server sent bad"
+ " Upgrade header '%s'\n",
+ wsi->utf8_token[WSI_TOKEN_UPGRADE].token);
+ goto bail2;
+ }
+
+ strtolower(wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
+ if (strcmp(wsi->utf8_token[WSI_TOKEN_CONNECTION].token, "upgrade")) {
+ fprintf(stderr, "libwebsocket_client_handshake server sent bad"
+ " Connection hdr '%s'\n",
+ wsi->utf8_token[WSI_TOKEN_CONNECTION].token);
+ goto bail2;
+ }
+ /*
+ * confirm the protocol the server wants to talk was in the list of
+ * protocols we offered
+ */
+
+ if (!wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len) {
+
+ /* no protocol name to work from, default to first protocol */
+ wsi->protocol = &clients->protocols[0];
+
+ goto check_accept;
+ }
+
+ pc = protocol;
+ while (*pc && !okay) {
+ if ((!strncmp(pc, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
+ wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len)) &&
+ (pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == ',' ||
+ pc[wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len] == '\0')) {
+ okay = 1;
+ continue;
+ }
+ while (*pc && *pc != ',')
+ pc++;
+ while (*pc && *pc != ' ')
+ pc++;
+ }
+ if (!okay) {
+ fprintf(stderr, "libwebsocket_client_handshake server "
+ "sent bad protocol '%s'\n",
+ wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
+ goto bail2;
+ }
+
+ /*
+ * identify the selected protocol struct and set it
+ */
+ n = 0;
+ wsi->protocol = NULL;
+ while (clients->protocols[n].callback) {
+ if (strcmp(wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
+ clients->protocols[n].name) == 0)
+ wsi->protocol = &clients->protocols[n];
+ n++;
+ }
+
+ if (wsi->protocol == NULL) {
+ fprintf(stderr, "libwebsocket_client_handshake server "
+ "requested protocol '%s', which we "
+ "said we supported but we don't!\n",
+ wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
+ goto bail2;
+ }
+
+check_accept:
+ /*
+ * Confirm his accept token is the same as the one we precomputed
+ */
+
+ if (strcmp(wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
+ wsi->initial_handshake_hash_base64)) {
+ fprintf(stderr, "libwebsocket_client_handshake server sent "
+ "bad ACCEPT '%s' vs computed '%s'\n",
+ wsi->utf8_token[WSI_TOKEN_ACCEPT].token,
+ wsi->initial_handshake_hash_base64);
+ goto bail2;
+ }
+
+ /*
+ * Calculate the masking key to use when sending data to server
+ */
+
+ strcpy(buf, key_b64);
+ p = buf + strlen(key_b64);
+ strcpy(p, wsi->utf8_token[WSI_TOKEN_NONCE].token);
+ p += wsi->utf8_token[WSI_TOKEN_NONCE].token_len;
+ strcpy(p, magic_websocket_04_masking_guid);
+ SHA1((unsigned char *)buf, strlen(buf), wsi->masking_key_04);
+
+ /* 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;
+
+ wsi->state = WSI_STATE_ESTABLISHED;
+ wsi->client_mode = 1;
+
+ fprintf(stderr, "handshake OK for protocol %s\n", wsi->protocol->name);
+
+ return wsi;
+
+
+bail2:
+ libwebsocket_client_close(wsi);
+bail1:
+ free(wsi);
+
+ return NULL;
+}
int nonce_len;
int accept_len;
- if (!wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len ||
+ if (!wsi->utf8_token[WSI_TOKEN_SWORIGIN].token_len ||
!wsi->utf8_token[WSI_TOKEN_HOST].token_len ||
- !wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len ||
- !wsi->utf8_token[WSI_TOKEN_KEY].token_len)
+ !wsi->utf8_token[WSI_TOKEN_KEY].token_len) {
+ debug("handshake_04 missing pieces\n");
/* completed header processing, but missing some bits */
goto bail;
+ }
if (wsi->utf8_token[WSI_TOKEN_KEY].token_len >=
MAX_WEBSOCKET_04_KEY_LEN) {
wsi->utf8_token[WSI_TOKEN_KEY].token_len +
strlen(websocket_magic_guid_04), hash);
- accept_len = lws_b64_encode_string((char *)hash, accept_buf,
+ accept_len = lws_b64_encode_string((char *)hash, 20, accept_buf,
sizeof accept_buf);
if (accept_len < 0) {
fprintf(stderr, "Base64 encoded hash too long\n");
strcpy(p, accept_buf);
p += accept_len;
+ strcpy(p, "\x0d\x0aSec-WebSocket-Nonce: ");
+ p += strlen("\x0d\x0aSec-WebSocket-Nonce: ");
+
/* select the nonce */
fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
if (fd < 1) {
fprintf(stderr, "Unable to open random device %s\n",
SYSTEM_RANDOM_FILEPATH);
- free(wsi->user_space);
+ if (wsi->user_space)
+ free(wsi->user_space);
goto bail;
}
n = read(fd, hash, 16);
if (n != 16) {
- fprintf(stderr, "Unable to read from random device %s\n",
- SYSTEM_RANDOM_FILEPATH);
- free(wsi->user_space);
+ fprintf(stderr, "Unable to read from random device %s %d\n",
+ SYSTEM_RANDOM_FILEPATH, n);
+ if (wsi->user_space)
+ free(wsi->user_space);
goto bail;
}
close(fd);
/* encode the nonce */
- nonce_len = lws_b64_encode_string((const char *)hash, nonce_buf,
+ nonce_len = lws_b64_encode_string((const char *)hash, 16, nonce_buf,
sizeof nonce_buf);
if (nonce_len < 0) {
fprintf(stderr, "Failed to base 64 encode the nonce\n");
- free(wsi->user_space);
+ if (wsi->user_space)
+ free(wsi->user_space);
goto bail;
}
#ifdef DEBUG
fwrite(buf, 1, len, stderr);
#endif
+
+ if (wsi->client_mode) {
+ for (n = 0; n < len; n++)
+ libwebsocket_client_rx_sm(wsi, *buf++);
+
+ return 0;
+ }
+
for (n = 0; n < len; n++)
libwebsocket_parse(wsi, *buf++);
if (wsi->parser_state != WSI_PARSING_COMPLETE)
break;
+ debug("libwebsocket_parse sees parsing complete\n");
+
/* is this websocket protocol or normal http 1.0? */
if (!wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
goto bail;
break;
case 4: /* 04 */
+ debug("libwebsocket_parse calling handshake_04\n");
if (handshake_04(wsi))
goto bail;
break;
break;
case WSI_STATE_ESTABLISHED:
+ if (wsi->client_mode) {
+ for (n = 0; n < len; n++)
+ libwebsocket_client_rx_sm(wsi, *buf++);
+
+ return 0;
+ }
+
+
if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0)
goto bail;
break;
if (wsi->state != WSI_STATE_ESTABLISHED)
continue;
+ /* only to clients connected to us */
+
+ if (wsi->client_mode)
+ continue;
+
/*
* only broadcast to connections using
* the requested protocol
if (this == NULL)
return 1;
- n = poll(this->fds, this->fds_count, timeout_ms);
+ /* don't check listen socket if we are not listening */
+
+ if (this->listen_port)
+ n = poll(this->fds, this->fds_count, timeout_ms);
+ else
+ n = poll(&this->fds[1], this->fds_count - 1, timeout_ms);
+
if (n < 0 || this->fds[0].revents & (POLLERR | POLLHUP)) {
fprintf(stderr, "Listen Socket dead\n");
this->wsi[this->fds_count]->sock = fd;
this->wsi[this->fds_count]->state = WSI_STATE_HTTP;
this->wsi[this->fds_count]->name_buffer_pos = 0;
+ this->wsi[this->fds_count]->client_mode = 0;
for (n = 0; n < WSI_TOKEN_COUNT; n++) {
this->wsi[this->fds_count]->
/**
- * libwebsocket_create_server() - Create the listening websockets server
- * @port: Port to listen on
+ * libwebsocket_create_context() - Create the websocket handler
+ * @port: Port to listen on... you can use 0 to suppress listening on
+ * any port, that's what you want if you are not running a
+ * websocket server at all but just using it as a client
* @protocols: Array of structures listing supported protocols and a protocol-
* specific callback for each one. The list is ended with an
* entry that has a NULL callback pointer.
*/
struct libwebsocket_context *
-libwebsocket_create_server(int port,
+libwebsocket_create_context(int port,
struct libwebsocket_protocols *protocols,
const char *ssl_cert_filepath,
const char *ssl_private_key_filepath,
int gid, int uid)
{
int n;
- int sockfd;
+ int sockfd = 0;
int fd;
struct sockaddr_in serv_addr, cli_addr;
int opt = 1;
/* set the private key from KeyFile */
if (SSL_CTX_use_PrivateKey_file(ssl_ctx,
ssl_private_key_filepath,
- SSL_FILETYPE_PEM) != 1) {
+ SSL_FILETYPE_PEM) != 1) {
fprintf(stderr, "ssl problem getting key '%s': %s\n",
ssl_private_key_filepath,
ERR_error_string(ERR_get_error(), ssl_err_buf));
this = malloc(sizeof(struct libwebsocket_context));
this->protocols = protocols;
+ this->listen_port = port;
/* set up our external listening socket we serve on */
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
- if (sockfd < 0) {
- fprintf(stderr, "ERROR opening socket");
- return NULL;
- }
+ if (port) {
- /* allow us to restart even if old sockets in TIME_WAIT */
- setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0) {
+ fprintf(stderr, "ERROR opening socket");
+ return NULL;
+ }
+
+ /* allow us to restart even if old sockets in TIME_WAIT */
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
- bzero((char *) &serv_addr, sizeof(serv_addr));
- serv_addr.sin_family = AF_INET;
- serv_addr.sin_addr.s_addr = INADDR_ANY;
- serv_addr.sin_port = htons(port);
+ bzero((char *) &serv_addr, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = INADDR_ANY;
+ serv_addr.sin_port = htons(port);
- n = bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
- if (n < 0) {
- fprintf(stderr, "ERROR on binding to port %d (%d %d)\n",
+ n = bind(sockfd, (struct sockaddr *) &serv_addr,
+ sizeof(serv_addr));
+ if (n < 0) {
+ fprintf(stderr, "ERROR on binding to port %d (%d %d)\n",
port, n, errno);
- return NULL;
+ return NULL;
+ }
}
/* drop any root privs for this process */
this->use_ssl = use_ssl;
#endif
- listen(sockfd, 5);
- fprintf(stderr, " Listening on port %d\n", port);
+ if (port) {
+ listen(sockfd, 5);
+ fprintf(stderr, " Listening on port %d\n", port);
+ }
/* set up our internal broadcast trigger sockets per-protocol */
return this;
}
+
#ifndef LWS_NO_FORK
/**
}
cli_addr.sin_family = AF_INET;
cli_addr.sin_port = htons(
- this->protocols[client - 1].broadcast_socket_port);
+ this->protocols[client - 1].broadcast_socket_port);
cli_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
n = connect(fd, (struct sockaddr *)&cli_addr,
sizeof cli_addr);
return -1;
}
- this->protocols[client - 1].broadcast_socket_user_fd = fd;
+ this->protocols[client - 1].broadcast_socket_user_fd =
+ fd;
}
continue;
/* never broadcast to non-established connection */
-
if (this->wsi[n]->state != WSI_STATE_ESTABLISHED)
continue;
/* only broadcast to guys using requested protocol */
-
if (this->wsi[n]->protocol != protocol)
continue;
#ifndef __LIBWEBSOCKET_H__
#define __LIBWEBSOCKET_H__
+#define CONTEXT_PORT_NO_LISTEN 0
enum libwebsocket_callback_reasons {
LWS_CALLBACK_ESTABLISHED,
LWS_CALLBACK_CLOSED,
LWS_CALLBACK_RECEIVE,
+ LWS_CALLBACK_CLIENT_RECEIVE,
LWS_CALLBACK_HTTP,
LWS_CALLBACK_BROADCAST
};
};
extern struct libwebsocket_context *
-libwebsocket_create_server(int port,
+libwebsocket_create_context(int port,
struct libwebsocket_protocols *protocols,
const char *ssl_cert_filepath,
const char *ssl_private_key_filepath, int gid, int uid);
* use the whole buffer without taking care of the above.
*/
-#define LWS_SEND_BUFFER_PRE_PADDING 12
+/* this is the frame nonce plus two header plus 8 length */
+
+#define LWS_SEND_BUFFER_PRE_PADDING (4 + 10)
#define LWS_SEND_BUFFER_POST_PADDING 1
extern int
extern size_t
libwebsockets_remaining_packet_payload(struct libwebsocket *wsi);
+extern struct libwebsocket *
+libwebsocket_client_connect(struct libwebsocket_context *clients,
+ const char *address,
+ int port,
+ const char *path,
+ const char *host,
+ const char *origin,
+ const char *protocol);
+
+void
+libwebsocket_client_close(struct libwebsocket *wsi);
+
#endif
#include "private-libwebsockets.h"
const struct lws_tokens lws_tokens[WSI_TOKEN_COUNT] = {
- [WSI_TOKEN_GET_URI] = { "GET ", 4 },
- [WSI_TOKEN_HOST] = { "Host:", 5 },
+ [WSI_TOKEN_GET_URI] = { "GET ", 4 },
+ [WSI_TOKEN_HOST] = { "Host:", 5 },
[WSI_TOKEN_CONNECTION] = { "Connection:", 11 },
[WSI_TOKEN_KEY1] = { "Sec-WebSocket-Key1:", 19 },
[WSI_TOKEN_KEY2] = { "Sec-WebSocket-Key2:", 19 },
[WSI_TOKEN_PROTOCOL] = { "Sec-WebSocket-Protocol:", 23 },
- [WSI_TOKEN_UPGRADE] = { "Upgrade:", 8 },
- [WSI_TOKEN_ORIGIN] = { "Origin:", 7 },
+ [WSI_TOKEN_UPGRADE] = { "Upgrade:", 8 },
+ [WSI_TOKEN_ORIGIN] = { "Origin:", 7 },
[WSI_TOKEN_DRAFT] = { "Sec-WebSocket-Draft:", 20 },
- [WSI_TOKEN_CHALLENGE] = { "\x0d\x0a", 2 },
+ [WSI_TOKEN_CHALLENGE] = { "\x0d\x0a", 2 },
[WSI_TOKEN_KEY] = { "Sec-WebSocket-Key:", 18 },
[WSI_TOKEN_VERSION] = { "Sec-WebSocket-Version:", 22 },
+ [WSI_TOKEN_ACCEPT] = { "Sec-WebSocket-Accept:", 21 },
+ [WSI_TOKEN_NONCE] = { "Sec-WebSocket-Nonce:", 20 },
+ [WSI_TOKEN_HTTP] = { "HTTP/1.1 ", 9 },
+ [WSI_TOKEN_SWORIGIN] = { "Sec-WebSocket-Origin:", 21 },
+
};
int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c)
case WSI_TOKEN_PROTOCOL:
case WSI_TOKEN_UPGRADE:
case WSI_TOKEN_ORIGIN:
+ case WSI_TOKEN_SWORIGIN:
case WSI_TOKEN_DRAFT:
case WSI_TOKEN_CHALLENGE:
case WSI_TOKEN_KEY:
case WSI_TOKEN_VERSION:
+ case WSI_TOKEN_ACCEPT:
+ case WSI_TOKEN_NONCE:
+ case WSI_TOKEN_HTTP:
debug("WSI_TOKEN_(%d) '%c'\n", wsi->parser_state, c);
/* collect into malloc'd buffers */
debug("known hdr '%s'\n", wsi->name_buffer);
wsi->parser_state = WSI_TOKEN_GET_URI + n;
wsi->current_alloc_len = LWS_INITIAL_HDR_ALLOC;
+
wsi->utf8_token[wsi->parser_state].token =
malloc(wsi->current_alloc_len);
wsi->utf8_token[wsi->parser_state].token_len = 0;
break;
}
+ if (wsi->parser_state != WSI_TOKEN_CHALLENGE)
+ break;
+
/* don't look for payload when it can just be http headers */
- if (wsi->parser_state == WSI_TOKEN_CHALLENGE &&
- !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len) {
+ if (!wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len) {
/* they're HTTP headers, not websocket upgrade! */
debug("Setting WSI_PARSING_COMPLETE "
"from http headers\n");
wsi->parser_state = WSI_PARSING_COMPLETE;
}
+
+ /* 04 version has no packet content after end of hdrs */
+
+ if (wsi->utf8_token[WSI_TOKEN_VERSION].token_len &&
+ atoi(wsi->utf8_token[WSI_TOKEN_VERSION].token) >= 4) {
+ debug("04 header completed\n");
+ wsi->parser_state = WSI_PARSING_COMPLETE;
+ }
+
+ /* client parser? */
+
+ if (wsi->ietf_spec_revision >= 4) {
+ debug("04 header completed\n");
+ wsi->parser_state = WSI_PARSING_COMPLETE;
+ }
+
break;
/* skipping arg part of a name we didn't recognize */
}
static unsigned char inline
-unmask(struct libwebsocket *wsi, unsigned char c)
+xor_mask(struct libwebsocket *wsi, unsigned char c)
{
c ^= wsi->masking_key_04[wsi->frame_mask_index++];
if (wsi->frame_mask_index == 20)
if (c == 0xff)
wsi->lws_rx_parse_state = LWS_RXPS_SEEN_76_FF;
if (c == 0) {
- wsi->lws_rx_parse_state = LWS_RXPS_EAT_UNTIL_76_FF;
+ wsi->lws_rx_parse_state =
+ LWS_RXPS_EAT_UNTIL_76_FF;
wsi->rx_user_buffer_head = 0;
}
break;
* FIN (b7)
*/
- c = unmask(wsi, c);
+ c = xor_mask(wsi, c);
if (c & 0x70) {
- fprintf(stderr, "Frame has extensions set illegally\n");
+ fprintf(stderr, "Frame has extensions set illegally 1\n");
/* kill the connection */
return -1;
}
break;
case LWS_RXPS_04_FRAME_HDR_LEN:
- c = unmask(wsi, c);
+ c = xor_mask(wsi, c);
if (c & 0x80) {
- fprintf(stderr, "Frame has extensions set illegally\n");
+ fprintf(stderr, "Frame has extensions "
+ "set illegally 2\n");
/* kill the connection */
return -1;
}
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
break;
default:
+// fprintf(stderr, "seen incoming 04 frame len %d\n", c);
wsi->rx_packet_length = c;
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
break;
case LWS_RXPS_04_FRAME_HDR_LEN16_2:
- c = unmask(wsi, c);
+ c = xor_mask(wsi, c);
wsi->rx_packet_length = c << 8;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
break;
case LWS_RXPS_04_FRAME_HDR_LEN16_1:
- c = unmask(wsi, c);
+ c = xor_mask(wsi, c);
wsi->rx_packet_length |= c;
wsi->lws_rx_parse_state =
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_8:
- c = unmask(wsi, c);
+ c = xor_mask(wsi, c);
if (c & 0x80) {
fprintf(stderr, "b63 of length must be zero\n");
/* kill the connection */
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_7:
- wsi->rx_packet_length |= ((size_t)unmask(wsi, c)) << 48;
+ wsi->rx_packet_length |= ((size_t)xor_mask(wsi, c)) << 48;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_6:
- wsi->rx_packet_length |= ((size_t)unmask(wsi, c)) << 40;
+ wsi->rx_packet_length |= ((size_t)xor_mask(wsi, c)) << 40;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_5:
- wsi->rx_packet_length |= ((size_t)unmask(wsi, c)) << 32;
+ wsi->rx_packet_length |= ((size_t)xor_mask(wsi, c)) << 32;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_4:
- wsi->rx_packet_length |= ((size_t)unmask(wsi, c)) << 24;
+ wsi->rx_packet_length |= ((size_t)xor_mask(wsi, c)) << 24;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_3:
- wsi->rx_packet_length |= ((size_t)unmask(wsi, c)) << 16;
+ wsi->rx_packet_length |= ((size_t)xor_mask(wsi, c)) << 16;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_2:
- wsi->rx_packet_length |= ((size_t)unmask(wsi, c)) << 8;
+ wsi->rx_packet_length |= ((size_t)xor_mask(wsi, c)) << 8;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_1:
- wsi->rx_packet_length |= ((size_t)unmask(wsi, c));
+ wsi->rx_packet_length |= ((size_t)xor_mask(wsi, c));
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
break;
case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
- (wsi->rx_user_buffer_head++)] = unmask(wsi, c);
+ (wsi->rx_user_buffer_head++)] = xor_mask(wsi, c);
+
if (--wsi->rx_packet_length == 0) {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
* so it can be sent straight out again using libwebsocket_write
*/
+ wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
+ wsi->rx_user_buffer_head] = '\0';
+
if (wsi->protocol->callback)
wsi->protocol->callback(wsi, LWS_CALLBACK_RECEIVE,
- wsi->user_space,
+ wsi->user_space,
&wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
- wsi->rx_user_buffer_head);
+ wsi->rx_user_buffer_head);
+ wsi->rx_user_buffer_head = 0;
+ break;
+ }
+
+ return 0;
+}
+
+
+int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c)
+{
+ int n;
+ unsigned char buf[20 + 4];
+
+ switch (wsi->lws_rx_parse_state) {
+ case LWS_RXPS_NEW:
+
+ switch (wsi->ietf_spec_revision) {
+ /* Firefox 4.0b6 likes this as of 30 Oct */
+ case 0:
+ if (c == 0xff)
+ wsi->lws_rx_parse_state = LWS_RXPS_SEEN_76_FF;
+ if (c == 0) {
+ wsi->lws_rx_parse_state =
+ LWS_RXPS_EAT_UNTIL_76_FF;
+ wsi->rx_user_buffer_head = 0;
+ }
+ break;
+ case 4:
+ /*
+ * 04 logical framing from the spec (all this is masked when incoming
+ * and has to be unmasked)
+ *
+ * We ignore the possibility of extension data because we don't
+ * negotiate any extensions at the moment.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-------+-+-------------+-------------------------------+
+ * |F|R|R|R| opcode|R| Payload len | Extended payload length |
+ * |I|S|S|S| (4) |S| (7) | (16/63) |
+ * |N|V|V|V| |V| | (if payload len==126/127) |
+ * | |1|2|3| |4| | |
+ * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
+ * | Extended payload length continued, if payload len == 127 |
+ * + - - - - - - - - - - - - - - - +-------------------------------+
+ * | | Extension data |
+ * +-------------------------------+ - - - - - - - - - - - - - - - +
+ * : :
+ * +---------------------------------------------------------------+
+ * : Application data :
+ * +---------------------------------------------------------------+
+ *
+ * We pass payload through to userland as soon as we get it, ignoring
+ * FIN. It's up to userland to buffer it up if it wants to see a
+ * whole unfragmented block of the original size (which may be up to
+ * 2^63 long!)
+ */
+
+ /*
+ * 04 spec defines the opcode like this: (1, 2, and 3 are
+ * "control frame" opcodes which may not be fragmented or
+ * have size larger than 126)
+ *
+ * frame-opcode =
+ * %x0 ; continuation frame
+ * / %x1 ; connection close
+ * / %x2 ; ping
+ * / %x3 ; pong
+ * / %x4 ; text frame
+ * / %x5 ; binary frame
+ * / %x6-F ; reserved
+ *
+ * FIN (b7)
+ */
+
+ if (c & 0x70) {
+ fprintf(stderr, "Frame has extensions set "
+ "illegally on first framing byte %02X\n", c);
+ /* kill the connection */
+ return -1;
+ }
+
+ wsi->opcode = c & 0xf;
+ wsi->final = !!((c >> 7) & 1);
+
+ if (wsi->final &&
+ wsi->opcode == LWS_WS_OPCODE_04__CONTINUATION &&
+ wsi->rx_packet_length == 0) {
+ fprintf(stderr,
+ "Frame starts with final continuation\n");
+ /* kill the connection */
+ return -1;
+ }
+
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
+ break;
+ }
+ break;
+
+
+ case LWS_RXPS_04_FRAME_HDR_LEN:
+
+ if (c & 0x80) {
+ fprintf(stderr,
+ "Frame has extensions set illegally 4\n");
+ /* kill the connection */
+ return -1;
+ }
+
+ switch (c) {
+ case 126:
+ /* control frames are not allowed to have big lengths */
+ switch (wsi->opcode) {
+ case LWS_WS_OPCODE_04__CLOSE:
+ case LWS_WS_OPCODE_04__PING:
+ case LWS_WS_OPCODE_04__PONG:
+ fprintf(stderr, "Control frame asking for "
+ "extended length is illegal\n");
+ /* kill the connection */
+ return -1;
+ default:
+ break;
+ }
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
+ break;
+ case 127:
+ /* control frames are not allowed to have big lengths */
+ switch (wsi->opcode) {
+ case LWS_WS_OPCODE_04__CLOSE:
+ case LWS_WS_OPCODE_04__PING:
+ case LWS_WS_OPCODE_04__PONG:
+ fprintf(stderr, "Control frame asking for "
+ "extended length is illegal\n");
+ /* kill the connection */
+ return -1;
+ default:
+ break;
+ }
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
+ break;
+ default:
+ wsi->rx_packet_length = c;
+ wsi->lws_rx_parse_state =
+ LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
+ break;
+ }
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN16_2:
+ wsi->rx_packet_length = c << 8;
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN16_1:
+ wsi->rx_packet_length |= c;
+ wsi->lws_rx_parse_state =
+ LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN64_8:
+ if (c & 0x80) {
+ fprintf(stderr, "b63 of length must be zero\n");
+ /* kill the connection */
+ return -1;
+ }
+ wsi->rx_packet_length = ((size_t)c) << 56;
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN64_7:
+ wsi->rx_packet_length |= ((size_t)c) << 48;
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN64_6:
+ wsi->rx_packet_length |= ((size_t)c) << 40;
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN64_5:
+ wsi->rx_packet_length |= ((size_t)c) << 32;
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN64_4:
+ wsi->rx_packet_length |= ((size_t)c) << 24;
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN64_3:
+ wsi->rx_packet_length |= ((size_t)c) << 16;
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN64_2:
+ wsi->rx_packet_length |= ((size_t)c) << 8;
+ wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
+ break;
+
+ case LWS_RXPS_04_FRAME_HDR_LEN64_1:
+ wsi->rx_packet_length |= (size_t)c;
+ wsi->lws_rx_parse_state =
+ LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
+ break;
+
+ case LWS_RXPS_EAT_UNTIL_76_FF:
+ if (c == 0xff) {
+ wsi->lws_rx_parse_state = LWS_RXPS_NEW;
+ goto issue;
+ }
+ wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
+ (wsi->rx_user_buffer_head++)] = c;
+
+ if (wsi->rx_user_buffer_head != MAX_USER_RX_BUFFER)
+ break;
+issue:
+ if (wsi->protocol->callback)
+ wsi->protocol->callback(wsi,
+ LWS_CALLBACK_CLIENT_RECEIVE,
+ wsi->user_space,
+ &wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
+ wsi->rx_user_buffer_head);
wsi->rx_user_buffer_head = 0;
break;
+ case LWS_RXPS_SEEN_76_FF:
+ if (c)
+ break;
+
+ debug("Seen that client is requesting "
+ "a v76 close, sending ack\n");
+ buf[0] = 0xff;
+ buf[1] = 0;
+ n = libwebsocket_write(wsi, buf, 2, LWS_WRITE_HTTP);
+ if (n < 0) {
+ fprintf(stderr, "ERROR writing to socket");
+ return -1;
+ }
+ debug(" v76 close ack sent, server closing skt\n");
+ /* returning < 0 will get it closed in parent */
+ return -1;
+
+ case LWS_RXPS_PULLING_76_LENGTH:
+ break;
+
+ case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
+ wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
+ (wsi->rx_user_buffer_head++)] = c;
+ if (--wsi->rx_packet_length == 0) {
+ wsi->lws_rx_parse_state = LWS_RXPS_NEW;
+ goto spill;
+ }
+ if (wsi->rx_user_buffer_head != MAX_USER_RX_BUFFER)
+ break;
+spill:
+ /*
+ * is this frame a control packet we should take care of at this
+ * layer? If so service it and hide it from the user callback
+ */
+
+ switch (wsi->opcode) {
+ case LWS_WS_OPCODE_04__CLOSE:
+ /* parrot the close packet payload back */
+ n = libwebsocket_write(wsi, (unsigned char *)
+ &wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
+ wsi->rx_user_buffer_head, LWS_WRITE_CLOSE);
+ /* close the connection */
+ return -1;
+
+ case LWS_WS_OPCODE_04__PING:
+ /* parrot the ping packet payload back as a pong*/
+ n = libwebsocket_write(wsi, (unsigned char *)
+ &wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
+ wsi->rx_user_buffer_head, LWS_WRITE_PONG);
+ break;
+
+ 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;
+
+ default:
+ break;
+ }
+
+ /*
+ * No it's real payload, pass it up to the user callback.
+ * It's nicely buffered with the pre-padding taken care of
+ * so it can be sent straight out again using libwebsocket_write
+ */
+
+ if (wsi->protocol->callback)
+ wsi->protocol->callback(wsi,
+ LWS_CALLBACK_CLIENT_RECEIVE,
+ wsi->user_space,
+ &wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
+ wsi->rx_user_buffer_head);
+ wsi->rx_user_buffer_head = 0;
+ break;
+ default:
+ fprintf(stderr, "client rx illegal state\n");
+ return 1;
}
return 0;
}
+
+
int libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi,
unsigned char *buf, size_t len)
{
/* let the rx protocol state machine have as much as it needs */
n = 0;
- while (wsi->lws_rx_parse_state !=
- LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED && n < len)
+ while (n < len)
if (libwebsocket_rx_sm(wsi, buf[n++]) < 0)
return -1;
- return -0;
+ return 0;
}
+static int
+libwebsocket_04_frame_mask_generate(struct libwebsocket *wsi)
+{
+ int fd;
+ char buf[4 + 20];
+ int n;
+
+ /* fetch the per-frame nonce */
+
+ fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
+ if (fd < 1) {
+ fprintf(stderr, "Unable to open random device %s\n",
+ SYSTEM_RANDOM_FILEPATH);
+ return 1;
+ }
+ n = read(fd, wsi->frame_masking_nonce_04, 4);
+ if (n != 4) {
+ fprintf(stderr, "Unable to read from random device %s %d\n",
+ SYSTEM_RANDOM_FILEPATH, n);
+ return 1;
+ }
+ close(fd);
+
+ /*
+ * the frame key is the frame nonce (4 bytes) followed by the
+ * connection masking key, hashed by SHA1
+ */
+
+ memcpy(buf, wsi->frame_masking_nonce_04, 4);
+ memcpy(buf + 4, wsi->masking_key_04, 20);
+
+ /* concatenate the nonce with the connection key then hash it */
+
+ SHA1((unsigned char *)buf, 4 + 20, wsi->frame_mask_04);
+
+ /* start masking from first byte of masking key buffer */
+ wsi->frame_mask_index = 0;
+
+ return 0;
+}
+
/**
* libwebsocket_write() - Apply protocol then write data to client
return -1;
}
+ /*
+ * We don't really support the metaframe concept with FIN.
+ * Just set FIN on every packet for now
+ */
+
+ n |= 1 << 7;
+
if (len < 126) {
buf[-2] = n;
buf[-1] = len;
fprintf(stderr, "\n");
#endif
+ /*
+ * Deal with masking if appropriate
+ */
+
+ if (wsi->client_mode && wsi->ietf_spec_revision == 4) {
+
+ if (libwebsocket_04_frame_mask_generate(wsi)) {
+ fprintf(stderr, "libwebsocket_write: "
+ "frame mask generation failed\n");
+ return 1;
+ }
+
+ /*
+ * use the XOR masking against everything we send
+ * past the frame nonce
+ */
+
+ for (n = 0; n < (len + pre + post); n++)
+ buf[n - pre] = xor_mask(wsi, buf[n - pre]);
+
+ /* make space for the frame nonce in clear */
+ pre += 4;
+
+ /* copy the frame nonce into place */
+ memcpy(&buf[0 - pre], wsi->frame_masking_nonce_04, 4);
+ }
+
+
send_raw:
#ifdef LWS_OPENSSL_SUPPORT
if (use_ssl) {
return 0;
}
+
/**
* libwebsockets_remaining_packet_payload() - Bytes to come before "overall"
* rx packet is complete
#include <openssl/sha.h>
#include "libwebsockets.h"
-/* #define DEBUG */
+//#define DEBUG
#ifdef DEBUG
-#define debug \
- fprintf(stderr,
+static inline void debug(const char *format, ...) {
+ va_list ap;
+ va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap);
+}
#else
static inline void debug(const char *format, ...) { }
#endif
#define LWS_MAX_PROTOCOLS 10
#define MAX_WEBSOCKET_04_KEY_LEN 128
-#define SYSTEM_RANDOM_FILEPATH "/dev/random"
+#define SYSTEM_RANDOM_FILEPATH "/dev/urandom"
enum lws_websocket_opcodes_04 {
LWS_WS_OPCODE_04__CONTINUATION = 0,
WSI_STATE_HTTP,
WSI_STATE_HTTP_HEADERS,
WSI_STATE_DEAD_SOCKET,
- WSI_STATE_ESTABLISHED
+ WSI_STATE_ESTABLISHED,
+ WSI_STATE_CLIENT_UNCONNECTED
};
enum lws_token_indexes {
/* new for 04 */
WSI_TOKEN_KEY,
WSI_TOKEN_VERSION,
+ WSI_TOKEN_SWORIGIN,
+
+ /* client receives these */
+ WSI_TOKEN_ACCEPT,
+ WSI_TOKEN_NONCE,
+ WSI_TOKEN_HTTP,
/* always last real token index*/
WSI_TOKEN_COUNT,
struct libwebsocket *wsi[MAX_CLIENTS + 1];
struct pollfd fds[MAX_CLIENTS + 1];
int fds_count;
+ int listen_port;
#ifdef LWS_OPENSSL_SUPPORT
int use_ssl;
#endif
int pings_vs_pongs;
+ /* client support */
+ char initial_handshake_hash_base64[30];
+ int client_mode;
+
#ifdef LWS_OPENSSL_SUPPORT
SSL *ssl;
#endif
void *user_space;
};
+extern int
+libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c);
+
extern void
libwebsocket_close_and_free_session(struct libwebsocket *wsi);
libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len);
extern int
-lws_b64_encode_string(const char *in, char *out, int out_size);
+lws_b64_encode_string(const char *in, int in_len, char *out, int out_size);
extern int
lws_b64_decode_string(const char *in, char *out, int out_size);
-<h2>libwebsocket_create_server - Create the listening websockets server</h2>
+<h2>libwebsocket_create_context - Create the websocket handler</h2>
<i>struct libwebsocket_context *</i>
-<b>libwebsocket_create_server</b>
+<b>libwebsocket_create_context</b>
(<i>int</i> <b>port</b>,
<i>struct libwebsocket_protocols *</i> <b>protocols</b>,
<i>const char *</i> <b>ssl_cert_filepath</b>,
<h3>Arguments</h3>
<dl>
<dt><b>port</b>
-<dd>Port to listen on
+<dd>Port to listen on... you can use 0 to suppress listening on
+any port, that's what you want if you are not running a
+websocket server at all but just using it as a client
<dt><b>protocols</b>
<dd>Array of structures listing supported protocols and a protocol-
specific callback for each one. The list is ended with an
-bin_PROGRAMS=libwebsockets-test-server
+bin_PROGRAMS=libwebsockets-test-server libwebsockets-test-client
libwebsockets_test_server_SOURCES=test-server.c
libwebsockets_test_server_LDADD=-L../lib -lwebsockets
-
+libwebsockets_test_client_SOURCES=test-client.c
+libwebsockets_test_client_LDADD=-L../lib -lwebsockets
#
# cook a random test cert and key
# notice your real cert and key will want to be 0600 permissions
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-bin_PROGRAMS = libwebsockets-test-server$(EXEEXT)
+bin_PROGRAMS = libwebsockets-test-server$(EXEEXT) \
+ libwebsockets-test-client$(EXEEXT)
subdir = test-server
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
CONFIG_CLEAN_VPATH_FILES =
am__installdirs = "$(DESTDIR)$(bindir)"
PROGRAMS = $(bin_PROGRAMS)
+am_libwebsockets_test_client_OBJECTS = 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_server_OBJECTS = \
$(am_libwebsockets_test_server_OBJECTS)
LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
$(LDFLAGS) -o $@
-SOURCES = $(libwebsockets_test_server_SOURCES)
-DIST_SOURCES = $(libwebsockets_test_server_SOURCES)
+SOURCES = $(libwebsockets_test_client_SOURCES) \
+ $(libwebsockets_test_server_SOURCES)
+DIST_SOURCES = $(libwebsockets_test_client_SOURCES) \
+ $(libwebsockets_test_server_SOURCES)
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
top_srcdir = @top_srcdir@
libwebsockets_test_server_SOURCES = test-server.c
libwebsockets_test_server_LDADD = -L../lib -lwebsockets
+libwebsockets_test_client_SOURCES = test-client.c
+libwebsockets_test_client_LDADD = -L../lib -lwebsockets
all: all-am
.SUFFIXES:
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
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-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)
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@
.c.o:
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
--- /dev/null
+/*
+ * libwebsockets-test-client - libwebsockets test implementation
+ *
+ * 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 "../lib/libwebsockets.h"
+#include <poll.h>
+
+/*
+ * This demo shows how to connect multiple websockets simultaneously to a
+ * websocket server (there is no restriction on their having to be the same
+ * server just it simplifies the demo).
+ *
+ * dumb-increment-protocol: we connect to the server and print the number
+ * 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)
+ */
+
+enum demo_protocols {
+
+ PROTOCOL_DUMB_INCREMENT,
+ PROTOCOL_LWS_MIRROR,
+
+ /* always last */
+ DEMO_PROTOCOL_COUNT
+};
+
+
+/* dumb_increment protocol */
+
+static int
+callback_dumb_increment(struct libwebsocket *wsi,
+ enum libwebsocket_callback_reasons reason,
+ void *user, void *in, size_t len)
+{
+ switch (reason) {
+
+ case LWS_CALLBACK_CLIENT_RECEIVE:
+ fprintf(stderr, "rx %d '%s'\n", len, in);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+/* 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)
+{
+ switch (reason) {
+
+ case LWS_CALLBACK_CLIENT_RECEIVE:
+// fprintf(stderr, "rx %d '%s'\n", len, in);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+/* list of supported protocols and callbacks */
+
+static struct libwebsocket_protocols protocols[] = {
+
+ [PROTOCOL_DUMB_INCREMENT] = {
+ .name = "dumb-increment-protocol",
+ .callback = callback_dumb_increment,
+ },
+ [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, 's' },
+ { NULL, 0, 0, 0 }
+};
+
+
+int main(int argc, char **argv)
+{
+ int n = 0;
+ int port = 7681;
+ int use_ssl = 0;
+ struct libwebsocket_context *context;
+ 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> "
+ "licensed under LGPL2.1\n");
+
+ if (argc < 2)
+ goto usage;
+
+ optind++;
+
+ while (n >= 0) {
+ n = getopt_long(argc, argv, "hsp:", options, NULL);
+ if (n < 0)
+ continue;
+ switch (n) {
+ case 's':
+ use_ssl = 1;
+ break;
+ case 'p':
+ port = atoi(optarg);
+ break;
+ case 'h':
+ goto usage;
+ }
+ }
+
+ /*
+ * create the websockets context. This tracks open connections and
+ * knows how to route any traffic and which protocol version to use,
+ * and if each connection is client or server side.
+ *
+ * For this client-only demo, we tell it to not listen on any port.
+ */
+
+ 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_dumb = libwebsocket_client_connect(context, address, port, "/",
+ "http://host", "origin",
+ protocols[PROTOCOL_DUMB_INCREMENT].name);
+
+ if (wsi_dumb == NULL) {
+ fprintf(stderr, "libwebsocket dumb connect failed\n");
+ return -1;
+ }
+
+ /* create a client websocket using mirror protocol */
+
+ wsi_mirror = libwebsocket_client_connect(context, address, port, "/",
+ "http://host", "origin",
+ protocols[PROTOCOL_LWS_MIRROR].name);
+
+ if (wsi_mirror == NULL) {
+ fprintf(stderr, "libwebsocket dumb connect failed\n");
+ return -1;
+ }
+
+ fprintf(stderr, "Websocket connections opened\n");
+
+ /*
+ * sit there servicing the websocket context to handle incoming
+ * packets, and drawing random circles on the mirror protocol websocket
+ */
+
+ 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);
+ }
+
+ libwebsocket_client_close(wsi_dumb);
+ libwebsocket_client_close(wsi_mirror);
+
+ return 0;
+
+usage:
+ fprintf(stderr, "Usage: libwebsockets-test-client "
+ "<server address> [--port=<p>] "
+ "[--ssl]\n");
+ return 1;
+}
LWS_SEND_BUFFER_POST_PADDING];
int port = 7681;
int use_ssl = 0;
- struct libwebsocket_context *server;
+ struct libwebsocket_context *context;
fprintf(stderr, "libwebsockets test server\n"
"(C) Copyright 2010 Andy Green <andy@warmcat.com> "
if (!use_ssl)
cert_path = key_path = NULL;
- server = libwebsocket_create_server(port, protocols, cert_path,
+ context = libwebsocket_create_context(port, protocols, cert_path,
key_path, -1, -1);
- if (server == NULL) {
+ if (context == NULL) {
fprintf(stderr, "libwebsocket init failed\n");
return -1;
}
* immediately and quickly.
*/
- libwebsocket_service(server, 0);
+ libwebsocket_service(context, 0);
}
#else
* don't have to take care about it.
*/
- n = libwebsockets_fork_service_loop(server);
+ n = libwebsockets_fork_service_loop(context);
if (n < 0) {
fprintf(stderr, "Unable to fork service loop %d\n", n);
return 1;