introduce-client-support.patch

Signed-off-by: Andy Green <andy@warmcat.com>
This commit is contained in:
Andy Green 2011-01-22 12:51:57 +00:00
parent ed11a02201
commit 4739e5c450
15 changed files with 1425 additions and 102 deletions

View file

@ -15,8 +15,8 @@ $ libwebsockets-test-server
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
@ -29,6 +29,9 @@ Incrementing numbers should appear in the browser display.
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
@ -55,15 +58,27 @@ libwebsockets from your own main loop instead. Use the
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>

View file

@ -5,6 +5,7 @@ dist_libwebsockets_la_SOURCES=libwebsockets.c \
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
@ -13,6 +14,7 @@ all-local:
../scripts/kernel-doc -html \
libwebsockets.c \
parsers.c \
client-handshake.c \
libwebsockets.h \
> ../libwebsockets-api-doc.html

View file

@ -72,7 +72,8 @@ LTLIBRARIES = $(lib_LTLIBRARIES)
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) \
@ -216,6 +217,7 @@ dist_libwebsockets_la_SOURCES = libwebsockets.c \
parsers.c \
libwebsockets.h \
base64-decode.c \
client-handshake.c \
private-libwebsockets.h
libwebsockets_la_CFLAGS = -Wall -Werror -std=gnu99 -pedantic -rdynamic -fPIC -c
@ -295,6 +297,7 @@ distclean-compile:
-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@
@ -348,6 +351,13 @@ libwebsockets_la-base64-decode.lo: base64-decode.c
@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
@ -582,6 +592,7 @@ all-local:
../scripts/kernel-doc -html \
libwebsockets.c \
parsers.c \
client-handshake.c \
libwebsockets.h \
> ../libwebsockets-api-doc.html

View file

@ -48,7 +48,7 @@ static const char decode[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW"
"$$$$$$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;
@ -56,12 +56,13 @@ lws_b64_encode_string(const char *in, char *out, int out_size)
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;
}
@ -175,7 +176,8 @@ lws_b64_selftest(void)
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);

394
lib/client-handshake.c Normal file
View file

@ -0,0 +1,394 @@
#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;
}

View file

@ -240,12 +240,13 @@ handshake_04(struct libwebsocket *wsi)
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) {
@ -262,7 +263,7 @@ handshake_04(struct libwebsocket *wsi)
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");
@ -307,31 +308,37 @@ handshake_04(struct libwebsocket *wsi)
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;
}
@ -455,12 +462,22 @@ libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len)
#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 ||
@ -527,6 +544,7 @@ libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len)
goto bail;
break;
case 4: /* 04 */
debug("libwebsocket_parse calling handshake_04\n");
if (handshake_04(wsi))
goto bail;
break;
@ -539,6 +557,14 @@ libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len)
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;

View file

