break-out-lib-sources.patch
authorAndy Green <andy@warmcat.com>
Mon, 8 Nov 2010 20:20:42 +0000 (20:20 +0000)
committerAndy Green <andy@warmcat.com>
Mon, 8 Nov 2010 20:20:42 +0000 (20:20 +0000)
Signed-off-by: Andy Green <andy@warmcat.com>
lib/Makefile.am
lib/Makefile.in
lib/handshake.c [new file with mode: 0644]
lib/libwebsockets.c
lib/parsers.c [new file with mode: 0644]
lib/private-libwebsockets.h [new file with mode: 0644]
test-server/test-server.c

index 771fee7..78ced38 100644 (file)
@@ -1,11 +1,18 @@
 lib_LTLIBRARIES=libwebsockets.la
 include_HEADERS=libwebsockets.h
-dist_libwebsockets_la_SOURCES=libwebsockets.c md5.c libwebsockets.h
+dist_libwebsockets_la_SOURCES=libwebsockets.c \
+                               handshake.c \
+                               parsers.c \
+                               md5.c \
+                               libwebsockets.h \
+                               private-libwebsockets.h
 libwebsockets_la_CFLAGS=-rdynamic -fPIC -c
 libwebsockets_la_LDFLAGS=-version-info 0:1
 
 all-local:
         ../scripts/kernel-doc -html \
                 libwebsockets.c \
-                ../test-server/test-server.c > ../libwebsockets-api-doc.html
+                parsers.c \
+                ../test-server/test-server.c \
+                       > ../libwebsockets-api-doc.html
 
index ad6368d..493db5b 100644 (file)
@@ -71,6 +71,7 @@ am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
 LTLIBRARIES = $(lib_LTLIBRARIES)
 libwebsockets_la_LIBADD =
 dist_libwebsockets_la_OBJECTS = libwebsockets_la-libwebsockets.lo \
+       libwebsockets_la-handshake.lo libwebsockets_la-parsers.lo \
        libwebsockets_la-md5.lo
 libwebsockets_la_OBJECTS = $(dist_libwebsockets_la_OBJECTS)
 libwebsockets_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
@@ -207,7 +208,13 @@ top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 lib_LTLIBRARIES = libwebsockets.la
 include_HEADERS = libwebsockets.h
-dist_libwebsockets_la_SOURCES = libwebsockets.c md5.c libwebsockets.h
+dist_libwebsockets_la_SOURCES = libwebsockets.c \
+                               handshake.c \
+                               parsers.c \
+                               md5.c \
+                               libwebsockets.h \
+                               private-libwebsockets.h
+
 libwebsockets_la_CFLAGS = -rdynamic -fPIC -c
 libwebsockets_la_LDFLAGS = -version-info 0:1
 all: all-am
@@ -284,8 +291,10 @@ mostlyclean-compile:
 distclean-compile:
        -rm -f *.tab.c
 
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-handshake.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-libwebsockets.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-md5.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-parsers.Plo@am__quote@
 
 .c.o:
 @am__fastdepCC_TRUE@   $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -315,6 +324,20 @@ libwebsockets_la-libwebsockets.lo: libwebsockets.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@  $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -c -o libwebsockets_la-libwebsockets.lo `test -f 'libwebsockets.c' || echo '$(srcdir)/'`libwebsockets.c
 
+libwebsockets_la-handshake.lo: handshake.c
+@am__fastdepCC_TRUE@   $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -MT libwebsockets_la-handshake.lo -MD -MP -MF $(DEPDIR)/libwebsockets_la-handshake.Tpo -c -o libwebsockets_la-handshake.lo `test -f 'handshake.c' || echo '$(srcdir)/'`handshake.c
+@am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/libwebsockets_la-handshake.Tpo $(DEPDIR)/libwebsockets_la-handshake.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='handshake.c' object='libwebsockets_la-handshake.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -c -o libwebsockets_la-handshake.lo `test -f 'handshake.c' || echo '$(srcdir)/'`handshake.c
+
+libwebsockets_la-parsers.lo: parsers.c
+@am__fastdepCC_TRUE@   $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -MT libwebsockets_la-parsers.lo -MD -MP -MF $(DEPDIR)/libwebsockets_la-parsers.Tpo -c -o libwebsockets_la-parsers.lo `test -f 'parsers.c' || echo '$(srcdir)/'`parsers.c
+@am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/libwebsockets_la-parsers.Tpo $(DEPDIR)/libwebsockets_la-parsers.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='parsers.c' object='libwebsockets_la-parsers.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -c -o libwebsockets_la-parsers.lo `test -f 'parsers.c' || echo '$(srcdir)/'`parsers.c
+
 libwebsockets_la-md5.lo: md5.c
 @am__fastdepCC_TRUE@   $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -MT libwebsockets_la-md5.lo -MD -MP -MF $(DEPDIR)/libwebsockets_la-md5.Tpo -c -o libwebsockets_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c
 @am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/libwebsockets_la-md5.Tpo $(DEPDIR)/libwebsockets_la-md5.Plo
