mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
introduce-client-support.patch
Signed-off-by: Andy Green <andy@warmcat.com>
This commit is contained in:
parent
ed11a02201
commit
4739e5c450
15 changed files with 1425 additions and 102 deletions
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
394
lib/client-handshake.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
474
lib/parsers.c
474
lib/parsers.c
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
390
test-server/test-client.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue