test-case-for-mozilla-issue
diff --git a/Makefile b/Makefile
index 402227a..251925d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,14 @@
+CFLAGS= -Wall -Werror -rdynamic -fPIC -c
+
all:
- gcc -Wall -Werror -rdynamic -fPIC -c libwebsockets.c
- gcc -Wall -Werror -rdynamic -fPIC -c md5.c
+ gcc $(CFLAGS) libwebsockets.c
+ gcc $(CFLAGS) md5.c
gcc libwebsockets.o md5.o --shared -o libwebsockets.so
+
+ gcc $(CFLAGS) test-server.c
+ gcc test-server.o ./libwebsockets.so -o test-server
clean:
- rm -f *.o *.so
+ rm -f *.o *.so test-server
+
diff --git a/libwebsockets.c b/libwebsockets.c
index 9e7c1d8..6f2e03b 100644
--- a/libwebsockets.c
+++ b/libwebsockets.c
@@ -3,6 +3,7 @@
#include <string.h>
#include <ctype.h>
#include <unistd.h>
+#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
@@ -14,8 +15,69 @@
#include "libwebsockets.h"
void md5(const unsigned char *input, int ilen, unsigned char output[16]);
+static void libwebsocket_service(struct libwebsocket *wsi, int sock);
-void dostuff(struct libwebsocket *wsi, int sock);
+#define LWS_MAX_HEADER_NAME_LENGTH 64
+#define LWS_MAX_HEADER_LEN 4096
+#define LWS_INITIAL_HDR_ALLOC 256
+#define LWS_ADDITIONAL_HDR_ALLOC 64
+
+
+enum lws_connection_states {
+ WSI_STATE_CLOSED,
+ WSI_STATE_HANDSHAKE_RX,
+ WSI_STATE_DEAD_SOCKET,
+ WSI_STATE_ESTABLISHED
+};
+
+enum lws_token_indexes {
+ WSI_TOKEN_GET_URI,
+ WSI_TOKEN_HOST,
+ WSI_TOKEN_CONNECTION,
+ WSI_TOKEN_KEY1,
+ WSI_TOKEN_KEY2,
+ WSI_TOKEN_PROTOCOL,
+ WSI_TOKEN_UPGRADE,
+ WSI_TOKEN_ORIGIN,
+ WSI_TOKEN_CHALLENGE,
+
+ /* always last real token index*/
+ WSI_TOKEN_COUNT,
+ /* parser state additions */
+ WSI_TOKEN_NAME_PART,
+ WSI_TOKEN_SKIPPING,
+ WSI_TOKEN_SKIPPING_SAW_CR,
+ WSI_PARSING_COMPLETE
+};
+
+
+struct lws_tokens {
+ char * token;
+ int token_len;
+};
+
+
+/*
+ * This is totally opaque to code using the library. It's exported as a
+ * forward-reference pointer-only declaration.
+ */
+
+struct libwebsocket {
+ int (*callback)(struct libwebsocket *,
+ enum libwebsocket_callback_reasons reason);
+
+ enum lws_connection_states state;
+
+ char name_buffer[LWS_MAX_HEADER_NAME_LENGTH];
+ int name_buffer_pos;
+ int current_alloc_len;
+ enum lws_token_indexes parser_state;
+ struct lws_tokens utf8_token[WSI_TOKEN_COUNT];
+ int ietf_spec_revision;
+
+ int sock;
+};
+
const struct lws_tokens lws_tokens[WSI_TOKEN_COUNT] = {
{ "GET ", 4 },
@@ -29,24 +91,47 @@
{ "\x0d\x0a", 2 },
};
-int libwebsocket_init(struct libwebsocket *wsi, int port)
+int libwebsocket_create_server(int port, int (*callback)(struct libwebsocket *, enum libwebsocket_callback_reasons))
{
int n;
int sockfd, newsockfd;
unsigned int clilen;
struct sockaddr_in serv_addr, cli_addr;
int pid;
+ struct libwebsocket *wsi = malloc(sizeof(struct libwebsocket));
+
+ if (!wsi)
+ return -1;
wsi->state = WSI_STATE_CLOSED;
wsi->name_buffer_pos = 0;
- wsi->response = NULL;
for (n = 0; n < WSI_TOKEN_COUNT; n++) {
wsi->utf8_token[n].token = NULL;
wsi->utf8_token[n].token_len = 0;
}
+
+ wsi->callback = callback;
+ wsi->ietf_spec_revision = 0;
- /* fork off a master server for this websocket server */
+ /* sit there listening for connects, accept and spawn session servers */
+
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0) {
+ fprintf(stderr, "ERROR opening socket");
+ }
+ 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 %d %d\n", n, errno);
+ return -1;
+ }
+
+ /* fork off a master server for this websocket server */
n = fork();
if (n < 0) {
@@ -59,18 +144,6 @@
if (n)
return 0;
- /* sit there listening for connects, accept and spawn session servers */
-
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
- if (sockfd < 0)
- fprintf(stderr, "ERROR opening socket");
- 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);
- if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
- fprintf(stderr, "ERROR on binding");
listen(sockfd, 5);
@@ -78,23 +151,32 @@
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
- if (newsockfd < 0)
+ if (newsockfd < 0) {
fprintf(stderr, "ERROR on accept");
+ continue;
+ }
/* fork off a new server instance */
pid = fork();
- if (pid < 0)
+ if (pid < 0) {
fprintf(stderr, "ERROR on fork");
- if (!pid) {
- close(sockfd);
-
- /* sit in dostuff() until session socket closed */
-
- dostuff(wsi, newsockfd);
- exit(0);
- } else
+ continue;
+ }
+
+ if (pid) {
close(newsockfd);
+ continue;
+ }
+
+ /* we are the session process */
+
+ close(sockfd);
+
+ /* sit in libwebsocket_service() until session socket closed */
+
+ libwebsocket_service(wsi, newsockfd);
+ exit(0);
}
}
@@ -104,15 +186,12 @@
wsi->state = WSI_STATE_DEAD_SOCKET;
- if (wsi->websocket_closed_callback)
- wsi->websocket_closed_callback(wsi);
+ if (wsi->callback)
+ wsi->callback(wsi, LWS_CALLBACK_CLOSED);
for (n = 0; n < WSI_TOKEN_COUNT; n++)
if (wsi->utf8_token[n].token)
free(wsi->utf8_token[n].token);
-
- if (wsi->response)
- free(wsi->response);
}
@@ -174,12 +253,12 @@
wsi->utf8_token[wsi->parser_state].token_len++] = c;
/* special payload limiting */
- if (wsi->parser_state == WSI_TOKEN_CHALLENGE)
- if (wsi->utf8_token[wsi->parser_state].token_len == 8) {
-// fprintf(stderr, "Setting WSI_PARSING_COMPLETE\n");
- wsi->parser_state = WSI_PARSING_COMPLETE;
- break;
- }
+ if (wsi->parser_state == WSI_TOKEN_CHALLENGE &&
+ wsi->utf8_token[wsi->parser_state].token_len == 8) {
+// fprintf(stderr, "Setting WSI_PARSING_COMPLETE\n");
+ wsi->parser_state = WSI_PARSING_COMPLETE;
+ break;
+ }
break;
@@ -274,7 +353,8 @@
/*
* We have to take care about parsing because the headers may be split
* into multiple fragments. They may contain unknown headers with arbitrary
- * argument lengths.
+ * argument lengths. So, we parse using a single-character at a time state
+ * machine that is completely independent of packet size.
*/
int libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len)
@@ -283,11 +363,13 @@
char *p;
unsigned int key1, key2;
unsigned char sum[16];
+ char *response;
switch (wsi->state) {
case WSI_STATE_CLOSED:
wsi->state = WSI_STATE_HANDSHAKE_RX;
wsi->parser_state = WSI_TOKEN_NAME_PART;
+ /* fallthru */
case WSI_STATE_HANDSHAKE_RX:
fprintf(stderr, "issuing %ld bytes to parser\n", len);
@@ -320,7 +402,7 @@
/* create the response packet */
- wsi->response = malloc(256 +
+ response = malloc(256 +
wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len +
wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len +
wsi->utf8_token[WSI_TOKEN_HOST].token_len +
@@ -331,7 +413,7 @@
fprintf(stderr, "'%s;\n", wsi->utf8_token[WSI_TOKEN_HOST].token);
- p = wsi->response;
+ p = response;
strcpy(p, "HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0aUpgrade: WebSocket\x0d\x0a");
p += strlen("HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0aUpgrade: WebSocket\x0d\x0a");
strcpy(p, "Connection: Upgrade\x0d\x0aSec-WebSocket-Origin: ");
@@ -349,15 +431,22 @@
if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) {
strcpy(p, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
p += wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len;
+ } else {
+ strcpy(p, "none");
+ p += strlen("none");
}
strcpy(p, "\x0d\x0a\x0d\x0a");
p += strlen("\x0d\x0a\x0d\x0a");
+ /* convert the two keys into 32-bit integers */
+
if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY1].token, &key1))
goto bail;
if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY2].token, &key2))
goto bail;
+
+ /* lay them out in network byte order (MSB first */
sum[0] = key1 >> 24;
sum[1] = key1 >> 16;
@@ -367,20 +456,42 @@
sum[5] = key2 >> 16;
sum[6] = key2 >> 8;
sum[7] = key2;
+
+ /* follow them with the challenge token we were sent */
+
memcpy(&sum[8], wsi->utf8_token[WSI_TOKEN_CHALLENGE].token, 8);
+ /*
+ * compute the md5sum of that 16-byte series and use as our
+ * payload after our headers
+ */
+
md5(sum, 16, (unsigned char *)p);
p += 16;
- wsi->response_length = p - wsi->response;
-
- wsi->state = WSI_STATE_ISSUE_HANDSHAKE;
+ /* it's complete, go ahead and send it */
- if (wsi->websocket_established_callback)
- wsi->websocket_established_callback(wsi);
+ fprintf(stderr, "issuing response packet %d len\n",
+ (int)(p - response));
+ fwrite(response, 1, p - response, stderr);
+
+ n = write(wsi->sock, response, p - response);
+ if (n < 0) {
+ fprintf(stderr, "ERROR writing to socket");
+ goto bail;
+ }
+
+ free(response);
+ wsi->state = WSI_STATE_ESTABLISHED;
+
+ /* notify user code that we're ready to roll */
+
+ if (wsi->callback)
+ wsi->callback(wsi, LWS_CALLBACK_ESTABLISHED);
break;
case WSI_STATE_ESTABLISHED:
+ fprintf(stderr, "received %ld byte packet\n", len);
break;
default:
break;
@@ -393,8 +504,74 @@
return -1;
}
+int libwebsocket_write(struct libwebsocket * wsi, void *buf, size_t len)
+{
+ int n;
+ unsigned char hdr[9];
+
+ if (wsi->state != WSI_STATE_ESTABLISHED)
+ return -1;
-void dostuff(struct libwebsocket *wsi, int sock)
+ switch (wsi->ietf_spec_revision) {
+ /* chrome */
+ case 0:
+ hdr[0] = 0xff;
+ hdr[1] = len >> 56;
+ hdr[2] = len >> 48;
+ hdr[3] = len >> 40;
+ hdr[4] = len >> 32;
+ hdr[5] = len >> 24;
+ hdr[6] = len >> 16;
+ hdr[7] = len >> 8;
+ hdr[8] = len;
+
+ n = write(wsi->sock, hdr, sizeof hdr);
+ if (n < 0) {
+ fprintf(stderr, "ERROR writing to socket");
+ return -1;
+ }
+ break;
+ /* just an unimplemented spec right now apparently */
+ case 2:
+ n = 0;
+ if (len < 126) {
+ hdr[n++] = 0x04;
+ hdr[n++] = len;
+ } else {
+ if (len < 65536) {
+ hdr[n++] = 0x04; /* text frame */
+ hdr[n++] = 126;
+ hdr[n++] = len >> 8;
+ hdr[n++] = len;
+ } else {
+ hdr[n++] = 0x04;
+ hdr[n++] = 127;
+ hdr[n++] = len >> 24;
+ hdr[n++] = len >> 16;
+ hdr[n++] = len >> 8;
+ hdr[n++] = len;
+ }
+ }
+ n = write(wsi->sock, hdr, n);
+ if (n < 0) {
+ fprintf(stderr, "ERROR writing to socket");
+ return -1;
+ }
+ break;
+ }
+
+ n = write(wsi->sock, buf, len);
+ if (n < 0) {
+ fprintf(stderr, "ERROR writing to socket");
+ return -1;
+ }
+
+ fprintf(stderr, "written %d bytes to websocket\n", (int)len);
+
+ return 0;
+}
+
+static void libwebsocket_service(struct libwebsocket *wsi, int sock)
{
int n;
unsigned char buf[256];
@@ -435,23 +612,11 @@
libwebsocket_read(wsi, buf, n);
}
- if (wsi->state == WSI_STATE_ISSUE_HANDSHAKE) {
-
- fprintf(stderr, "issuing response packet %d len\n", wsi->response_length);
-
- fwrite(wsi->response, 1, wsi->response_length, stderr);
-
- n = write(sock, wsi->response, wsi->response_length);
- if (n < 0) {
- fprintf(stderr, "ERROR writing to socket");
- exit(1);
- }
- wsi->state = WSI_STATE_ESTABLISHED;
+ if (wsi->state != WSI_STATE_ESTABLISHED)
continue;
- }
- if (wsi->websocket_send_callback)
- wsi->websocket_send_callback(wsi);
+ if (wsi->callback)
+ wsi->callback(wsi, LWS_CALLBACK_SEND);
}
}
diff --git a/libwebsockets.h b/libwebsockets.h
index 5258a17..870b7fe 100644
--- a/libwebsockets.h
+++ b/libwebsockets.h
@@ -1,68 +1,15 @@
-#define LWS_MAX_HEADER_NAME_LENGTH 64
-#define LWS_MAX_HEADER_LEN 4096
-#define LWS_INITIAL_HDR_ALLOC 256
-#define LWS_ADDITIONAL_HDR_ALLOC 64
-
-
-enum lws_connection_states {
- WSI_STATE_CLOSED,
- WSI_STATE_HANDSHAKE_RX,
- WSI_STATE_ISSUE_HANDSHAKE,
- WSI_STATE_DEAD_SOCKET,
- WSI_STATE_ESTABLISHED
+enum libwebsocket_callback_reasons {
+ LWS_CALLBACK_ESTABLISHED,
+ LWS_CALLBACK_CLOSED,
+ LWS_CALLBACK_SEND,
+ LWS_CALLBACK_RECEIVE,
};
-enum lws_token_indexes {
- WSI_TOKEN_GET_URI,
- WSI_TOKEN_HOST,
- WSI_TOKEN_CONNECTION,
- WSI_TOKEN_KEY1,
- WSI_TOKEN_KEY2,
- WSI_TOKEN_PROTOCOL,
- WSI_TOKEN_UPGRADE,
- WSI_TOKEN_ORIGIN,
- WSI_TOKEN_CHALLENGE,
-
- /* always last real token index*/
- WSI_TOKEN_COUNT,
- /* parser state additions */
- WSI_TOKEN_NAME_PART,
- WSI_TOKEN_SKIPPING,
- WSI_TOKEN_SKIPPING_SAW_CR,
- WSI_PARSING_COMPLETE
-};
+struct libwebsocket;
+extern int libwebsocket_create_server(int port,
+ int (*callback)(struct libwebsocket *,
+ enum libwebsocket_callback_reasons));
-struct lws_tokens {
- char * token;
- int token_len;
-};
-
-struct libwebsocket {
-
- /* set these up before calling libwebsocket_init */
-
- int (*websocket_established_callback)(struct libwebsocket *);
- int (*websocket_closed_callback)(struct libwebsocket *);
- int (*websocket_send_callback)(struct libwebsocket *);
- int (*websocket_receive_callback)(struct libwebsocket *);
-
- /* these are all opaque and maintained by the library */
-
- enum lws_connection_states state;
-
- char name_buffer[LWS_MAX_HEADER_NAME_LENGTH];
- int name_buffer_pos;
- int current_alloc_len;
- enum lws_token_indexes parser_state;
- struct lws_tokens utf8_token[WSI_TOKEN_COUNT];
- char * response;
- int response_length;
-
- int sock;
-};
-
-
-extern int libwebsocket_init(struct libwebsocket *wsi, int port);
-
+extern int libwebsocket_write(struct libwebsocket *, void *buf, size_t len);
diff --git a/test-server.c b/test-server.c
new file mode 100644
index 0000000..0056e80
--- /dev/null
+++ b/test-server.c
@@ -0,0 +1,55 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "libwebsockets.h"
+
+#define PORT 7681
+
+int websocket_callback(struct libwebsocket * wsi,
+ enum libwebsocket_callback_reasons reason)
+{
+ int n;
+ char buf[256];
+ static int bump;
+
+ switch (reason) {
+ case LWS_CALLBACK_ESTABLISHED:
+ fprintf(stderr, "Websocket connection established\n");
+ break;
+
+ case LWS_CALLBACK_CLOSED:
+ fprintf(stderr, "Websocket connection closed\n");
+ break;
+
+ case LWS_CALLBACK_SEND:
+ sleep(1);
+ n = sprintf(buf, "%d\n", bump++);
+ n = libwebsocket_write(wsi, buf, n);
+ if (n < 0) {
+ fprintf(stderr, "ERROR writing to socket");
+ exit(1);
+ }
+ break;
+
+ case LWS_CALLBACK_RECEIVE:
+ break;
+ }
+ return 0;
+}
+
+
+int main(int argv, char **argc)
+{
+ if (libwebsocket_create_server(PORT, websocket_callback) < 0) {
+ fprintf(stderr, "libwebsocket init failed\n");
+ return -1;
+ }
+
+ fprintf(stderr, "Listening on port %d\n", PORT);
+
+ while (1)
+ sleep(1);
+
+ return 0;
+}
diff --git a/test.html b/test.html
new file mode 100644
index 0000000..5d8b46f
--- /dev/null
+++ b/test.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset=utf-8 />
+ <title>Minimal Websocket message sender</title>
+</head>
+
+<body>
+<script>
+ var pos = 0;
+
+ var socket = new WebSocket("ws://127.0.0.1:7681");
+
+ try {
+// alert('<p class="event">Socket Status: '+socket.readyState);
+
+ socket.onopen = function(){
+ alert('<p class="event">Socket Status: '+socket.readyState+' (open)');
+ }
+
+ socket.onmessage = got_packet;
+
+ socket.onclose = function(){
+ alert('<p class="event">Socket Status: (Closed)');
+ }
+ } catch(exception){
+ alert('<p>Error'+exception);
+ }
+
+function got_packet(msg){
+// alert('got packet' + msg);
+
+ document.write(msg.data + "\n");
+
+}
+</script>
+
+</body>
+</html>