@@ -555,7 +578,9 @@ uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES
 all-local:
         ../scripts/kernel-doc -html \
                 libwebsockets.c \
-                ../test-server/test-server.c > ../libwebsockets-api-doc.html
+                parsers.c \
+                ../test-server/test-server.c \
+                       > ../libwebsockets-api-doc.html
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/lib/handshake.c b/lib/handshake.c
new file mode 100644 (file)
index 0000000..caebd61
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ * 
+ * Copyright (C) 2010 Andy Green <andy@warmcat.com>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include "private-libwebsockets.h"
+
+void md5(const unsigned char *input, int ilen, unsigned char output[16]);
+
+static int interpret_key(const char *key, unsigned int *result)
+{
+       char digits[20];
+       int digit_pos = 0;
+       const char *p = key;
+       int spaces = 0;
+       
+       while (*p) {
+               if (isdigit(*p)) {
+                       if (digit_pos == sizeof(digits) - 1)
+                               return -1;
+                       digits[digit_pos++] = *p;
+               }
+               p++;
+       }
+       digits[digit_pos] = '\0';
+       if (!digit_pos)
+               return -2;
+               
+       while (*key) {
+               if (*key == ' ')
+                       spaces++;
+               key++;
+       }
+       
+       if (!spaces)
+               return -3;
+               
+       *result = atol(digits) / spaces;
+       
+       return 0;
+}
+
+/*
+ * 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.  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)
+{
+       size_t n;
+       char *p;
+       unsigned int key1, key2;
+       unsigned char sum[16];
+       char *response;
+       
+       switch (wsi->state) {
+       case WSI_STATE_HTTP:
+               wsi->state = WSI_STATE_HTTP_HEADERS;
+               wsi->parser_state = WSI_TOKEN_NAME_PART;
+               /* fallthru */
+       case WSI_STATE_HTTP_HEADERS:
+       
+               debug("issuing %d bytes to parser\n", (int)len);        
+#ifdef DEBUG
+               fwrite(buf, 1, len, stderr);
+#endif
+               for (n = 0; n< len; n++)
+                       libwebsocket_parse(wsi, *buf++);
+                       
+               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,
+                                                       &wsi->user_space[0],
+                                                                      NULL, 0);
+                       wsi->state = WSI_STATE_HTTP;
+                       return 0;
+               }
+
+               /* Websocket - confirm we have all the necessary pieces */
+               
+               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)
+                       /* completed header processing, but missing some bits */
+                       goto bail;
+               
+               /* 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 +
+                       wsi->utf8_token[WSI_TOKEN_HOST].token_len +
+                       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);
+               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\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\x0a"
+                           "Sec-WebSocket-Origin: ");
+               p += strlen("Connection: Upgrade\x0d\x0a"
+                           "Sec-WebSocket-Origin: ");
+               strcpy(p, wsi->utf8_token[WSI_TOKEN_ORIGIN].token);
+               p += wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len;
+#ifdef LWS_OPENSSL_SUPPORT
+               if (use_ssl) {
+                       strcpy(p,   "\x0d\x0aSec-WebSocket-Location: wss://");
+                       p += strlen("\x0d\x0aSec-WebSocket-Location: wss://");
+               } else {
+#endif
+                       strcpy(p,   "\x0d\x0aSec-WebSocket-Location: ws://");
+                       p += strlen("\x0d\x0aSec-WebSocket-Location: ws://");
+#ifdef LWS_OPENSSL_SUPPORT
+               }
+#endif
+               strcpy(p, wsi->utf8_token[WSI_TOKEN_HOST].token);
+               p += wsi->utf8_token[WSI_TOKEN_HOST].token_len;
+               strcpy(p, wsi->utf8_token[WSI_TOKEN_GET_URI].token);
+               p += wsi->utf8_token[WSI_TOKEN_GET_URI].token_len;
+
+               if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) {
+                       strcpy(p,   "\x0d\x0aSec-WebSocket-Protocol: ");
+                       p += strlen("\x0d\x0aSec-WebSocket-Protocol: ");
+                       strcpy(p, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
+                       p += wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len;
+               }
+
+               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;
+               sum[2] = key1 >> 8;
+               sum[3] = key1;
+               sum[4] = key2 >> 24;
+               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;
+
+               /* it's complete: go ahead and send it */
+               
+               debug("issuing response packet %d len\n",
+                                                          (int)(p - response));
+#ifdef DEBUG
+               fwrite(response, 1,  p - response, stderr);
+#endif
+               n = libwebsocket_write(wsi, (unsigned char *)response, p - response,
+                                                               LWS_WRITE_HTTP);
+               if (n < 0) {
+                       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->user_space[0], NULL, 0);
+               break;
+
+       case WSI_STATE_ESTABLISHED:
+               if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0)
+                       goto bail;
+               break;
+       default:
+               break;
+       }
+       
+       return 0;
+       
+bail:
+       libwebsocket_close_and_free_session(wsi);
+       return -1;
+}
index ac20738..6db4d07 100644 (file)
  *  MA  02110-1301  USA
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <signal.h>
-
-#include <sys/types.h> 
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-#include <poll.h>
-#include <sys/mman.h>
+#include "private-libwebsockets.h"
 
 #ifdef LWS_OPENSSL_SUPPORT
