test-case-for-mozilla-issue
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);
 	}
 }