add-basic-http-fileserving.patch
authorAndy Green <andy@warmcat.com>
Sun, 31 Oct 2010 11:57:17 +0000 (11:57 +0000)
committerAndy Green <andy@warmcat.com>
Sun, 31 Oct 2010 11:57:17 +0000 (11:57 +0000)
Signed-off-by: Andy Green <andy@warmcat.com>
favicon.ico [new file with mode: 0644]
libwebsockets.c
libwebsockets.h
test-server.c

diff --git a/favicon.ico b/favicon.ico
new file mode 100644 (file)
index 0000000..c0cc2e3
Binary files /dev/null and b/favicon.ico differ
index fc67be7..7fcf4e1 100644 (file)
@@ -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 @@ static void libwebsocket_service(struct libwebsocket *wsi, int sock);
 
 
 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 @@ int libwebsocket_create_server(int port,
        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 @@ int libwebsocket_create_server(int port,
        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 @@ int libwebsocket_create_server(int port,
        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 @@ int libwebsocket_create_server(int port,
                        fprintf(stderr, "ERROR on accept");
                        continue;
                }
+               
+//             fcntl(newsockfd, F_SETFL, O_NONBLOCK);
                        
                /* fork off a new server instance */
                        
@@ -247,6 +252,13 @@ void libwebsocket_close(struct libwebsocket *wsi)
                        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 @@ static int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c)
                                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 @@ static int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c)
                        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 @@ int libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len)
        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 @@ int libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len)
                        
                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 @@ bail:
  */
 
 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 @@ int libwebsocket_write(struct libwebsocket * wsi, unsigned char *buf,
        /* 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 @@ int libwebsocket_write(struct libwebsocket * wsi, unsigned char *buf,
                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");
+
+send_raw:
 
-       n = write(wsi->sock, buf - pre, len + pre + post);
+       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 @@ static void libwebsocket_service(struct libwebsocket *wsi, int sock)
        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;
+}
index 0264957..6ecccd1 100644 (file)
@@ -4,6 +4,13 @@ enum libwebsocket_callback_reasons {
        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 @@ extern int libwebsocket_create_server(int port,
 /*
  * 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 @@ extern int libwebsocket_create_server(int port,
  * 
  * 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);
index 1717c4d..5bbda6f 100644 (file)
@@ -2,6 +2,8 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <getopt.h>
+#include <string.h>
+
 
 #include "libwebsockets.h"
 
@@ -19,14 +21,16 @@ static int websocket_callback(struct libwebsocket * wsi,
               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 @@ static int websocket_callback(struct libwebsocket * wsi,
                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 @@ static int websocket_callback(struct libwebsocket * wsi,
        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;
 }