implement-v76-write-and-recognize-close-req.patch

Signed-off-by: Andy Green <andy@warmcat.com>
diff --git a/libwebsockets.c b/libwebsockets.c
index 6f2e03b..5b2628d 100644
--- a/libwebsockets.c
+++ b/libwebsockets.c
@@ -23,6 +23,30 @@
 #define LWS_ADDITIONAL_HDR_ALLOC 64
 
 
+/*
+ * Chrome (v0)
+ * 
+ * GET / HTTP/1.1
+ * Upgrade: WebSocket
+ * Connection: Upgrade
+ * Host: 127.0.0.1:7681
+ * Origin: null
+ * Sec-WebSocket-Key1: +46 3      1 75 7Y 60
+ * Sec-WebSocket-Key2: m^+J358s0 6N 6e0 Q1 0  ~4~
+ *
+ * Firefox (v76)
+ *
+ * GET / HTTP/1.1
+ * Upgrade: WebSocket
+ * Host: 127.0.0.1:7681
+ * Connection: Upgrade
+ * Sec-WebSocket-Key1: EC."/$14 7 687YG+gZ 44d 16
+ * Origin: file://
+ * Sec-WebSocket-Key2: 2  /  9 0. 4 B8 77|ov968
+ *
+ */
+
+
 enum lws_connection_states {
 	WSI_STATE_CLOSED,
 	WSI_STATE_HANDSHAKE_RX,
@@ -50,6 +74,15 @@
 	WSI_PARSING_COMPLETE
 };
 
+enum lws_rx_parse_state {
+	LWS_RXPS_NEW,
+	
+	LWS_RXPS_SEEN_76_FF,
+	LWS_RXPS_PULLING_76_LENGTH,
+	
+	LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED
+};
+
 
 struct lws_tokens {
 	char * token;
@@ -64,7 +97,7 @@
 
 struct libwebsocket {
 	int (*callback)(struct libwebsocket *,
-				     enum libwebsocket_callback_reasons reason);
+		enum libwebsocket_callback_reasons reason, void *, size_t);
 
 	enum lws_connection_states state;
 
@@ -76,6 +109,9 @@
 	int ietf_spec_revision;
 	
 	int sock;
+
+	enum lws_rx_parse_state lws_rx_parse_state;
+	size_t rx_packet_length;
 };
 
 
@@ -91,7 +127,8 @@
 	{ "\x0d\x0a", 2 },
 };
 
-int libwebsocket_create_server(int port, int (*callback)(struct libwebsocket *, enum libwebsocket_callback_reasons))
+
+int libwebsocket_create_server(int port, int (*callback)(struct libwebsocket *, enum libwebsocket_callback_reasons, void *, size_t))
 {
 	int n;
 	int sockfd, newsockfd;
@@ -112,7 +149,8 @@
 	}
 	
 	wsi->callback = callback;
-	wsi->ietf_spec_revision = 0;
+//	wsi->ietf_spec_revision = 0;
+ 	wsi->ietf_spec_revision = 76;
  
 	/* sit there listening for connects, accept and spawn session servers */
  
@@ -187,7 +225,7 @@
 	wsi->state = WSI_STATE_DEAD_SOCKET;
 
 	if (wsi->callback)
-		wsi->callback(wsi, LWS_CALLBACK_CLOSED);
+		wsi->callback(wsi, LWS_CALLBACK_CLOSED, NULL, 0);
 
 	for (n = 0; n < WSI_TOKEN_COUNT; n++)
 		if (wsi->utf8_token[n].token)
@@ -226,13 +264,14 @@
 		}
 
 		/* allocate appropriate memory */
-		if (wsi->utf8_token[wsi->parser_state].token_len == wsi->current_alloc_len - 1) {
+		if (wsi->utf8_token[wsi->parser_state].token_len ==
+						   wsi->current_alloc_len - 1) {
 			/* need to extend */
 			wsi->current_alloc_len += LWS_ADDITIONAL_HDR_ALLOC;
 			if (wsi->current_alloc_len >= LWS_MAX_HEADER_LEN) {
 				/* it's waaay to much payload, fail it */
 				strcpy(wsi->utf8_token[wsi->parser_state].token,
-				    "!!! Length exceeded maximum supported !!!");
+				   "!!! Length exceeded maximum supported !!!");
 				wsi->parser_state = WSI_TOKEN_SKIPPING;
 				break;
 			}
@@ -349,6 +388,76 @@
 	return 0;
 }
 
