test-case-for-mozilla-issue
authorAndy Green <andy@warmcat.com>
Fri, 29 Oct 2010 13:15:22 +0000 (14:15 +0100)
committerAndy Green <andy@warmcat.com>
Fri, 29 Oct 2010 13:15:22 +0000 (14:15 +0100)
Makefile
libwebsockets.c
libwebsockets.h
test-server.c [new file with mode: 0644]
test.html [new file with mode: 0644]

index 402227a..251925d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,14 @@
+CFLAGS= -Wall -Werror -rdynamic -fPIC -c
+
 all:
-       gcc -Wall -Werror -rdynamic -fPIC -c libwebsockets.c
-       gcc -Wall -Werror -rdynamic -fPIC -c md5.c
+       gcc $(CFLAGS) libwebsockets.c
+       gcc $(CFLAGS) md5.c
        gcc libwebsockets.o md5.o --shared -o libwebsockets.so
+       
+       gcc $(CFLAGS) test-server.c
+       gcc  test-server.o ./libwebsockets.so -o test-server
 
 clean:
-       rm -f *.o *.so
+       rm -f *.o *.so test-server
+       
        
index 9e7c1d8..6f2e03b 100644 (file)
@@ -3,6 +3,7 @@
 #include <string.h>
 #include <ctype.h>
 #include <unistd.h>
+#include <errno.h>
 
 #include <sys/types.h> 
 #include <sys/socket.h>
 #include "libwebsockets.h"
 
 void md5(const unsigned char *input, int ilen, unsigned char output[16]);
+static void libwebsocket_service(struct libwebsocket *wsi, int sock);
+
+#define LWS_MAX_HEADER_NAME_LENGTH 64
+#define LWS_MAX_HEADER_LEN 4096
+#define LWS_INITIAL_HDR_ALLOC 256
+#define LWS_ADDITIONAL_HDR_ALLOC 64
+
+
+enum lws_connection_states {
+       WSI_STATE_CLOSED,
+       WSI_STATE_HANDSHAKE_RX,
+       WSI_STATE_DEAD_SOCKET,
+       WSI_STATE_ESTABLISHED
+};
+
+enum lws_token_indexes {
+       WSI_TOKEN_GET_URI,
+       WSI_TOKEN_HOST,
+       WSI_TOKEN_CONNECTION,
+       WSI_TOKEN_KEY1,
+       WSI_TOKEN_KEY2,
+       WSI_TOKEN_PROTOCOL,
+       WSI_TOKEN_UPGRADE,
+       WSI_TOKEN_ORIGIN,
+       WSI_TOKEN_CHALLENGE,
+       
+       /* always last real token index*/
+       WSI_TOKEN_COUNT,
+       /* parser state additions */
+       WSI_TOKEN_NAME_PART,
+       WSI_TOKEN_SKIPPING,
+       WSI_TOKEN_SKIPPING_SAW_CR,
+       WSI_PARSING_COMPLETE
+};
+
+
+struct lws_tokens {
+       char * token;
+       int token_len;
+};
+
+
+/*
+ * This is totally opaque to code using the library.  It's exported as a
+ * forward-reference pointer-only declaration.
+ */
+
+struct libwebsocket {
+       int (*callback)(struct libwebsocket *,
+                                    enum libwebsocket_callback_reasons reason);
+
+       enum lws_connection_states state;
+
+       char name_buffer[LWS_MAX_HEADER_NAME_LENGTH];
+       int name_buffer_pos;
+       int current_alloc_len;
+       enum lws_token_indexes parser_state;
+       struct lws_tokens utf8_token[WSI_TOKEN_COUNT];
+       int ietf_spec_revision;
+       
+       int sock;
+};
 