@ -131,6 +131,11 @@ libwebsocket_poll_connections(struct libwebsocket_context *this)
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
@ -207,7 +212,13 @@ libwebsocket_service(struct libwebsocket_context *this, int timeout_ms)
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");
@ -308,6 +319,7 @@ libwebsocket_service(struct libwebsocket_context *this, int timeout_ms)
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]->
@ -378,8 +390,10 @@ fatal:
/**
* 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.
@ -419,14 +433,14 @@ fatal:
*/
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;
@ -487,7 +501,7 @@ libwebsocket_create_server(int port,
/* 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));
@ -512,28 +526,33 @@ libwebsocket_create_server(int port,
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;
}
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);
/* allow us to restart even if old sockets in TIME_WAIT */
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
n = bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
if (n < 0) {
fprintf(stderr, "ERROR on binding to port %d (%d %d)\n",
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",
port, n, errno);
return NULL;
return NULL;
}
}
/* drop any root privs for this process */
@ -561,8 +580,10 @@ libwebsocket_create_server(int port,
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 */
@ -617,6 +638,7 @@ libwebsocket_create_server(int port,
return this;
}
#ifndef LWS_NO_FORK
/**
@ -653,7 +675,7 @@ libwebsockets_fork_service_loop(struct libwebsocket_context *this)
}
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);
@ -664,7 +686,8 @@ libwebsockets_fork_service_loop(struct libwebsocket_context *this)
return -1;
}
this->protocols[client - 1].broadcast_socket_user_fd = fd;
this->protocols[client - 1].broadcast_socket_user_fd =
fd;
}
@ -749,12 +772,10 @@ libwebsockets_broadcast(const struct libwebsocket_protocols *protocol,
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;

View file

@ -22,11 +22,13 @@
#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
};
@ -135,7 +137,7 @@ struct libwebsocket_protocols {
};
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);
@ -170,7 +172,9 @@ libwebsocket_service(struct libwebsocket_context *this, int timeout_ms);
* 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
@ -193,4 +197,16 @@ libwebsockets_get_protocol(struct libwebsocket *wsi);
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

View file

@ -22,20 +22,25 @@
#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)
@ -51,10 +56,14 @@ 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 */
@ -140,6 +149,7 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c)
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;
@ -155,15 +165,33 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c)
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 */
@ -193,7 +221,7 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c)
}
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)
@ -217,7 +245,8 @@ static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c)
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;
@ -318,10 +347,10 @@ static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c)
* 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;
}
@ -342,10 +371,11 @@ static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c)
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;
}
@ -382,6 +412,7 @@ static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c)
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;
@ -390,14 +421,14 @@ static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c)
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 =
@ -405,7 +436,7 @@ static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c)
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 */
@ -416,37 +447,37 @@ static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c)
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;
@ -491,7 +522,308 @@ issue:
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;
}
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
*/
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->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
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;
@ -538,17 +870,23 @@ spill:
*/
if (wsi->protocol->callback)
wsi->protocol->callback(wsi, LWS_CALLBACK_RECEIVE,
wsi->user_space,
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);
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)
{
@ -564,15 +902,55 @@ int libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi,
/* 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
@ -676,6 +1054,13 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
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;
@ -718,6 +1103,34 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
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) {
@ -795,6 +1208,7 @@ int libwebsockets_serve_http_file(struct libwebsocket *wsi, const char *file,
return 0;
}
/**
* libwebsockets_remaining_packet_payload() - Bytes to come before "overall"
* rx packet is complete

View file

@ -51,12 +51,14 @@
#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
@ -77,7 +79,7 @@ extern int use_ssl;
#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,
@ -92,7 +94,8 @@ enum lws_connection_states {
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 {
@ -110,6 +113,12 @@ 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,
@ -159,6 +168,7 @@ struct libwebsocket_context {
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
@ -204,6 +214,10 @@ struct libwebsocket {
int pings_vs_pongs;
/* client support */
char initial_handshake_hash_base64[30];
int client_mode;
#ifdef LWS_OPENSSL_SUPPORT
SSL *ssl;
#endif
@ -211,6 +225,9 @@ struct libwebsocket {
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);
@ -225,7 +242,7 @@ extern int
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);

View file

@ -1,6 +1,6 @@
<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>,
@ -10,7 +10,9 @@
<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

View file

@ -1,7 +1,8 @@
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

View file

@ -34,7 +34,8 @@ PRE_UNINSTALL = :
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
@ -47,6 +48,10 @@ CONFIG_CLEAN_FILES =
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)
@ -64,8 +69,10 @@ CCLD = $(CC)
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)
@ -184,6 +191,8 @@ top_builddir = @top_builddir@
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:
@ -261,6 +270,9 @@ clean-binPROGRAMS:
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)
@ -271,6 +283,7 @@ mostlyclean-compile:
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:
@ -501,7 +514,6 @@ uninstall-am: uninstall-binPROGRAMS
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

390
test-server/test-client.c Normal file
View file

@ -0,0 +1,390 @@
/*
* 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;
}

View file

@ -230,7 +230,7 @@ int main(int argc, char **argv)
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> "
@ -257,9 +257,9 @@ int main(int argc, char **argv)
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;
}
@ -304,7 +304,7 @@ int main(int argc, char **argv)
* immediately and quickly.
*/
libwebsocket_service(server, 0);
libwebsocket_service(context, 0);
}
#else
@ -320,7 +320,7 @@ int main(int argc, char **argv)
* 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;