+static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c)
+{
+	int n;
+	unsigned char buf[2];
+
+	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 76:
+			if (c == 0xff)
+				wsi->lws_rx_parse_state = LWS_RXPS_SEEN_76_FF;
+			break;
+		case 0:
+			break;
+		}
+		break;
+	case LWS_RXPS_SEEN_76_FF:
+		if (c != 0) {
+			break;
+		}
+
+		fprintf(stderr, "Seen that client is requesting a v76 close, sending ack\n");
+		buf[0] = 0xff;
+		buf[1] = 0;
+		n = write(wsi->sock, buf, 2);
+		if (n < 0) {
+			fprintf(stderr, "ERROR writing to socket");
+			return -1;
+		}
+		fprintf(stderr, "  v76 close ack sent, server closing socket\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:
+		break;
+	}
+
+	return 0;
+}
+
+static int libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi,
+						 unsigned char *buf, size_t len)
+{
+	int n;
+
+	fprintf(stderr, "received %d byte packet\n", (int)len);
+	for (n = 0; n < len; n++)
+		fprintf(stderr, "%02X ", buf[n]);
+	fprintf(stderr, "\n");
+
+	/* 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)
+		if (libwebsocket_rx_sm(wsi, buf[n++]) < 0)
+			return -1;
+		
+	if (n != len) {
+		if (wsi->callback)
+			wsi->callback(wsi, LWS_CALLBACK_RECEIVE, &buf[n], len - n);
+	}
+	
+	return -0;
+}
+
 
 /*
  * We have to take care about parsing because the headers may be split
@@ -372,8 +481,7 @@
 		/* fallthru */
 	case WSI_STATE_HANDSHAKE_RX:
 	
-		fprintf(stderr, "issuing %ld bytes to parser\n", len);
-	
+		fprintf(stderr, "issuing %d bytes to parser\n", (int)len);
 	
 		fwrite(buf, 1, len, stderr);
 		for (n = 0; n< len; n++)
@@ -402,6 +510,8 @@
 		
 		/* create the response packet */
 		
+		/* make a buffer big enough for everything */
+		
 		response = malloc(256 +
 			wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len +
 			wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len +
@@ -409,13 +519,16 @@
 			wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len +
 			wsi->utf8_token[WSI_TOKEN_GET_URI].token_len +
 			wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len);
-		
-		
-		fprintf(stderr, "'%s;\n", wsi->utf8_token[WSI_TOKEN_HOST].token);
+		if (!response) {
+			fprintf(stderr, "Out of memory for response buffer\n");
+			goto bail;
+		}
 		
 		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,   "HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0a"
+						  "Upgrade: WebSocket\x0d\x0a");
+		p += strlen("HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0a"
+						  "Upgrade: WebSocket\x0d\x0a");
 		strcpy(p,   "Connection: Upgrade\x0d\x0aSec-WebSocket-Origin: ");
 		p += strlen("Connection: Upgrade\x0d\x0aSec-WebSocket-Origin: ");
 		strcpy(p, wsi->utf8_token[WSI_TOKEN_ORIGIN].token);
@@ -442,7 +555,6 @@
 		
 		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;
 			
@@ -469,10 +581,10 @@
 		md5(sum, 16, (unsigned char *)p);
 		p += 16;
 
-		/* it's complete, go ahead and send it */
+		/* it's complete: go ahead and send it */
 		
 		fprintf(stderr, "issuing response packet %d len\n",
-							  (int)(p - response));
+							   (int)(p - response));
 		fwrite(response, 1,  p - response, stderr);
 			
 		n = write(wsi->sock, response, p - response);
@@ -480,18 +592,22 @@
 			fprintf(stderr, "ERROR writing to socket");
 			goto bail;
 		}
+		
+		/* alright clean up and set ourselves into established state */
 
 		free(response);
 		wsi->state = WSI_STATE_ESTABLISHED;
+		wsi->lws_rx_parse_state = LWS_RXPS_NEW;
 		
 		/* notify user code that we're ready to roll */
 				
 		if (wsi->callback)
-			wsi->callback(wsi, LWS_CALLBACK_ESTABLISHED);
+			wsi->callback(wsi, LWS_CALLBACK_ESTABLISHED, NULL, 0);
 		break;
 
 	case WSI_STATE_ESTABLISHED:
-		fprintf(stderr, "received %ld byte packet\n", len);
+		if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0)
+			goto bail;
 		break;
 	default:
 		break;
@@ -504,68 +620,127 @@
 	return -1;
 }
 
-int libwebsocket_write(struct libwebsocket * wsi, void *buf, size_t len)
+
+/*
+ * notice, we will use up to LWS_SEND_BUFFER_PRE_PADDING bytes BEFORE the
+ * buffer pointer given and LWS_SEND_BUFFER_POST_PADDING bytes AFTER
+ * buf + len !!!  Caller must allocate and offset pointer accordingly!
+ * 
+ * This lets us send packets in one write() action including the protocol
+ * pre- and post- data without copying the payload around.
+ */
+
+int libwebsocket_write(struct libwebsocket * wsi, unsigned char *buf,
+						      size_t len, int is_binary)
 {
 	int n;
-	unsigned char hdr[9];
+	int pre = 0;
+	int post = 0;
+	unsigned int shift = 7;
 	
 	if (wsi->state != WSI_STATE_ESTABLISHED)
 		return -1;
 
 	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;
+	/* Firefox 4.0b6 likes this as of 30 Oct */
+	case 76:
+		if (is_binary) {
+			/* in binary mode we send 7-bit used length blocks */
+			pre = 1;
+			while (len & (127 << shift)) {
+				pre++;
+				shift += 7;
+			}
+			n = 0;
+			shift -= 7;
+			while (shift >= 0) {
+				if (shift)
+					buf[0 - pre + n] =
+						  ((len >> shift) & 127) | 0x80;
+				else
+					buf[0 - pre + n] =
+						  ((len >> shift) & 127);
+				n++;
+				shift -= 7;
+			}
+			break;
 		}
+
+		/* frame type = text, length-free spam mode */
+
+		buf[-1] = 0;
+		buf[len] = 0xff; /* EOT marker */
+		pre = 1;
+		post = 1;
 		break;
+
+	/* chrome likes this as of 30 Oct */
+	case 0:
+		buf[-9] = 0xff;
+#if defined __LP64__
+			buf[-8] = len >> 56;
+			buf[-7] = len >> 48;
+			buf[-6] = len >> 40;
+			buf[-5] = len >> 32;
+#else
+			buf[-8] = 0;
+			buf[-7] = 0;
+			buf[-6] = 0;
+			buf[-5] = 0;
+#endif
+		buf[-4] = len >> 24;
+		buf[-3] = len >> 16;
+		buf[-2] = len >> 8;
+		buf[-1] = len;
+		pre = 9;
+		break;
+		
 	/* just an unimplemented spec right now apparently */
 	case 2:
-		n = 0;
+		n = 4; /* text */
+		if (is_binary)
+			n = 5; /* binary */
 		if (len < 126) {
-			hdr[n++] = 0x04;
-			hdr[n++] = len;
+			buf[-2] = n;
+			buf[-1] = len;
+			pre = 2;
 		} else {
 			if (len < 65536) {
-				hdr[n++] = 0x04; /* text frame */
-				hdr[n++] = 126;
-				hdr[n++] = len >> 8;
-				hdr[n++] = len;
+				buf[-4] = n;
+				buf[-3] = 126;
+				buf[-2] = len >> 8;
+				buf[-1] = len;
+				pre = 4;
 			} else {
-				hdr[n++] = 0x04;
-				hdr[n++] = 127;
-				hdr[n++] = len >> 24;
-				hdr[n++] = len >> 16;
-				hdr[n++] = len >> 8;
-				hdr[n++] = len;
+				buf[-10] = n;
+				buf[-9] = 127;
+#if defined __LP64__
+					buf[-8] = (len >> 56) & 0x7f;
+					buf[-7] = len >> 48;
+					buf[-6] = len >> 40;
+					buf[-5] = len >> 32;
+#else
+					buf[-8] = 0;
+					buf[-7] = 0;
+					buf[-6] = 0;
+					buf[-5] = 0;
+#endif
+				buf[-4] = len >> 24;
+				buf[-3] = len >> 16;
+				buf[-2] = len >> 8;
+				buf[-1] = len;
+				pre = 10;
 			}
 		}
-		n = write(wsi->sock, hdr, n);
-		if (n < 0) {
-			fprintf(stderr, "ERROR writing to socket");
-			return -1;
-		}
 		break;
 	}
 