-void dostuff(struct libwebsocket *wsi, int sock);
 
 const struct lws_tokens lws_tokens[WSI_TOKEN_COUNT] = {
        { "GET ", 4 },
@@ -29,24 +91,47 @@ const struct lws_tokens lws_tokens[WSI_TOKEN_COUNT] = {
        { "\x0d\x0a", 2 },
 };
 
-int libwebsocket_init(struct libwebsocket *wsi, int port)
+int libwebsocket_create_server(int port, int (*callback)(struct libwebsocket *, enum libwebsocket_callback_reasons))
 {
        int n;
        int sockfd, newsockfd;
        unsigned int clilen;
        struct sockaddr_in serv_addr, cli_addr;
        int pid;
+       struct libwebsocket *wsi = malloc(sizeof(struct libwebsocket));
+     
+       if (!wsi)
+               return -1;
      
        wsi->state = WSI_STATE_CLOSED;
        wsi->name_buffer_pos = 0;
-       wsi->response = NULL;
 
        for (n = 0; n < WSI_TOKEN_COUNT; n++) {
                wsi->utf8_token[n].token = NULL;
                wsi->utf8_token[n].token_len = 0;
        }
+       
+       wsi->callback = callback;
+       wsi->ietf_spec_revision = 0;
  
-       /* fork off a master server for this websocket server */
+       /* sit there listening for connects, accept and spawn session servers */
+       sockfd = socket(AF_INET, SOCK_STREAM, 0);
+       if (sockfd < 0) {
+               fprintf(stderr, "ERROR opening socket");
+       }
+       bzero((char *) &serv_addr, sizeof(serv_addr));
+
+       serv_addr.sin_family = AF_INET;
+       serv_addr.sin_addr.s_addr = INADDR_ANY;
+       serv_addr.sin_port = htons(port);
+       n = bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
+       if (n < 0) {
+              fprintf(stderr, "ERROR on binding %d %d\n", n, errno);
+              return -1;
+        }
+       /* fork off a master server for this websocket server */
  
        n = fork();
        if (n < 0) {
@@ -59,18 +144,6 @@ int libwebsocket_init(struct libwebsocket *wsi, int port)
        if (n)
                return 0;
  
-       /* sit there listening for connects, accept and spawn session servers */
-       sockfd = socket(AF_INET, SOCK_STREAM, 0);
-       if (sockfd < 0) 
-       fprintf(stderr, "ERROR opening socket");
-       bzero((char *) &serv_addr, sizeof(serv_addr));
-
-       serv_addr.sin_family = AF_INET;
-       serv_addr.sin_addr.s_addr = INADDR_ANY;
-       serv_addr.sin_port = htons(port);
-       if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 
-              fprintf(stderr, "ERROR on binding");
               
        listen(sockfd, 5);
     
@@ -78,23 +151,32 @@ int libwebsocket_init(struct libwebsocket *wsi, int port)
                clilen = sizeof(cli_addr);
 
                 newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
-                if (newsockfd < 0) 
+                if (newsockfd < 0) {
                        fprintf(stderr, "ERROR on accept");
+                       continue;
+               }
                        
                /* fork off a new server instance */
                        
                 pid = fork();
-                if (pid < 0)
+                if (pid < 0) {
                        fprintf(stderr, "ERROR on fork");
-                if (!pid)  {
-                       close(sockfd);
-                       
-                       /* sit in dostuff() until session socket closed */
-                       
-                       dostuff(wsi, newsockfd);
-                       exit(0);
-               } else
+                       continue;
+               }
+               
+               if (pid) {
                        close(newsockfd);
+                       continue;
+               }
+
+               /* we are the session process */
+
+               close(sockfd);
+               
+               /* sit in libwebsocket_service() until session socket closed */
+               
+               libwebsocket_service(wsi, newsockfd);
+               exit(0);
        }
 }
 
@@ -104,15 +186,12 @@ void libwebsocket_close(struct libwebsocket *wsi)
 
        wsi->state = WSI_STATE_DEAD_SOCKET;
 
-       if (wsi->websocket_closed_callback)
-               wsi->websocket_closed_callback(wsi);
+       if (wsi->callback)
+               wsi->callback(wsi, LWS_CALLBACK_CLOSED);
 
        for (n = 0; n < WSI_TOKEN_COUNT; n++)
                if (wsi->utf8_token[n].token)
                        free(wsi->utf8_token[n].token);
-                       
-       if (wsi->response)
-               free(wsi->response);
 }
 
 
@@ -174,12 +253,12 @@ static int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c)
                            wsi->utf8_token[wsi->parser_state].token_len++] = c;
 
                /* special payload limiting */
