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>