-#include <openssl/ssl.h>
-#include <openssl/evp.h>
-#include <openssl/err.h>
-
 SSL_CTX *ssl_ctx;
 int use_ssl;
 #endif
 
-//#define DEBUG
-
-#include "libwebsockets.h"
 
-#ifdef DEBUG
-#define debug(format, args...)  \
-      fprintf(stderr, format , ## args)
-#else
-#define debug(format, args...) 
-#endif
-
-void md5(const unsigned char *input, int ilen, unsigned char output[16]);
-static int 
+extern int 
 libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len);
 
-#define MAX_CLIENTS 100
-#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_HTTP,
-       WSI_STATE_HTTP_HEADERS,
-       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
-};
-
-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;
-       int token_len;
-};
-
-
-/*
- * This is totally opaque to code using the library.  It's exported as a
- * forward-reference pointer-only declaration; the user can use the pointer with
- * other APIs to get information out of it.
- */
-
-struct libwebsocket {
-       int (*callback)(struct libwebsocket *,
-               enum libwebsocket_callback_reasons reason, void *, void *, size_t);
-
-       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;
-
-       enum lws_rx_parse_state lws_rx_parse_state;
-       size_t rx_packet_length;
-       
-#ifdef LWS_OPENSSL_SUPPORT
-       char m_fOccupied;
-       struct sockaddr_in m_addr;
-       int m_addrlen;
-
-       SSL *ssl;
-
-               // these are valid if it is a POST
-
-       char m_fOngoingPost;
-       int m_nSessionID;
-
-       time_t m_timeStarted;
-       long long m_llTransferred;
-       long long m_llSizeIfKnown;
 
-       char m_szTitle[PATH_MAX];
-       char m_szStatus[PATH_MAX];
-#endif
-
-       /* last */
-       char user_space[0];
-};
-
-
-const struct lws_tokens lws_tokens[WSI_TOKEN_COUNT] = {
-       { "GET ", 4 },
-       { "Host:", 5 },
-       { "Connection:", 11 },
-       { "Sec-WebSocket-Key1:", 19 },
-       { "Sec-WebSocket-Key2:", 19 },
-       { "Sec-WebSocket-Protocol:", 23 },
-       { "Upgrade:", 8 },
-       { "Origin:", 7 },
-       { "\x0d\x0a", 2 },
-};
 
-static void 
+void 
 libwebsocket_close_and_free_session(struct libwebsocket *wsi)
 {
        int n = wsi->state;
@@ -568,662 +427,4 @@ fatal:
        return 0;
 }
 