-               if (wsi->parser_state == WSI_TOKEN_CHALLENGE)
-                       if (wsi->utf8_token[wsi->parser_state].token_len == 8) {
-//                             fprintf(stderr, "Setting WSI_PARSING_COMPLETE\n");
-                               wsi->parser_state = WSI_PARSING_COMPLETE;
-                               break;
-                       }
+               if (wsi->parser_state == WSI_TOKEN_CHALLENGE &&
+                           wsi->utf8_token[wsi->parser_state].token_len == 8) {
+//                     fprintf(stderr, "Setting WSI_PARSING_COMPLETE\n");
+                       wsi->parser_state = WSI_PARSING_COMPLETE;
+                       break;
+               }
                
                break;
 
@@ -274,7 +353,8 @@ static int interpret_key(const char *key, unsigned int *result)
 /*
  * We have to take care about parsing because the headers may be split
  * into multiple fragments.  They may contain unknown headers with arbitrary
- * argument lengths.
+ * argument lengths.  So, we parse using a single-character at a time state
+ * machine that is completely independent of packet size.
  */
 
 int libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len)
@@ -283,11 +363,13 @@ int libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len)
        char *p;
        unsigned int key1, key2;
        unsigned char sum[16];
+       char *response;
        
        switch (wsi->state) {
        case WSI_STATE_CLOSED:
                wsi->state = WSI_STATE_HANDSHAKE_RX;
                wsi->parser_state = WSI_TOKEN_NAME_PART;
+               /* fallthru */
        case WSI_STATE_HANDSHAKE_RX:
        
                fprintf(stderr, "issuing %ld bytes to parser\n", len);
@@ -320,7 +402,7 @@ int libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len)
                
                /* create the response packet */
                
-               wsi->response = malloc(256 +
+               response = malloc(256 +
                        wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len +
                        wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len +
                        wsi->utf8_token[WSI_TOKEN_HOST].token_len +
@@ -331,7 +413,7 @@ int libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len)
                
                fprintf(stderr, "'%s;\n", wsi->utf8_token[WSI_TOKEN_HOST].token);
                
-               p = wsi->response;
+               p = response;
                strcpy(p,   "HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0aUpgrade: WebSocket\x0d\x0a");
                p += strlen("HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0aUpgrade: WebSocket\x0d\x0a");
                strcpy(p,   "Connection: Upgrade\x0d\x0aSec-WebSocket-Origin: ");
@@ -349,15 +431,22 @@ int libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len)
                if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) {
                        strcpy(p, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
                        p += wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len;
+               } else {
+                       strcpy(p,   "none");
+                       p += strlen("none");
                }
                strcpy(p,   "\x0d\x0a\x0d\x0a");
                p += strlen("\x0d\x0a\x0d\x0a");
                
+               /* convert the two keys into 32-bit integers */
+               
                if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY1].token, &key1))
                        goto bail;
 
                if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY2].token, &key2))
                        goto bail;
+                       
+               /* lay them out in network byte order (MSB first */
 
                sum[0] = key1 >> 24;
                sum[1] = key1 >> 16;
@@ -367,20 +456,42 @@ int libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len)
                sum[5] = key2 >> 16;
                sum[6] = key2 >> 8;
                sum[7] = key2;
+               
+               /* follow them with the challenge token we were sent */
+               
                memcpy(&sum[8], wsi->utf8_token[WSI_TOKEN_CHALLENGE].token, 8);
 
+               /* 
+                * compute the md5sum of that 16-byte series and use as our
+                * payload after our headers
+                */
+
                md5(sum, 16, (unsigned char *)p);
                p += 16;
 
-               wsi->response_length = p - wsi->response;
+               /* it's complete, go ahead and send it */
+               
+               fprintf(stderr, "issuing response packet %d len\n",
+                                                         (int)(p - response));
+               fwrite(response, 1,  p - response, stderr);
+                       
+               n = write(wsi->sock, response, p - response);
+               if (n < 0) {
+                       fprintf(stderr, "ERROR writing to socket");
+                       goto bail;
+               }
 
-               wsi->state = WSI_STATE_ISSUE_HANDSHAKE;
+               free(response);
+               wsi->state = WSI_STATE_ESTABLISHED;
                
-               if (wsi->websocket_established_callback)
-                       wsi->websocket_established_callback(wsi);
+               /* notify user code that we're ready to roll */
+                               
+               if (wsi->callback)
+                       wsi->callback(wsi, LWS_CALLBACK_ESTABLISHED);
                break;
 
        case WSI_STATE_ESTABLISHED:
+               fprintf(stderr, "received %ld byte packet\n", len);
                break;
        default:
                break;
@@ -393,8 +504,74 @@ bail:
        return -1;
 }
 
+int libwebsocket_write(struct libwebsocket * wsi, void *buf, size_t len)
+{
+       int n;
+       unsigned char hdr[9];
+       
+       if (wsi->state != WSI_STATE_ESTABLISHED)
+               return -1;
+
+       switch (wsi->ietf_spec_revision) {
+       /* chrome */
+       case 0:
+               hdr[0] = 0xff;
+               hdr[1] = len >> 56;
+               hdr[2] = len >> 48;
+               hdr[3] = len >> 40;
+               hdr[4] = len >> 32;
+               hdr[5] = len >> 24;
+               hdr[6] = len >> 16;
+               hdr[7] = len >> 8;
+               hdr[8] = len;
+
+               n = write(wsi->sock, hdr, sizeof hdr);
+               if (n < 0) {
+                       fprintf(stderr, "ERROR writing to socket");
+                       return -1;
+               }
+               break;
+       /* just an unimplemented spec right now apparently */
+       case 2:
+               n = 0;
+               if (len < 126) {
+                       hdr[n++] = 0x04;
+                       hdr[n++] = len;
+               } else {
+                       if (len < 65536) {
+                               hdr[n++] = 0x04; /* text frame */
+                               hdr[n++] = 126;
+                               hdr[n++] = len >> 8;
+                               hdr[n++] = len;
+                       } else {
+                               hdr[n++] = 0x04;
+                               hdr[n++] = 127;
+                               hdr[n++] = len >> 24;
+                               hdr[n++] = len >> 16;
+                               hdr[n++] = len >> 8;
+                               hdr[n++] = len;
+                       }
+               }
+               n = write(wsi->sock, hdr, n);
+               if (n < 0) {
+                       fprintf(stderr, "ERROR writing to socket");
+                       return -1;
+               }
+               break;
+       }
 
-void dostuff(struct libwebsocket *wsi, int sock)
+       n = write(wsi->sock, buf, len);
+       if (n < 0) {
+               fprintf(stderr, "ERROR writing to socket");
+               return -1;
+       }
+       
+       fprintf(stderr, "written %d bytes to websocket\n", (int)len);
+       
+       return 0;
+}
+
+static void libwebsocket_service(struct libwebsocket *wsi, int sock)
 {
        int n;
        unsigned char buf[256];
@@ -435,23 +612,11 @@ void dostuff(struct libwebsocket *wsi, int sock)
                                libwebsocket_read(wsi, buf, n);
                }
                
-               if (wsi->state == WSI_STATE_ISSUE_HANDSHAKE) {
-                       
-                       fprintf(stderr, "issuing response packet %d len\n", wsi->response_length);
-                       
-                       fwrite(wsi->response, 1, wsi->response_length, stderr);
-                       
-                       n = write(sock, wsi->response, wsi->response_length);
-                       if (n < 0) {
-                               fprintf(stderr, "ERROR writing to socket");
-                               exit(1);
-                       }
-                       wsi->state = WSI_STATE_ESTABLISHED;
+               if (wsi->state != WSI_STATE_ESTABLISHED)
                        continue;
-               }
                
-               if (wsi->websocket_send_callback)
-                       wsi->websocket_send_callback(wsi);
+               if (wsi->callback)
+                       wsi->callback(wsi, LWS_CALLBACK_SEND);
        }
 }
 
index 5258a17..870b7fe 100644 (file)
@@ -1,68 +1,15 @@
 