-	n = write(wsi->sock, buf, len);
+	n = write(wsi->sock, buf - pre, len + pre + post);
 	if (n < 0) {
 		fprintf(stderr, "ERROR writing to socket");
 		return -1;
 	}
-	
+
 	fprintf(stderr, "written %d bytes to websocket\n", (int)len);
 	
 	return 0;
@@ -616,7 +791,7 @@
 			continue;
 		
 		if (wsi->callback)
-			wsi->callback(wsi, LWS_CALLBACK_SEND);
+			wsi->callback(wsi, LWS_CALLBACK_SEND, NULL, 0);
 	}
 }
 
diff --git a/libwebsockets.h b/libwebsockets.h
index 870b7fe..b23f588 100644
--- a/libwebsockets.h
+++ b/libwebsockets.h
@@ -10,6 +10,30 @@
 
 extern int libwebsocket_create_server(int port,
 		  int (*callback)(struct libwebsocket *,
-					   enum libwebsocket_callback_reasons));
+					   enum libwebsocket_callback_reasons, void *, size_t));
 
-extern int libwebsocket_write(struct libwebsocket *, void *buf, size_t len);
+/*
+ * IMPORTANT NOTICE!
+ * 
+ * The send buffer has to have LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE
+ * buf, and LWS_SEND_BUFFER_POST_PADDING bytes valid AFTER (buf + len).
+ * 
+ * This allows us to add protocol info before and after the data, and send as
+ * one packet on the network without payload copying, for maximum efficiency.
+ * 
+ * So for example you need this kind of code to use libwebsocket_write with a
+ * 128-byte payload 
+ * 
+ * char buf[LWS_SEND_BUFFER_PRE_PADDING + 128 + LWS_SEND_BUFFER_POST_PADDING];
+ * 
+ * // fill your part of the buffer... for example here it's all zeros
+ * memset(&buf[LWS_SEND_BUFFER_PRE_PADDING], 0, 128);
+ * 
+ * libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], 128);
+ * 
+ */
+
+#define LWS_SEND_BUFFER_PRE_PADDING 12
+#define LWS_SEND_BUFFER_POST_PADDING 1
+
+extern int libwebsocket_write(struct libwebsocket *, unsigned char *buf, size_t len, int is_binary);
diff --git a/test-server.c b/test-server.c
index 0056e80..4f13664 100644
--- a/test-server.c
+++ b/test-server.c
@@ -6,11 +6,11 @@
 
 #define PORT 7681
 
-int websocket_callback(struct libwebsocket * wsi,
-				      enum libwebsocket_callback_reasons reason)
+static int websocket_callback(struct libwebsocket * wsi,
+	       enum libwebsocket_callback_reasons reason, void *in, size_t len)
 {
 	int n;
-	char buf[256];
+	char buf[LWS_SEND_BUFFER_PRE_PADDING + 256 + LWS_SEND_BUFFER_POST_PADDING];
 	static int bump;
 	
 	switch (reason) {
@@ -24,8 +24,8 @@
 
 	case LWS_CALLBACK_SEND:	
 		sleep(1);
-		n = sprintf(buf, "%d\n", bump++);
-		n = libwebsocket_write(wsi, buf, n);
+		n = sprintf(&buf[LWS_SEND_BUFFER_PRE_PADDING], "%d\n", bump++);
+		n = libwebsocket_write(wsi, (unsigned char *)&buf[LWS_SEND_BUFFER_PRE_PADDING], n, 0);
 		if (n < 0) {
 			fprintf(stderr, "ERROR writing to socket");
 			exit(1);
@@ -33,6 +33,7 @@
 		break;
 
 	case LWS_CALLBACK_RECEIVE:
+		fprintf(stderr, "Received %d bytes payload\n", (int)len);
 		break;
 	}
 	return 0;
diff --git a/test.html b/test.html
index 5d8b46f..59ad43f 100644
--- a/test.html
+++ b/test.html
@@ -15,7 +15,7 @@
 //        alert('<p class="event">Socket Status: '+socket.readyState);  
   
         socket.onopen = function(){  
-             alert('<p class="event">Socket Status: '+socket.readyState+' (open)');  
+ //            alert('<p class="event">Socket Status: '+socket.readyState+' (open)');  
         }  
   
         socket.onmessage = got_packet;
@@ -28,8 +28,8 @@
           }
 	
 function got_packet(msg){
-//	alert('got packet' + msg);
 
+//	alert('got packet' + msg.data);
 	document.write(msg.data + "\n");
 	
 }