add-basic-http-fileserving.patch

Signed-off-by: Andy Green <andy@warmcat.com>
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000..c0cc2e3
--- /dev/null
+++ b/favicon.ico
Binary files differ
diff --git a/libwebsockets.c b/libwebsockets.c
index fc67be7..7fcf4e1 100644
--- a/libwebsockets.c
+++ b/libwebsockets.c
@@ -4,6 +4,9 @@
 #include <ctype.h>
 #include <unistd.h>
 #include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 
 #include <sys/types.h> 
 #include <sys/socket.h>
@@ -48,8 +51,8 @@
 
 
 enum lws_connection_states {
-	WSI_STATE_CLOSED,
-	WSI_STATE_HANDSHAKE_RX,
+	WSI_STATE_HTTP,
+	WSI_STATE_HTTP_HEADERS,
 	WSI_STATE_DEAD_SOCKET,
 	WSI_STATE_ESTABLISHED
 };
@@ -143,7 +146,7 @@
 	if (!wsi)
 		return -1;
      
-	wsi->state = WSI_STATE_CLOSED;
+	wsi->state = WSI_STATE_HTTP;
 	wsi->name_buffer_pos = 0;
 
 	for (n = 0; n < WSI_TOKEN_COUNT; n++) {
@@ -156,7 +159,7 @@
 	case 0:
 	case 2:
 	case 76:
-		fprintf(stderr, "Using protocol v%d\n", protocol);
+		fprintf(stderr, " Using protocol v%d\n", protocol);
 		wsi->ietf_spec_revision = protocol;
 		break;
 	default:
@@ -196,7 +199,7 @@
 	if (n)
 		return 0;
  
-	fprintf(stderr, "Listening on port %d\n", port);
+	fprintf(stderr, " Listening on port %d\n", port);
  
 	listen(sockfd, 5);
     
@@ -208,6 +211,8 @@
 			fprintf(stderr, "ERROR on accept");
 			continue;
 		}
+		
+//		fcntl(newsockfd, F_SETFL, O_NONBLOCK);
 			
 		/* fork off a new server instance */
 			
@@ -247,6 +252,13 @@
 			free(wsi->utf8_token[n].token);
 }
 