-#define LWS_MAX_HEADER_NAME_LENGTH 64
-#define LWS_MAX_HEADER_LEN 4096
-#define LWS_INITIAL_HDR_ALLOC 256
-#define LWS_ADDITIONAL_HDR_ALLOC 64
-
-
-enum lws_connection_states {
-       WSI_STATE_CLOSED,
-       WSI_STATE_HANDSHAKE_RX,
-       WSI_STATE_ISSUE_HANDSHAKE,
-       WSI_STATE_DEAD_SOCKET,
-       WSI_STATE_ESTABLISHED
-};
-
-enum lws_token_indexes {
-       WSI_TOKEN_GET_URI,
-       WSI_TOKEN_HOST,
-       WSI_TOKEN_CONNECTION,
-       WSI_TOKEN_KEY1,
-       WSI_TOKEN_KEY2,
-       WSI_TOKEN_PROTOCOL,
-       WSI_TOKEN_UPGRADE,
-       WSI_TOKEN_ORIGIN,
-       WSI_TOKEN_CHALLENGE,
-       
-       /* always last real token index*/
-       WSI_TOKEN_COUNT,
-       /* parser state additions */
-       WSI_TOKEN_NAME_PART,
-       WSI_TOKEN_SKIPPING,
-       WSI_TOKEN_SKIPPING_SAW_CR,
-       WSI_PARSING_COMPLETE
-};
-
-
-struct lws_tokens {
-       char * token;
-       int token_len;
-};
-
-struct libwebsocket {
-       
-       /* set these up before calling libwebsocket_init */
-       
-       int (*websocket_established_callback)(struct libwebsocket *);
-       int (*websocket_closed_callback)(struct libwebsocket *);
-       int (*websocket_send_callback)(struct libwebsocket *);
-       int (*websocket_receive_callback)(struct libwebsocket *);
-       
-       /* these are all opaque and maintained by the library */
-       
-       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];
-       char * response;
-       int response_length;
-       
-       int sock;
+enum libwebsocket_callback_reasons {
+       LWS_CALLBACK_ESTABLISHED,
+       LWS_CALLBACK_CLOSED,
+       LWS_CALLBACK_SEND,
+       LWS_CALLBACK_RECEIVE,
 };
 
+struct libwebsocket;
 
-extern int libwebsocket_init(struct libwebsocket *wsi, int port);
+extern int libwebsocket_create_server(int port,
+                 int (*callback)(struct libwebsocket *,
+                                          enum libwebsocket_callback_reasons));
 
+extern int libwebsocket_write(struct libwebsocket *, void *buf, size_t len);
diff --git a/test-server.c b/test-server.c
new file mode 100644 (file)
index 0000000..0056e80
--- /dev/null
@@ -0,0 +1,55 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "libwebsockets.h"
+
+#define PORT 7681
+
+int websocket_callback(struct libwebsocket * wsi,
+                                     enum libwebsocket_callback_reasons reason)
+{
+       int n;
+       char buf[256];
+       static int bump;
+       
+       switch (reason) {
+       case LWS_CALLBACK_ESTABLISHED:
+               fprintf(stderr, "Websocket connection established\n");
+               break;
+
+       case LWS_CALLBACK_CLOSED:
+               fprintf(stderr, "Websocket connection closed\n");
+               break;
+
+       case LWS_CALLBACK_SEND: 
+               sleep(1);
+               n = sprintf(buf, "%d\n", bump++);
+               n = libwebsocket_write(wsi, buf, n);
+               if (n < 0) {
+                       fprintf(stderr, "ERROR writing to socket");
+                       exit(1);
+               }
+               break;
+
+       case LWS_CALLBACK_RECEIVE:
+               break;
+       }
+       return 0;
+}
+
+
+int main(int argv, char **argc)
+{
+       if (libwebsocket_create_server(PORT, websocket_callback) < 0) {
+               fprintf(stderr, "libwebsocket init failed\n");
+               return -1;
+       }
+       
+       fprintf(stderr, "Listening on port %d\n", PORT);
+       
+       while (1)
+               sleep(1);
+
+       return 0;
+}
diff --git a/test.html b/test.html
new file mode 100644 (file)
index 0000000..5d8b46f
--- /dev/null
+++ b/test.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset=utf-8 />
+ <title>Minimal Websocket message sender</title>
+</head>
+
+<body>
+<script>
+       var pos = 0;
+
+    var socket = new WebSocket("ws://127.0.0.1:7681");  
+
+       try {
+//        alert('<p class="event">Socket Status: '+socket.readyState);  
+  
+        socket.onopen = function(){  
+             alert('<p class="event">Socket Status: '+socket.readyState+' (open)');  
+        }  
+  
+        socket.onmessage = got_packet;
+  
+        socket.onclose = function(){  
+             alert('<p class="event">Socket Status:  (Closed)');  
+        }
+          } catch(exception){  
+             alert('<p>Error'+exception);  
+          }
+       
+function got_packet(msg){
+//     alert('got packet' + msg);
+
+       document.write(msg.data + "\n");
+       
+}  
+</script>
+
+</body>
+</html>