-/**
- * libwebsocket_get_uri() - Return the URI path being requested
- * @wsi:       Websocket instance
- * 
- *     The user code can find out the local path being opened from this
- *     call, it's valid on HTTP or established websocket connections.
- *     If the client opened the connection with "http://127.0.0.1/xyz/abc.d"
- *     then this call will return a pointer to "/xyz/abc.d"
- */
-
-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)
-{
-       int n;
-
-       switch (wsi->parser_state) {
-       case WSI_TOKEN_GET_URI:
-       case WSI_TOKEN_HOST:
-       case WSI_TOKEN_CONNECTION:
-       case WSI_TOKEN_KEY1:
-       case WSI_TOKEN_KEY2:
-       case WSI_TOKEN_PROTOCOL:
-       case WSI_TOKEN_UPGRADE:
-       case WSI_TOKEN_ORIGIN:
-       case WSI_TOKEN_CHALLENGE:
-       
-               debug("WSI_TOKEN_(%d) '%c'\n", wsi->parser_state, c);
-
-               /* collect into malloc'd buffers */
-               /* optional space swallow */
-               if (!wsi->utf8_token[wsi->parser_state].token_len && c == ' ')
-                       break;
-                       
-               /* special case space terminator for get-uri */
-               if (wsi->parser_state == WSI_TOKEN_GET_URI && c == ' ') {
-                       wsi->utf8_token[wsi->parser_state].token[
-                          wsi->utf8_token[wsi->parser_state].token_len] = '\0';
-                       wsi->parser_state = WSI_TOKEN_SKIPPING;
-                       break;
-               }
-
-               /* allocate appropriate memory */
-               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 !!!");
-                               wsi->parser_state = WSI_TOKEN_SKIPPING;
-                               break;
-                       }
-                       wsi->utf8_token[wsi->parser_state].token =
-                              realloc(wsi->utf8_token[wsi->parser_state].token,
-                                                       wsi->current_alloc_len);
-               }
-
-               /* bail at EOL */
-               if (wsi->parser_state != WSI_TOKEN_CHALLENGE && c == '\x0d') {
-                       wsi->utf8_token[wsi->parser_state].token[
-                          wsi->utf8_token[wsi->parser_state].token_len] = '\0';
-                       wsi->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
-                       break;
-               }
-
-               wsi->utf8_token[wsi->parser_state].token[
-                           wsi->utf8_token[wsi->parser_state].token_len++] = c;
-
-               /* special payload limiting */
-               if (wsi->parser_state == WSI_TOKEN_CHALLENGE &&
-                           wsi->utf8_token[wsi->parser_state].token_len == 8) {
-                       debug("Setting WSI_PARSING_COMPLETE\n");
-                       wsi->parser_state = WSI_PARSING_COMPLETE;
-                       break;
-               }
-               
-               break;
-
-               /* collecting and checking a name part */
-       case WSI_TOKEN_NAME_PART:
-               debug("WSI_TOKEN_NAME_PART '%c'\n", c);
-
-               if (wsi->name_buffer_pos == sizeof(wsi->name_buffer) - 1) {
-                       /* name bigger than we can handle, skip until next */
-                       wsi->parser_state = WSI_TOKEN_SKIPPING;
-                       break;
-               }
-               wsi->name_buffer[wsi->name_buffer_pos++] = c;
-               wsi->name_buffer[wsi->name_buffer_pos] = '\0';
-               
-               for (n = 0; n < WSI_TOKEN_COUNT; n++) {
-                       if (wsi->name_buffer_pos != lws_tokens[n].token_len)
-                               continue;
-                       if (strcmp(lws_tokens[n].token, wsi->name_buffer))
-                               continue;
-                       debug("known hdr '%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 =
-                                                malloc(wsi->current_alloc_len);
-                       wsi->utf8_token[wsi->parser_state].token_len = 0;
-                       n = WSI_TOKEN_COUNT;
-               }
-
-               /* colon delimiter means we just don't know this name */
-
-               if (wsi->parser_state == WSI_TOKEN_NAME_PART && c == ':') {
-                       debug("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! */
-                       debug("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 */
-       case WSI_TOKEN_SKIPPING:
-               debug("WSI_TOKEN_SKIPPING '%c'\n", c);
-               if (c == '\x0d')
-                       wsi->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
-               break;
-       case WSI_TOKEN_SKIPPING_SAW_CR:
-               debug("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
-               if (c == '\x0a')
-                       wsi->parser_state = WSI_TOKEN_NAME_PART;
-               else
-                       wsi->parser_state = WSI_TOKEN_SKIPPING;
-               wsi->name_buffer_pos = 0;
-               break;
-               /* we're done, ignore anything else */
-       case WSI_PARSING_COMPLETE:
-               debug("WSI_PARSING_COMPLETE '%c'\n", c);
-               break;
-               
-       default:        /* keep gcc happy */
-               break;
-       }
-       
-       return 0;
-}
-
-static int interpret_key(const char *key, unsigned int *result)
-{
-       char digits[20];
-       int digit_pos = 0;
-       const char *p = key;
-       int spaces = 0;
-       
-       while (*p) {
-               if (isdigit(*p)) {
-                       if (digit_pos == sizeof(digits) - 1)
-                               return -1;
-                       digits[digit_pos++] = *p;
-               }
-               p++;
-       }
-       digits[digit_pos] = '\0';
-       if (!digit_pos)
-               return -2;
-               
-       while (*key) {
-               if (*key == ' ')
-                       spaces++;
-               key++;
-       }
-       
-       if (!spaces)
-               return -3;
-               
-       *result = atol(digits) / spaces;
-       
-       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)
-                       break;
-
-               debug("Seen that client is requesting "
-                               "a v76 close, sending ack\n");
-               buf[0] = 0xff;
-               buf[1] = 0;
-               n = libwebsocket_write(wsi, buf, 2, LWS_WRITE_HTTP);
-               if (n < 0) {
-                       fprintf(stderr, "ERROR writing to socket");
-                       return -1;
-               }
-               debug("  v76 close ack sent, server closing skt\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 && wsi->callback)
-               wsi->callback(wsi, LWS_CALLBACK_RECEIVE, &wsi->user_space[0],
-                                                             &buf[n], len - n);
-       
-       return -0;
-}
-
 
-/*
- * 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.  So, we parse using a single-character at a time state
- * machine that is completely independent of packet size.
- */
-
-static int 
-libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len)
-{
-       size_t n;
-       char *p;
-       unsigned int key1, key2;
-       unsigned char sum[16];
-       char *response;
-       
-       switch (wsi->state) {
-       case WSI_STATE_HTTP:
-               wsi->state = WSI_STATE_HTTP_HEADERS;
-               wsi->parser_state = WSI_TOKEN_NAME_PART;
-               /* fallthru */
-       case WSI_STATE_HTTP_HEADERS:
-       
-               debug("issuing %d bytes to parser\n", (int)len);        
-#ifdef DEBUG
-               fwrite(buf, 1, len, stderr);
-#endif
-               for (n = 0; n< len; n++)
-                       libwebsocket_parse(wsi, *buf++);
-                       
-               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,
-                                                       &wsi->user_space[0],
-                                                                      NULL, 0);
-                       wsi->state = WSI_STATE_HTTP;
-                       return 0;
-               }
-
-               /* Websocket - confirm we have all the necessary pieces */
-               
-               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)
-                       /* completed header processing, but missing some bits */
-                       goto bail;
-               
-               /* 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 +
-                       wsi->utf8_token[WSI_TOKEN_HOST].token_len +
-                       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);
-               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\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\x0a"
-                           "Sec-WebSocket-Origin: ");
-               p += strlen("Connection: Upgrade\x0d\x0a"
-                           "Sec-WebSocket-Origin: ");
-               strcpy(p, wsi->utf8_token[WSI_TOKEN_ORIGIN].token);
-               p += wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len;
-#ifdef LWS_OPENSSL_SUPPORT
-               if (use_ssl) {
-                       strcpy(p,   "\x0d\x0aSec-WebSocket-Location: wss://");
-                       p += strlen("\x0d\x0aSec-WebSocket-Location: wss://");
-               } else {
-#endif
-                       strcpy(p,   "\x0d\x0aSec-WebSocket-Location: ws://");
-                       p += strlen("\x0d\x0aSec-WebSocket-Location: ws://");
-#ifdef LWS_OPENSSL_SUPPORT
-               }
-#endif
-               strcpy(p, wsi->utf8_token[WSI_TOKEN_HOST].token);
-               p += wsi->utf8_token[WSI_TOKEN_HOST].token_len;
-               strcpy(p, wsi->utf8_token[WSI_TOKEN_GET_URI].token);
-               p += wsi->utf8_token[WSI_TOKEN_GET_URI].token_len;
-
-               if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) {
-                       strcpy(p,   "\x0d\x0aSec-WebSocket-Protocol: ");
-                       p += strlen("\x0d\x0aSec-WebSocket-Protocol: ");
-                       strcpy(p, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
-                       p += wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len;
-               }
-
-               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;
-               sum[2] = key1 >> 8;
-               sum[3] = key1;
-               sum[4] = key2 >> 24;
-               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;
-
-               /* it's complete: go ahead and send it */
-               
-               debug("issuing response packet %d len\n",
-                                                          (int)(p - response));
-#ifdef DEBUG
-               fwrite(response, 1,  p - response, stderr);
-#endif
-               n = libwebsocket_write(wsi, (unsigned char *)response, p - response,
-                                                               LWS_WRITE_HTTP);
-               if (n < 0) {
-                       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->user_space[0], NULL, 0);
-               break;
-
-       case WSI_STATE_ESTABLISHED:
-               if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0)
-                       goto bail;
-               break;
-       default:
-               break;
-       }
-       
-       return 0;
-       
-bail:
-       libwebsocket_close_and_free_session(wsi);
-       return -1;
-}
-
-/**
- * libwebsocket_write() - Apply protocol then write data to client
- * @wsi:       Websocket instance (available from user callback)
- * @buf:       The data to send.  For data being sent on a websocket
- *             connection (ie, not default http), this buffer MUST have
- *             LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE the pointer
- *             and an additional LWS_SEND_BUFFER_POST_PADDING bytes valid
- *             in the buffer after (buf + len).  This is so the protocol
- *             header and trailer data can be added in-situ.
- * @len:       Count of the data bytes in the payload starting from buf
- * @protocol:  Use LWS_WRITE_HTTP to reply to an http connection, and one
- *             of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate
- *             data on a websockets connection.  Remember to allow the extra
- *             bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT
- *             are used.
- *
- *     This function provides the way to issue data back to the client
- *     for both http and websocket protocols.
- * 
- *     In the case of sending using websocket protocol, be sure to allocate
- *     valid storage before and after buf as explained above.  This scheme
- *     allows maximum efficiency of sending data and protocol in a single
- *     packet while not burdening the user code with any protocol knowledge.
- */
-
-int libwebsocket_write(struct libwebsocket * wsi, unsigned char *buf,
-                         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) {
-       /* chrome likes this as of 30 Oct */
-       /* Firefox 4.0b6 likes this as of 30 Oct */
-       case 76:
-               if (protocol == LWS_WRITE_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;
-
-       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 = 4; /* text */
-               if (protocol == LWS_WRITE_BINARY)
-                       n = 5; /* binary */
-               if (len < 126) {
-                       buf[-2] = n;
-                       buf[-1] = len;
-                       pre = 2;
-               } else {
-                       if (len < 65536) {
-                               buf[-4] = n;
-                               buf[-3] = 126;
-                               buf[-2] = len >> 8;
-                               buf[-1] = len;
-                               pre = 4;
-                       } else {
-                               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;
-                       }
-               }
-               break;
-       }
-
-#if 0
-       for (n = 0; n < (len + pre + post); n++)
-               fprintf(stderr, "%02X ", buf[n - pre]);
-               
-       fprintf(stderr, "\n");
-#endif
-
-send_raw:
-#ifdef LWS_OPENSSL_SUPPORT
-       if (use_ssl) {
-               n = SSL_write(wsi->ssl, buf - pre, len + pre + post);
-               if (n < 0) {
-                       fprintf(stderr, "ERROR writing to socket");
-                       return -1;
-               }
-       } else {
-#endif
-               n = send(wsi->sock, buf - pre, len + pre + post, 0);
-               if (n < 0) {
-                       fprintf(stderr, "ERROR writing to socket");
-                       return -1;
-               }
-#ifdef LWS_OPENSSL_SUPPORT
-       }
-#endif
-//     fprintf(stderr, "written %d bytes to client\n", (int)len);
-       
-       return 0;
-}
-
-
-/**
- * libwebsockets_serve_http_file() - Send a file back to the client using http
- * @wsi:               Websocket instance (available from user callback)
- * @file:              The file to issue over http
- * @content_type:      The http content type, eg, text/html
- * 
- *     This function is intended to be called from the callback in response
- *     to http requests from the client.  It allows the callback to issue
- *     local files down the http link in a single step.
- */
-
-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/lib/parsers.c b/lib/parsers.c
new file mode 100644 (file)
index 0000000..e52e443
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ * 
+ * Copyright (C) 2010 Andy Green <andy@warmcat.com>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include "private-libwebsockets.h"
+
+const struct lws_tokens lws_tokens[WSI_TOKEN_COUNT] = {
+       { "GET ", 4 },
+       { "Host:", 5 },
+       { "Connection:", 11 },
+       { "Sec-WebSocket-Key1:", 19 },
+       { "Sec-WebSocket-Key2:", 19 },
+       { "Sec-WebSocket-Protocol:", 23 },
+       { "Upgrade:", 8 },
+       { "Origin:", 7 },
+       { "\x0d\x0a", 2 },
+};
+
+/**
+ * libwebsocket_get_uri() - Return the URI path being requested
+ * @wsi:       Websocket instance
+ * 
+ *     The user code can find out the local path being opened from this
+ *     call, it's valid on HTTP or established websocket connections.
+ *     If the client opened the connection with "http://127.0.0.1/xyz/abc.d"
+ *     then this call will return a pointer to "/xyz/abc.d"
+ */
+
+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;
+}
+
+
+int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c)
+{
+       int n;
+
+       switch (wsi->parser_state) {
+       case WSI_TOKEN_GET_URI:
+       case WSI_TOKEN_HOST:
+       case WSI_TOKEN_CONNECTION:
+       case WSI_TOKEN_KEY1:
+       case WSI_TOKEN_KEY2:
+       case WSI_TOKEN_PROTOCOL:
+       case WSI_TOKEN_UPGRADE:
+       case WSI_TOKEN_ORIGIN:
+       case WSI_TOKEN_CHALLENGE:
+       
+               debug("WSI_TOKEN_(%d) '%c'\n", wsi->parser_state, c);
+
+               /* collect into malloc'd buffers */
+               /* optional space swallow */
+               if (!wsi->utf8_token[wsi->parser_state].token_len && c == ' ')
+                       break;
+                       
+               /* special case space terminator for get-uri */
+               if (wsi->parser_state == WSI_TOKEN_GET_URI && c == ' ') {
+                       wsi->utf8_token[wsi->parser_state].token[
+                          wsi->utf8_token[wsi->parser_state].token_len] = '\0';
+                       wsi->parser_state = WSI_TOKEN_SKIPPING;
+                       break;
+               }
+
+               /* allocate appropriate memory */
+               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 !!!");
+                               wsi->parser_state = WSI_TOKEN_SKIPPING;
+                               break;
+                       }
+                       wsi->utf8_token[wsi->parser_state].token =
+                              realloc(wsi->utf8_token[wsi->parser_state].token,
+                                                       wsi->current_alloc_len);
+               }
+
+               /* bail at EOL */
+               if (wsi->parser_state != WSI_TOKEN_CHALLENGE && c == '\x0d') {
+                       wsi->utf8_token[wsi->parser_state].token[
+                          wsi->utf8_token[wsi->parser_state].token_len] = '\0';
+                       wsi->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
+                       break;
+               }
+
+               wsi->utf8_token[wsi->parser_state].token[
+                           wsi->utf8_token[wsi->parser_state].token_len++] = c;
+
+               /* special payload limiting */
+               if (wsi->parser_state == WSI_TOKEN_CHALLENGE &&
+                           wsi->utf8_token[wsi->parser_state].token_len == 8) {
+                       debug("Setting WSI_PARSING_COMPLETE\n");
+                       wsi->parser_state = WSI_PARSING_COMPLETE;
+                       break;
+               }
+               
+               break;
+
+               /* collecting and checking a name part */
+       case WSI_TOKEN_NAME_PART:
+               debug("WSI_TOKEN_NAME_PART '%c'\n", c);
+
+               if (wsi->name_buffer_pos == sizeof(wsi->name_buffer) - 1) {
+                       /* name bigger than we can handle, skip until next */
+                       wsi->parser_state = WSI_TOKEN_SKIPPING;
+                       break;
+               }
+               wsi->name_buffer[wsi->name_buffer_pos++] = c;
+               wsi->name_buffer[wsi->name_buffer_pos] = '\0';
+               
+               for (n = 0; n < WSI_TOKEN_COUNT; n++) {
+                       if (wsi->name_buffer_pos != lws_tokens[n].token_len)
+                               continue;
+                       if (strcmp(lws_tokens[n].token, wsi->name_buffer))
+                               continue;
+                       debug("known hdr '%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 =
+                                                malloc(wsi->current_alloc_len);
+                       wsi->utf8_token[wsi->parser_state].token_len = 0;
+                       n = WSI_TOKEN_COUNT;
+               }
+
+               /* colon delimiter means we just don't know this name */
+
+               if (wsi->parser_state == WSI_TOKEN_NAME_PART && c == ':') {
+                       debug("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! */
+                       debug("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 */
+       case WSI_TOKEN_SKIPPING:
+               debug("WSI_TOKEN_SKIPPING '%c'\n", c);
+               if (c == '\x0d')
+                       wsi->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
+               break;
+       case WSI_TOKEN_SKIPPING_SAW_CR:
+               debug("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
+               if (c == '\x0a')
+                       wsi->parser_state = WSI_TOKEN_NAME_PART;
+               else
+                       wsi->parser_state = WSI_TOKEN_SKIPPING;
+               wsi->name_buffer_pos = 0;
+               break;
+               /* we're done, ignore anything else */
+       case WSI_PARSING_COMPLETE:
+               debug("WSI_PARSING_COMPLETE '%c'\n", c);
+               break;
+               
+       default:        /* keep gcc happy */
+               break;
+       }
+       
+       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)
+                       break;
+
+               debug("Seen that client is requesting "
+                               "a v76 close, sending ack\n");
+               buf[0] = 0xff;
+               buf[1] = 0;
+               n = libwebsocket_write(wsi, buf, 2, LWS_WRITE_HTTP);
+               if (n < 0) {
+                       fprintf(stderr, "ERROR writing to socket");
+                       return -1;
+               }
+               debug("  v76 close ack sent, server closing skt\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;
+}
+
+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 && wsi->callback)
+               wsi->callback(wsi, LWS_CALLBACK_RECEIVE, &wsi->user_space[0],
+                                                             &buf[n], len - n);
+       
+       return -0;
+}
+
+
+
+/**
+ * libwebsocket_write() - Apply protocol then write data to client
+ * @wsi:       Websocket instance (available from user callback)
+ * @buf:       The data to send.  For data being sent on a websocket
+ *             connection (ie, not default http), this buffer MUST have
+ *             LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE the pointer
+ *             and an additional LWS_SEND_BUFFER_POST_PADDING bytes valid
+ *             in the buffer after (buf + len).  This is so the protocol
+ *             header and trailer data can be added in-situ.
+ * @len:       Count of the data bytes in the payload starting from buf
+ * @protocol:  Use LWS_WRITE_HTTP to reply to an http connection, and one
+ *             of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate
+ *             data on a websockets connection.  Remember to allow the extra
+ *             bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT
+ *             are used.
+ *
+ *     This function provides the way to issue data back to the client
+ *     for both http and websocket protocols.
+ * 
+ *     In the case of sending using websocket protocol, be sure to allocate
+ *     valid storage before and after buf as explained above.  This scheme
+ *     allows maximum efficiency of sending data and protocol in a single
+ *     packet while not burdening the user code with any protocol knowledge.
+ */
+
+int libwebsocket_write(struct libwebsocket * wsi, unsigned char *buf,
+                         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) {
+       /* chrome likes this as of 30 Oct */
+       /* Firefox 4.0b6 likes this as of 30 Oct */
+       case 76:
+               if (protocol == LWS_WRITE_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;
+
+       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 = 4; /* text */
+               if (protocol == LWS_WRITE_BINARY)
+                       n = 5; /* binary */
+               if (len < 126) {
+                       buf[-2] = n;
+                       buf[-1] = len;
+                       pre = 2;
+               } else {
+                       if (len < 65536) {
+                               buf[-4] = n;
+                               buf[-3] = 126;
+                               buf[-2] = len >> 8;
+                               buf[-1] = len;
+                               pre = 4;
+                       } else {
+                               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;
+                       }
+               }
+               break;
+       }
+
+#if 0
+       for (n = 0; n < (len + pre + post); n++)
+               fprintf(stderr, "%02X ", buf[n - pre]);
+               
+       fprintf(stderr, "\n");
+#endif
+
+send_raw:
+#ifdef LWS_OPENSSL_SUPPORT
+       if (use_ssl) {
+               n = SSL_write(wsi->ssl, buf - pre, len + pre + post);
+               if (n < 0) {
+                       fprintf(stderr, "ERROR writing to socket");
+                       return -1;
+               }
+       } else {
+#endif
+               n = send(wsi->sock, buf - pre, len + pre + post, 0);
+               if (n < 0) {
+                       fprintf(stderr, "ERROR writing to socket");
+                       return -1;
+               }
+#ifdef LWS_OPENSSL_SUPPORT
+       }
+#endif
+//     fprintf(stderr, "written %d bytes to client\n", (int)len);
+       
+       return 0;
+}
+
+
+/**
+ * libwebsockets_serve_http_file() - Send a file back to the client using http
+ * @wsi:               Websocket instance (available from user callback)
+ * @file:              The file to issue over http
+ * @content_type:      The http content type, eg, text/html
+ * 
+ *     This function is intended to be called from the callback in response
+ *     to http requests from the client.  It allows the callback to issue
+ *     local files down the http link in a single step.
+ */
+
+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/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
new file mode 100644 (file)
index 0000000..4009064
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ * 
+ * Copyright (C) 2010 Andy Green <andy@warmcat.com>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include <sys/types.h> 
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <poll.h>
+#include <sys/mman.h>
+
+#ifdef LWS_OPENSSL_SUPPORT
+#include <openssl/ssl.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#endif
+
+#include "libwebsockets.h"
+
+//#define DEBUG
+
+
+#ifdef DEBUG
+#define debug(format, args...)  \
+      fprintf(stderr, format , ## args)
+#else
+#define debug(format, args...) 
+#endif
+
+#ifdef LWS_OPENSSL_SUPPORT
+extern SSL_CTX *ssl_ctx;
+extern int use_ssl;
+#endif
+
+
+#define MAX_CLIENTS 100
+#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_HTTP,
+       WSI_STATE_HTTP_HEADERS,
+       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
+};
+
+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;
+       int token_len;
+};
+
+
+/*
+ * This is totally opaque to code using the library.  It's exported as a
+ * forward-reference pointer-only declaration; the user can use the pointer with
+ * other APIs to get information out of it.
+ */
+
+struct libwebsocket {
+       int (*callback)(struct libwebsocket *,
+            enum libwebsocket_callback_reasons reason, void *, void *, size_t);
+
+       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;
+
+       enum lws_rx_parse_state lws_rx_parse_state;
+       size_t rx_packet_length;
+       
+#ifdef LWS_OPENSSL_SUPPORT
+       SSL *ssl;
+#endif
+
+       /* last */
+       char user_space[0];
+};
+
+extern void 
+libwebsocket_close_and_free_session(struct libwebsocket *wsi);
index 81be54a..755d932 100644 (file)
@@ -104,7 +104,8 @@ static int websocket_callback(struct libwebsocket * wsi,
         */
        case LWS_CALLBACK_SEND: 
                n = sprintf(p, "%d", pss->number++);
-               n = libwebsocket_write(wsi, (unsigned char *)p, n, LWS_WRITE_TEXT);
+               n = libwebsocket_write(wsi, (unsigned char *)p, n,
+                                                               LWS_WRITE_TEXT);
                if (n < 0) {
                        fprintf(stderr, "ERROR writing to socket");
                        exit(1);
@@ -163,8 +164,10 @@ static struct option options[] = {
 int main(int argc, char **argv)
 {
        int n = 0;
-       const char * cert_path = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
-       const char * key_path = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
+       const char * cert_path =
+                           LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
+       const char * key_path =
+                       LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
 
        fprintf(stderr, "libwebsockets test server\n"
                        "Copyright 2010 Andy Green <andy@warmcat.com> "