+const char * libwebsocket_get_uri(struct libwebsocket *wsi)
+{
+	if (wsi->utf8_token[WSI_TOKEN_GET_URI].token)
+		return wsi->utf8_token[WSI_TOKEN_GET_URI].token;
+	
+	return NULL;
+}
 
 static int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c)
 {
@@ -333,6 +345,7 @@
 				continue;
 			if (strcmp(lws_tokens[n].token, wsi->name_buffer))
 				continue;
+			fprintf(stderr, "known header '%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 =
@@ -340,8 +353,23 @@
 			wsi->utf8_token[wsi->parser_state].token_len = 0;
 			n = WSI_TOKEN_COUNT;
 		}
-		if (wsi->parser_state != WSI_TOKEN_NAME_PART)
+
+		/* colon delimiter means we just don't know this name */
+
+		if (wsi->parser_state == WSI_TOKEN_NAME_PART && c == ':') {
+			fprintf(stderr, "skipping unknown header '%s'\n", wsi->name_buffer);
+			wsi->parser_state = WSI_TOKEN_SKIPPING;
 			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) {
+			/* they're HTTP headers, not websocket upgrade! */
+			fprintf(stderr, "Setting WSI_PARSING_COMPLETE from http headers\n");
+			wsi->parser_state = WSI_PARSING_COMPLETE;
+		}
 		break;
 			
 		/* skipping arg part of a name we didn't recognize */
@@ -490,11 +518,11 @@
 	char *response;
 	
 	switch (wsi->state) {
-	case WSI_STATE_CLOSED:
-		wsi->state = WSI_STATE_HANDSHAKE_RX;
+	case WSI_STATE_HTTP:
+		wsi->state = WSI_STATE_HTTP_HEADERS;
 		wsi->parser_state = WSI_TOKEN_NAME_PART;
 		/* fallthru */
-	case WSI_STATE_HANDSHAKE_RX:
+	case WSI_STATE_HTTP_HEADERS:
 	
 		fprintf(stderr, "issuing %d bytes to parser\n", (int)len);
 	
@@ -504,24 +532,29 @@
 			
 		if (wsi->parser_state != WSI_PARSING_COMPLETE)
 			break;
+
+		/* is this websocket protocol or normal http 1.0? */
+		
+		if (!wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
+			     !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len) {
+			if (wsi->callback)
+				(wsi->callback)(wsi, LWS_CALLBACK_HTTP, NULL, 0);
+			wsi->state = WSI_STATE_HTTP;
+			return 0;
+		}
+
 			
 		fprintf(stderr, "Preparing return packet\n");
 
-
-		/* Confirm we have all the necessary pieces */
+		/* Websocket - confirm we have all the necessary pieces */
 		
-		if (
-			!wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
-			!wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len ||
-			!wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len ||
+		if (!wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len ||
 			!wsi->utf8_token[WSI_TOKEN_HOST].token_len ||
 			!wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len ||
 			!wsi->utf8_token[WSI_TOKEN_KEY1].token_len ||
-			!wsi->utf8_token[WSI_TOKEN_KEY2].token_len) {
-				
+				     !wsi->utf8_token[WSI_TOKEN_KEY2].token_len)
 			/* completed header processing, but missing some bits */
 			goto bail;
-		}
 		
 		/* create the response packet */
 		
@@ -645,20 +678,25 @@
  */
 
 int libwebsocket_write(struct libwebsocket * wsi, unsigned char *buf,
-						      size_t len, int is_binary)
+			  size_t len, enum libwebsocket_write_protocol protocol)
 {
 	int n;
 	int pre = 0;
 	int post = 0;
 	unsigned int shift = 7;
 	
+	if (protocol == LWS_WRITE_HTTP)
+		goto send_raw;
+	
+	/* websocket protocol, either binary or text */
+	
 	if (wsi->state != WSI_STATE_ESTABLISHED)
 		return -1;
 
 	switch (wsi->ietf_spec_revision) {
 	/* Firefox 4.0b6 likes this as of 30 Oct */
 	case 76:
-		if (is_binary) {
+		if (protocol == LWS_WRITE_BINARY) {
 			/* in binary mode we send 7-bit used length blocks */
 			pre = 1;
 			while (len & (127 << shift)) {
@@ -712,7 +750,7 @@
 	/* just an unimplemented spec right now apparently */
 	case 2:
 		n = 4; /* text */
-		if (is_binary)
+		if (protocol == LWS_WRITE_BINARY)
 			n = 5; /* binary */
 		if (len < 126) {
 			buf[-2] = n;
@@ -749,18 +787,20 @@
 		break;
 	}
 	
-	for (n = 0; n < (len + pre + post); n++)
-		fprintf(stderr, "%02X ", buf[n - pre]);
-		
-	fprintf(stderr, "\n");
+//	for (n = 0; n < (len + pre + post); n++)
+//		fprintf(stderr, "%02X ", buf[n - pre]);
+//		
+//	fprintf(stderr, "\n");
 
-	n = write(wsi->sock, buf - pre, len + pre + post);
+send_raw:
+
+	n = send(wsi->sock, buf - pre, len + pre + post, 0);
 	if (n < 0) {
 		fprintf(stderr, "ERROR writing to socket");
 		return -1;
 	}
 
-	fprintf(stderr, "written %d bytes to websocket\n", (int)len);
+//	fprintf(stderr, "written %d bytes to client\n", (int)len);
 	
 	return 0;
 }
@@ -772,45 +812,102 @@
 	struct pollfd fds;
 	
 	wsi->sock = sock;
-	
-	fds.fd = sock;
-	fds.events = POLLIN | POLLOUT;
-      
+	    
 	while (1) {
-		
-		n = poll(&fds, 1, 10);
+		fds.fd = sock;
+		fds.events = POLLIN;
+		fds.revents = 0;
+ 		n = poll(&fds, 1, 50);
+
+		/* 
+		 * we seem to get a ton of POLLINs coming when there's nothing
+		 * to read (at least, 0 bytes read)... don't understand why yet
+		 * but I stuck a usleep() in the 0 byte read case to regulate
+		 * CPU
+		 */
+
+
 		if (n < 0) {
 			fprintf(stderr, "Socket dead (poll = %d)\n", n);
 			return;
 		}
+		if (n == 0)
+			goto pout;
 
 		if (fds.revents & (POLLERR | POLLHUP)) {
 			fprintf(stderr, "Socket dead\n");
 			return;
 		}
 		
-		if (wsi->state == WSI_STATE_DEAD_SOCKET)
+		if (wsi->state == WSI_STATE_DEAD_SOCKET) {
+			fprintf(stderr, "Seen socket dead, returning from service\n");
 			return;
-		
+		}
 		
 		if (fds.revents & POLLIN) {
 			
 //			fprintf(stderr, "POLLIN\n");
 			
-			n = read(sock, buf, sizeof(buf));
+			n = recv(sock, buf, sizeof(buf), 0);
 			if (n < 0) {
 				fprintf(stderr, "Socket read returned %d\n", n);
 				continue;
 			}
 			if (n)
 				libwebsocket_read(wsi, buf, n);
+			else {
+//				fprintf(stderr, "POLLIN with zero length waiting\n");
+				usleep(50000);
+			}
 		}
-		
+pout:
 		if (wsi->state != WSI_STATE_ESTABLISHED)
 			continue;
-		
+
+//		fprintf(stderr, "POLLOUT\n");
+					
 		if (wsi->callback)
 			wsi->callback(wsi, LWS_CALLBACK_SEND, NULL, 0);
 	}
 }
 
+int libwebsockets_serve_http_file(struct libwebsocket *wsi, const char * file,
+						      const char * content_type)
+{
+	int fd;
+	struct stat stat;
+	char buf[512];
+	char *p = buf;
+	int n;
+
+	fd = open(file, O_RDONLY);
+	if (fd < 1) {
+		p += sprintf(p, "HTTP/1.0 400 Bad\x0d\x0a"
+			"Server: libwebsockets\x0d\x0a"
+			"\x0d\x0a"
+		);
+		libwebsocket_write(wsi, (unsigned char *)buf, p - buf, LWS_WRITE_HTTP);
+		
+		return -1;
+	}
+
+	fstat(fd, &stat);
+	p += sprintf(p, "HTTP/1.0 200 OK\x0d\x0a"
+			"Server: libwebsockets\x0d\x0a"
+			"Content-Type: %s\x0d\x0a"
+			"Content-Length: %u\x0d\x0a"
+			"\x0d\x0a", content_type, (unsigned int)stat.st_size
+			);
+			
+	libwebsocket_write(wsi, (unsigned char *)buf, p - buf, LWS_WRITE_HTTP);
+
+	n = 1;
+	while (n > 0) {
+		n = read(fd, buf, 512);
+		libwebsocket_write(wsi, (unsigned char *)buf, n, LWS_WRITE_HTTP);
+	}
+	
+	close(fd);
+		
+	return 0;
+}
diff --git a/libwebsockets.h b/libwebsockets.h
index 0264957..6ecccd1 100644
--- a/libwebsockets.h
+++ b/libwebsockets.h
@@ -4,6 +4,13 @@
 	LWS_CALLBACK_CLOSED,
 	LWS_CALLBACK_SEND,
 	LWS_CALLBACK_RECEIVE,
+	LWS_CALLBACK_HTTP
+};
+
+enum libwebsocket_write_protocol {
+	LWS_WRITE_TEXT,
+	LWS_WRITE_BINARY,
+	LWS_WRITE_HTTP
 };
 
 struct libwebsocket;
@@ -16,7 +23,8 @@
 /*
  * IMPORTANT NOTICE!
  * 
- * The send buffer has to have LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE
+ * When sending with websocket protocol (LWS_WRITE_TEXT or LWS_WRITE_BINARY)
+ * 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
@@ -32,9 +40,18 @@
  * 
  * libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], 128);
  * 
+ * When sending LWS_WRITE_HTTP, there is no protocol addition and you can just
+ * use the whole buffer without taking care of the above.
  */
 
 #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);
+extern int
+libwebsocket_write(struct libwebsocket *, unsigned char *buf, size_t len,
+				     enum libwebsocket_write_protocol protocol);
+extern const char *
+libwebsocket_get_uri(struct libwebsocket *wsi);
+extern int
+libwebsockets_serve_http_file(struct libwebsocket *wsi, const char * file,
+						     const char * content_type);
diff --git a/test-server.c b/test-server.c
index 1717c4d..5bbda6f 100644
--- a/test-server.c
+++ b/test-server.c
@@ -2,6 +2,8 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <getopt.h>
+#include <string.h>
+
 
 #include "libwebsockets.h"
 
@@ -19,14 +21,16 @@
 	       enum libwebsocket_callback_reasons reason, void *in, size_t len)
 {
 	int n;
-	char buf[LWS_SEND_BUFFER_PRE_PADDING + 256 + LWS_SEND_BUFFER_POST_PADDING];
+	char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 + LWS_SEND_BUFFER_POST_PADDING];
 	static int bump;
 	static int slow;
+	char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
+	const char *uri;
 	
 	switch (reason) {
 	case LWS_CALLBACK_ESTABLISHED:
 		fprintf(stderr, "Websocket connection established\n");
-		slow = 500;
+		slow = 100;
 		break;
 
 	case LWS_CALLBACK_CLOSED:
@@ -34,14 +38,14 @@
 		break;
 
 	case LWS_CALLBACK_SEND:	
-		slow--;
-		if (slow) {
-			usleep(10000);
-			break;
-		}
-		slow = 100;
-		n = sprintf(&buf[LWS_SEND_BUFFER_PRE_PADDING], "%d", bump++);
-		n = libwebsocket_write(wsi, (unsigned char *)&buf[LWS_SEND_BUFFER_PRE_PADDING], n, 0);
+//		slow--;
+//		if (slow) {
+//			usleep(10000);
+//			break;
+//		}
+//		slow = 20;
+		n = sprintf(p, "%d", bump++);
+		n = libwebsocket_write(wsi, (unsigned char *)p, n, 0);
 		if (n < 0) {
 			fprintf(stderr, "ERROR writing to socket");
 			exit(1);
@@ -51,6 +55,33 @@
 	case LWS_CALLBACK_RECEIVE:
 		fprintf(stderr, "Received %d bytes payload\n", (int)len);
 		break;
+	case LWS_CALLBACK_HTTP:
+		/*
+		 * The client has asked us for something in normal HTTP mode,
+		 * not websockets mode.  Normally it means we want to send
+		 * our script / html to the client, and when that script runs
+		 * it will start up separate websocket connections.
+		 */
+		uri = libwebsocket_get_uri(wsi);
+		if (uri == NULL)
+			fprintf(stderr, "LWS_CALLBACK_HTTP NULL URI\n");
+		else
+			fprintf(stderr, "LWS_CALLBACK_HTTP  '%s'\n", uri);
+			
+		if (uri && strcmp(uri, "/favicon.ico") == 0) {
+			if (libwebsockets_serve_http_file(wsi, "./favicon.ico", "image/x-icon")) {
+				fprintf(stderr, "Failed to send favicon file\n");
+			}
+			break;
+		}
+		
+		/* send the script... when it runs it'll start websockets */
+
+		if (libwebsockets_serve_http_file(wsi, "./test.html", "text/html")) {
+			fprintf(stderr, "Failed to send HTTP file\n");
+		}
+
+		break;
 	}
 	return 0;
 }