GainSpan Wi-Fi library see: http://mbed.org/users/gsfan/notebook/gainspan_wifi/

Dependents:   GSwifi_httpd GSwifi_websocket GSwifi_tcpclient GSwifi_tcpserver ... more

Fork of GSwifi by gs fan

GainSpan Wi-Fi library

The GS1011 is an ultra low power 802.11b wireless module from GainSpan.

see: http://mbed.org/users/gsfan/notebook/gainspan_wifi/

/media/uploads/gsfan/gs_im_002.jpg /media/uploads/gsfan/gs1011m_2.jpg

ゲインスパン Wi-Fi モジュール ライブラリ

ゲインスパン社の低電力 Wi-Fiモジュール(無線LAN) GS1011 シリーズ用のライブラリです。

解説: http://mbed.org/users/gsfan/notebook/gainspan_wifi/

Files at this revision

API Documentation at this revision

Comitter:
gsfan
Date:
Mon Jan 21 05:58:28 2013 +0000
Parent:
22:9b077e2823ce
Child:
24:5c350ae2e703
Commit message:
support websocket

Changed in this revision

GSwifi.cpp Show annotated file Show diff for this revision Revisions of this file
GSwifi.h Show annotated file Show diff for this revision Revisions of this file
GSwifi_httpd.cpp Show annotated file Show diff for this revision Revisions of this file
GSwifi_net.h Show annotated file Show diff for this revision Revisions of this file
dbg.h Show annotated file Show diff for this revision Revisions of this file
sha1.cpp Show annotated file Show diff for this revision Revisions of this file
sha1.h Show annotated file Show diff for this revision Revisions of this file
--- a/GSwifi.cpp	Wed Dec 26 08:41:43 2012 +0000
+++ b/GSwifi.cpp	Mon Jan 21 05:58:28 2013 +0000
@@ -15,24 +15,12 @@
 #include "GSwifi.h"
 #include <ctype.h>
 
-#ifdef GS_UART_DIRECT
-#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
-#define _gs_getc() LPC_UART1->RBR
-#define _gs_putc(c) while(!(LPC_UART1->LSR & (1<<5))); LPC_UART1->THR = c
-#elif defined(TARGET_LPC11U24)
-#define _gs_getc() LPC_USART->RBR
-#define _gs_putc(c) while(!(LPC_USART->LSR & (1<<5))); LPC_USART->THR = c
-#endif
-#else
-#define _gs_getc() _gs.getc()
-#define _gs_putc(c) _gs.putc(c)
-#endif
 
 GSwifi::GSwifi (PinName p_tx, PinName p_rx, int baud) : _gs(p_tx, p_rx), _buf_cmd(GS_CMD_SIZE) {
     _connect = false;
     _status = GSSTAT_READY;
     _escape = 0;
-    _response = 1;
+    _response = GSRES_NONE;
     _gs_mode = GSMODE_COMMAND;
 
     _gs.baud(baud);
@@ -44,7 +32,7 @@
     _connect = false;
     _status = GSSTAT_READY;
     _escape = 0;
-    _response = 1;
+    _response = GSRES_NONE;
     _gs_mode = GSMODE_COMMAND;
 
     _gs.baud(baud);
@@ -95,8 +83,16 @@
     dat = _gs_getc();
     
     if (flg & ((1 << 7)|(1 << 3)|(1 << 4))) return;
+#ifdef DEBUG_VIEW
 //    DBG("%02x_", dat);
-
+#endif
+/*
+    if (dat >= 0x20 && dat < 0x7f) {
+        DBG("_%c", dat);
+    } else {
+        DBG("_%02x", dat);
+    }
+*/
     switch (_gs_mode) {
     case GSMODE_COMMAND: // command responce
         if (_escape) {
@@ -145,7 +141,11 @@
                 _buf_cmd.put(dat);
                 if (dat == '\n') {
                     _gs_enter ++;
-                    if (!_response && _connect) poll_cmd();
+                    DBG("* %d %d *", _response, _connect);
+                    if (_response == GSRES_NONE && _connect) {
+                        DBG("poll_cmd\r\n");
+                        poll_cmd();
+                    }
                 }
             }
         }
@@ -317,7 +317,7 @@
         return 0;
     }
 
-    _response = 1;
+    _response = res;
     _buf_cmd.clear();
     _gs_ok = 0;
     _gs_failure = 0;
@@ -329,11 +329,11 @@
     _gs_putc('\n');
     DBG("command: %s\r\n", cmd);
     if (strlen(cmd) == 0) return 0;
-    wait_ms(10);
+//    wait_ms(10);
     if (timeout) {
         r = cmdResponse(res, timeout);
     }
-    _response = 0;
+    _response = GSRES_NONE;
     return r;
 }
 
@@ -883,7 +883,7 @@
         // received "\n"
         char buf[GS_CMD_SIZE];
 
-        wait_ms(10);
+//        wait_ms(10);
         _gs_enter --;
         i = 0;
         while (_buf_cmd.use() && i < sizeof(buf)) {
@@ -894,7 +894,7 @@
             i ++;
         }
         buf[i] = 0;
-        DBG("poll: %s\r\n", buf);
+        DBG("poll: %d %s\r\n", _gs_enter, buf);
 
         if (i == 0) {
         } else
@@ -1049,8 +1049,8 @@
     if (! _gs_sock[cid].connect) return -1;
 
     _gs_sock[cid].connect = false;
-    delete _gs_sock[cid].data;
-    _gs_sock[cid].data = NULL;
+//    delete _gs_sock[cid].data;
+//    _gs_sock[cid].data = NULL;
     sprintf(cmd, "AT+NCLOSE=%X", cid);
     return command(cmd, GSRES_NORMAL);    
 }
@@ -1071,14 +1071,18 @@
         _gs.printf("\x1bZ%X%04d", cid, len);
         for (i = 0; i < len; i ++) {
             _gs_putc(buf[i]);
-//            DBG("%c", buf[i]);
+#ifdef DEBUG_VIEW
+            DBG("%c", buf[i]);
+#endif
         }
 #else
         _gs.printf("\x1bS%X", cid);
         for (i = 0; i < len; i ++) {
             if (buf[i] >= 0x20 && buf[i] < 0x7f) {
                 _gs_putc(buf[i]);
-//                DBG("%c", buf[i]);
+#ifdef DEBUG_VIEW
+                DBG("%c", buf[i]);
+#endif
             }
         }
         _gs_putc(0x1b);
@@ -1108,7 +1112,9 @@
         _gs.printf("%04d", len);
         for (i = 0; i < len; i ++) {
             _gs_putc(buf[i]);
-//            DBG("%c", buf[i]);
+#ifdef DEBUG_VIEW
+            DBG("%c", buf[i]);
+#endif
         }
 #else
         _gs.printf("\x1bU%X", cid);
@@ -1116,7 +1122,9 @@
         for (i = 0; i < len; i ++) {
             if (buf[i] >= 0x20 && buf[i] < 0x7f) {
                 _gs_putc(buf[i]);
-//                DBG("%c", buf[i]);
+#ifdef DEBUG_VIEW
+                DBG("%c", buf[i]);
+#endif
             }
         }
         _gs_putc(0x1b);
@@ -1303,7 +1311,6 @@
 }
 
 int GSwifi::setBaud (int baud) {
-    char cmd[GS_CMD_SIZE];
 
     if (_status != GSSTAT_READY) return -1;
 
@@ -1418,9 +1425,19 @@
     return 0;
 }
 
-
 #ifdef DEBUG
 // for test
+void GSwifi::dump () {
+    int i;
+    
+    DBG("GS mode=%d, escape=%d, cid=%d\r\n", _gs_mode, _escape, _cid);
+    for (i = 0; i < 16; i ++) {
+        DBG("%d: connect=%d, type=%d, protocol=%d, len=%d\r\n", i, _gs_sock[i].connect, _gs_sock[i].type, _gs_sock[i].protocol, _gs_sock[i].data->use());
+        if (_gs_sock[i].protocol == GSPROT_HTTPD) {
+            DBG("  mode=%d, type=%d, len=%d\r\n", i, _httpd[i].mode, _httpd[i].type, _httpd[i].len);
+        }
+    }
+}
 
 void GSwifi::test () {
 /*
--- a/GSwifi.h	Wed Dec 26 08:41:43 2012 +0000
+++ b/GSwifi.h	Mon Jan 21 05:58:28 2013 +0000
@@ -37,6 +37,19 @@
 #define GS_DATA_SIZE 500
 #endif
 
+#ifdef GS_UART_DIRECT
+#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
+#define _gs_getc() LPC_UART1->RBR
+#define _gs_putc(c) while(!(LPC_UART1->LSR & (1<<5))); LPC_UART1->THR = c
+#elif defined(TARGET_LPC11U24)
+#define _gs_getc() LPC_USART->RBR
+#define _gs_putc(c) while(!(LPC_USART->LSR & (1<<5))); LPC_USART->THR = c
+#endif
+#else
+#define _gs_getc() _gs.getc()
+#define _gs_putc(c) _gs.putc(c)
+#endif
+
 /**
  * Wi-Fi security
  */
@@ -72,6 +85,7 @@
 };
 
 enum GSRESPONCE {
+    GSRES_NONE,
     GSRES_NORMAL,
     GSRES_CONNECT,
     GSRES_WPS,
@@ -361,6 +375,9 @@
      * attach uri to cgi handler
      */
     int attach_httpd (const char *uri, onHttpdCgiFunc ponHttpCgi);
+#ifdef GS_USE_WEBSOCKET
+    int send_websocket (int cid, const char *buf, int len);
+#endif
 #endif
 
     /**
@@ -385,6 +402,7 @@
     int urldecode (char *str, char *buf, int len);
 
 #ifdef DEBUG
+    void dump ();
     void test ();
     int getc();
     void putc(char c);
@@ -405,8 +423,10 @@
 #endif
 
 #ifdef GS_USE_HTTPD
+    int get_handler (char *uri);
     int httpd_request (int cid, GS_httpd *gshttpd, char *dir);
     char *mimetype (char *file);
+    int strnicmp (char *p1, char *p2, int n);
 #endif
 
 private:
@@ -415,7 +435,7 @@
     volatile bool _connect;
     volatile GSSTATUS _status;
     volatile int _gs_ok, _gs_failure, _gs_enter;
-    volatile int _response;
+    volatile GSRESPONCE _response;
     GSMODE _gs_mode;
     int _escape;
     int _cid, _rssi;
@@ -432,6 +452,10 @@
     int _handler_count;
 
     void poll_httpd (int cid, int len);
+#ifdef GS_USE_WEBSOCKET
+    void poll_websocket (int cid, int len);
+    void send_websocket_accept (int cid);
+#endif
 #endif
 };
 
--- a/GSwifi_httpd.cpp	Wed Dec 26 08:41:43 2012 +0000
+++ b/GSwifi_httpd.cpp	Mon Jan 21 05:58:28 2013 +0000
@@ -1,15 +1,11 @@
 #include "dbg.h"
 #include "mbed.h"
 #include "GSwifi.h"
+#include "sha1.h"
+#include <string.h>
 
 #ifdef GS_USE_HTTPD
 
-#define HTTPD_REQUEST 0
-#define HTTPD_HEAD 1
-#define HTTPD_SPACE 2
-#define HTTPD_BODY 3
-#define HTTPD_ERROR 4
-
 #define MIMETABLE_NUM 8
 struct {
     char ext[6];
@@ -31,10 +27,9 @@
 
     if (! _connect || _status != GSSTAT_READY) return -1;
 
+    memset(&_httpd, 0, sizeof(_httpd));
     for (i = 0; i < 16; i ++) {
-        _httpd[i].mode = HTTPD_REQUEST;
-        _httpd[i].buf = NULL;
-        _httpd[i].uri = NULL;
+        _httpd[i].mode = GSHTTPDMODE_REQUEST;
     }
     _handler_count = 0;
 
@@ -57,48 +52,62 @@
     char c;
 
     if (len == 0) {
-        _httpd[cid].mode = HTTPD_REQUEST;
+        // start request
+        _httpd[cid].mode = GSHTTPDMODE_REQUEST;
         _httpd[cid].len = 0;
         _httpd[cid].keepalive = 0;
+#ifdef GS_USE_WEBSOCKET
+        _httpd[cid].websocket = 0;
+#endif
         return;
     }
 
+#ifdef GS_USE_WEBSOCKET
+    if (_httpd[cid].mode >= GSHTTPDMODE_WEBSOCKET) {
+        poll_websocket(cid, len);
+        return;
+    }
+#endif
+
   while (_gs_sock[cid].connect && _gs_sock[cid].data->use()) {
     flg = 0;
     if (_httpd[cid].buf == NULL) {
         _httpd[cid].buf = new char[HTTPD_BUF_SIZE];
     }
+    // get 1 line
     for (j = 0; j < len; j ++) {
         _gs_sock[cid].data->get(&c);
         if (c == '\r') continue;
-        if (c == '\n' && _httpd[cid].mode != HTTPD_BODY) break;
+        if (c == '\n' && _httpd[cid].mode != GSHTTPDMODE_BODY) break;
         
         if (_httpd[cid].len < HTTPD_BUF_SIZE - 1) {
             _httpd[cid].buf[_httpd[cid].len] = c; 
         }
         _httpd[cid].len ++;
-        if (_httpd[cid].len >= _httpd[cid].length && _httpd[cid].mode == HTTPD_BODY) break;
+        if (_httpd[cid].mode == GSHTTPDMODE_BODY && _httpd[cid].len >= _httpd[cid].length) break; // end of body
     }
-    if (j >= len) return;
+    if (j >= len) return; // continue
     if (_httpd[cid].len < HTTPD_BUF_SIZE) {
         _httpd[cid].buf[_httpd[cid].len] = 0;
         DBG("httpd %d: %d %s (%d)\r\n", cid, _httpd[cid].mode, _httpd[cid].buf, _httpd[cid].len);
     }
 
+    // parse
     switch (_httpd[cid].mode) {
-    case HTTPD_REQUEST:
-        if (strncmp(_httpd[cid].buf, "GET ", 4) == 0) {
+    case GSHTTPDMODE_REQUEST:
+        if (strnicmp(_httpd[cid].buf, "GET ", 4) == 0) {
             _httpd[cid].type = GSPROT_HTTPGET;
             j = 4;
         } else
-        if (strncmp(_httpd[cid].buf, "POST ", 5) == 0) {
+        if (strnicmp(_httpd[cid].buf, "POST ", 5) == 0) {
             _httpd[cid].type = GSPROT_HTTPPOST;
             j = 5;
         } else {
-            _httpd[cid].mode = HTTPD_ERROR;
+            _httpd[cid].mode = GSHTTPDMODE_ERROR;
             break;
         }
 
+        // get uri
         for (i = j; i < _httpd[cid].len; i ++) {
             if (_httpd[cid].buf[i] == ' ') break;
         }
@@ -110,79 +119,105 @@
             strncpy(_httpd[cid].uri, &_httpd[cid].buf[j], i);
             _httpd[cid].uri[i] = 0;
         }
-        _httpd[cid].mode = HTTPD_HEAD;
+        _httpd[cid].mode = GSHTTPDMODE_HEAD;
         _httpd[cid].length = 0;
         DBG("uri: %s\r\n", _httpd[cid].uri);
         break;
         
-    case HTTPD_HEAD:
+    case GSHTTPDMODE_HEAD:
         if (_httpd[cid].len == 0) {
-            _httpd[cid].mode = HTTPD_BODY;
+            // blank line (end of header)
+            _httpd[cid].mode = GSHTTPDMODE_BODY;
             if (_httpd[cid].length == 0) flg = 1; // no body
+#ifdef GS_USE_WEBSOCKET
+            if (_httpd[cid].websocket && _httpd[cid].websocket_key) {
+                // enter websocket
+                _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET;
+                _httpd[cid].len = 0;
+                flg = 1;
+            }
+#endif
         } else
-        if (strncmp(_httpd[cid].buf, "Content-Length: ", 16) == 0) {
+        if (strnicmp(_httpd[cid].buf, "Content-Length: ", 16) == 0) {
             _httpd[cid].length = atoi(&_httpd[cid].buf[16]);
         } else
-        if (strncmp(_httpd[cid].buf, "Connection: Keep-Alive", 22) == 0 ||
-          strncmp(_httpd[cid].buf, "Connection: keep-alive", 22) == 0) {
+        if (strnicmp(_httpd[cid].buf, "Connection: Keep-Alive", 22) == 0) {
             if (! _httpd[cid].keepalive) {
                 _httpd[cid].keepalive = HTTPD_KEEPALIVE;
             }
+#ifdef GS_USE_WEBSOCKET
+        } else
+        if (strnicmp(_httpd[cid].buf, "Upgrade: websocket", 18) == 0) {
+            if (! _httpd[cid].websocket) _httpd[cid].websocket = 1;
+        } else
+        if (strnicmp(_httpd[cid].buf, "Sec-WebSocket-Version: ", 23) == 0) {
+            _httpd[cid].websocket = atoi(&_httpd[cid].buf[23]);
+        } else
+        if (strnicmp(_httpd[cid].buf, "Sec-WebSocket-Key: ", 19) == 0) {
+            if (_httpd[cid].websocket_key == NULL) {
+                _httpd[cid].websocket_key = new char[30];
+            }
+            strncpy(_httpd[cid].websocket_key, &_httpd[cid].buf[19], 30);
+#endif
         }
         break;
 
-    case HTTPD_BODY:
+    case GSHTTPDMODE_BODY:
         if (_httpd[cid].len >= _httpd[cid].length) {
             DBG("body: %s\r\n", _httpd[cid].buf);
             flg = 1;
         }
         break;
+
     }
 
+#ifdef GS_USE_WEBSOCKET
+    if (flg && _httpd[cid].mode == GSHTTPDMODE_WEBSOCKET) {
+        // websocket
+        send_websocket_accept(cid);
+        break; // break while
+
+    } else
+#endif
     if (flg) {
         // http request
         _httpd[cid].buf[_httpd[cid].len] = 0;
-        // scan handler
-        flg = 0;
-        for (i = 0; i < _handler_count; i ++) {
-            j = strlen(_handler[i].uri);
-            if (strncmp(_httpd[cid].uri, _handler[i].uri, j) == NULL) {
-                // found
-                _httpd[cid].host = _gs_sock[cid].host;
-                _httpd[cid].file = &_httpd[cid].uri[j];
-                _httpd[cid].query = NULL;
-                for (; j < strlen(_httpd[cid].uri); j ++) {
-                    if (_httpd[cid].uri[j] == '?') {
-                        // query string
-                        _httpd[cid].uri[j] = 0;
-                        _httpd[cid].query = &_httpd[cid].uri[j + 1];
-                        break;
-                    }
-                }
 
-                if (_handler[i].dir) {
-                    // file
-                    httpd_request(cid, &_httpd[cid], _handler[i].dir);
-                    flg = 1;
-                } else
-                if (_handler[i].onHttpCgi) {
-                    // cgi
-                    _handler[i].onHttpCgi(cid, &_httpd[cid]);
-                    _httpd[cid].keepalive = 0;
-                    LOG("%d.%d.%d.%d ", _httpd[cid].host.getIp()[0], _httpd[cid].host.getIp()[1], _httpd[cid].host.getIp()[2], _httpd[cid].host.getIp()[3]);
-                    LOG("%s %s %d 200 -\r\n", _httpd[cid].type == GSPROT_HTTPGET ? "GET" : "POST", _httpd[cid].uri, _httpd[cid].length);
-                    flg = 1;
+        i = get_handler(_httpd[cid].uri);
+        if (i >= 0) {
+            _httpd[cid].host = _gs_sock[cid].host;
+            j = strlen(_handler[i].uri);
+            _httpd[cid].file = &_httpd[cid].uri[j];
+            _httpd[cid].query = NULL;
+            for (; j < strlen(_httpd[cid].uri); j ++) {
+                if (_httpd[cid].uri[j] == '?') {
+                    // query string
+                    _httpd[cid].uri[j] = 0;
+                    _httpd[cid].query = &_httpd[cid].uri[j + 1];
+                    break;
                 }
-                break;
             }
-        }
-        if (! flg) {
+
+            if (_handler[i].dir) {
+                // file
+                httpd_request(cid, &_httpd[cid], _handler[i].dir);
+                flg = 1;
+            } else
+            if (_handler[i].onHttpCgi) {
+                // cgi
+                _handler[i].onHttpCgi(cid, &_httpd[cid]);
+                _httpd[cid].keepalive = 0;
+                LOG("%d.%d.%d.%d ", _httpd[cid].host.getIp()[0], _httpd[cid].host.getIp()[1], _httpd[cid].host.getIp()[2], _httpd[cid].host.getIp()[3]);
+                LOG("%s %s %d 200 -\r\n", _httpd[cid].type == GSPROT_HTTPGET ? "GET" : "POST", _httpd[cid].uri, _httpd[cid].length);
+                flg = 1;
+            }
+        } else {
             // not found
             send_httpd_error(cid, 403);
         }
         
         if (_httpd[cid].keepalive) {
-            _httpd[cid].mode = HTTPD_REQUEST;
+            _httpd[cid].mode = GSHTTPDMODE_REQUEST;
             _httpd[cid].len = 0;
             _httpd[cid].length = 0;
             _httpd[cid].keepalive --;
@@ -191,12 +226,25 @@
         }
     }
 
-    if (_httpd[cid].mode == HTTPD_ERROR) {
+    if (_httpd[cid].mode == GSHTTPDMODE_ERROR) {
         send_httpd_error(cid, 400);
     }
 
     _httpd[cid].len = 0;
-  }
+  } // while
+}
+
+int GSwifi::get_handler (char *uri) {
+    int i, j;
+
+    for (i = 0; i < _handler_count; i ++) {
+        j = strlen(_handler[i].uri);
+        if (strncmp(uri, _handler[i].uri, j) == NULL) {
+            // found
+            return i;
+        }
+    }
+    return -1;
 }
 
 int GSwifi::httpd_request (int cid, GS_httpd *gshttpd, char *dir) {
@@ -261,13 +309,26 @@
     DBG("<%s>\r\n", file);
     for (i = 0; i < MIMETABLE_NUM; i ++) {
         j = strlen(mimetable[i].ext);
-        if (strncmp(&file[strlen(file) - j], mimetable[i].ext, j) == NULL) {
+        if (strnicmp(&file[strlen(file) - j], mimetable[i].ext, j) == NULL) {
             return mimetable[i].type;
         }
     }
     return mimetable[0].type;
 }
 
+int GSwifi::strnicmp (char *p1, char *p2, int n) {
+    int i, r;
+    char c1, c2;
+    
+    for (i = 0; i < n; i ++) {
+        c1 = (p1[i] >= 'a' && p1[i] <= 'z') ? p1[i] - ('a' - 'A'): p1[i];
+        c2 = (p2[i] >= 'a' && p2[i] <= 'z') ? p2[i] - ('a' - 'A'): p2[i];
+        r = c1 - c2;
+        if (r) break;
+    }
+    return r;
+}
+
 void GSwifi::send_httpd_error (int cid, int err) {
     char buf[100], msg[30];
     
@@ -330,4 +391,182 @@
     }
 }
 
+#ifdef GS_USE_WEBSOCKET
+void GSwifi::poll_websocket (int cid, int len) {
+    int i, j, flg;
+    unsigned char c;
+
+    while (_gs_sock[cid].connect && _gs_sock[cid].data->use()) {
+        flg = 0;
+        // get 1 line
+        for (j = 0; j < len; j ++) {
+            _gs_sock[cid].data->get((char*)&c);
+//            DBG("_%c", c);
+
+            switch (_httpd[cid].mode) {
+            case GSHTTPDMODE_WEBSOCKET:
+                if (_httpd[cid].len == 0) {
+                    _httpd[cid].type = c & 0x0f;
+                    _httpd[cid].websocket_flg = c << 8;
+                    _httpd[cid].len ++;
+                } else
+                if (_httpd[cid].len == 1) {
+                    _httpd[cid].websocket_flg |= c;
+                    _httpd[cid].length = c & 0x7f;
+                    _httpd[cid].len ++;
+                    if (_httpd[cid].length < 126) {
+                        _httpd[cid].len = 0;
+                        if (_httpd[cid].websocket_flg & 0x0080) {
+                            _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET_MASK;
+                        } else {
+                            _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET_BODY;
+                        }
+                        DBG("ws length %d\r\n", _httpd[cid].length);
+                    }
+                } else {
+                    // length 16bit,64bit
+                    if (_httpd[cid].len == 2) {
+                        _httpd[cid].length = c;
+                        _httpd[cid].len ++;
+                    } else
+                    if (_httpd[cid].len < 9 && (_httpd[cid].websocket_flg & 0x7f) == 127) {
+                        // 64bit
+                        _httpd[cid].length = (_httpd[cid].length << 8) | c;
+                        _httpd[cid].len ++;
+                    } else {
+                        _httpd[cid].length = (_httpd[cid].length << 8) | c;
+                        _httpd[cid].len = 0;
+                        if (_httpd[cid].websocket_flg & 0x0080) {
+                            _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET_MASK;
+                        } else {
+                            _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET_BODY;
+                        }
+                        DBG("ws length2 %d\r\n", _httpd[cid].length);
+                    }
+                }
+                break;
+
+            case GSHTTPDMODE_WEBSOCKET_MASK:
+                _httpd[cid].websocket_mask[_httpd[cid].len] = c;
+                _httpd[cid].len ++;
+                if (_httpd[cid].len >= 4) {
+                    _httpd[cid].len = 0;
+                    _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET_BODY;
+                    DBG("ws mask\r\n");
+                }
+                break;
+
+            case GSHTTPDMODE_WEBSOCKET_BODY:
+                if (_httpd[cid].len < HTTPD_BUF_SIZE - 1) {
+                    if (_httpd[cid].websocket_flg & 0x0080) {
+                        _httpd[cid].buf[_httpd[cid].len] = c ^ _httpd[cid].websocket_mask[_httpd[cid].len & 0x03]; 
+                    } else {
+                        _httpd[cid].buf[_httpd[cid].len] = c; 
+                    }
+                    _httpd[cid].len ++;
+                }
+                break;
+            }
+
+            if (_httpd[cid].mode == GSHTTPDMODE_WEBSOCKET_BODY && _httpd[cid].len >= _httpd[cid].length) {
+                flg = 1;
+                break;
+            }
+        }
+        if (j >= len) return; // continue
+        if (_httpd[cid].len < HTTPD_BUF_SIZE) {
+            _httpd[cid].buf[_httpd[cid].len] = 0;
+            DBG("websocket %d: (%d)\r\n", cid, _httpd[cid].len);
+        }
+
+    if (flg) {
+        // websocket request
+      DBG("ws type %d\r\n", _httpd[cid].type);
+      switch (_httpd[cid].type) {
+      case 0x00: // continuation
+      case 0x01: // text
+      case 0x02: // binary
+        i = get_handler(_httpd[cid].uri);
+        if (i >= 0) {
+            _httpd[cid].host = _gs_sock[cid].host;
+            j = strlen(_handler[i].uri);
+            _httpd[cid].file = &_httpd[cid].uri[j];
+            _httpd[cid].query = NULL;
+
+            if (_handler[i].onHttpCgi) {
+                // cgi
+                _handler[i].onHttpCgi(cid, &_httpd[cid]);
+                LOG("%d.%d.%d.%d ", _httpd[cid].host.getIp()[0], _httpd[cid].host.getIp()[1], _httpd[cid].host.getIp()[2], _httpd[cid].host.getIp()[3]);
+                LOG("%s %s %d 200 -\r\n", _httpd[cid].type == GSPROT_HTTPGET ? "GET" : "POST", _httpd[cid].uri, _httpd[cid].length);
+                flg = 1;
+            }
+        }
+        break;
+
+      case 0x08: // close
+        close(cid);
+        break;
+
+      case 0x09: // ping
+        _gs_putc(0x8a); // pong
+        _gs_putc(0x04);
+        for (i = 0; i < _httpd[cid].len; i ++) {
+            _gs_putc(_httpd[cid].buf[i]);
+        }
+        break;
+
+      case 0x0a: // pong
+        break;
+      }
+      _httpd[cid].mode = GSHTTPDMODE_WEBSOCKET;
+      _httpd[cid].len = 0;
+      _httpd[cid].length = 0;
+    }
+    } // while
+}
+
+int GSwifi::send_websocket (int cid, const char *buf, int len) {
+    int r;
+    char tmp[10];
+
+    tmp[0] = 0x81; // single, text frame
+    if (len < 126) {
+        tmp[1] = len;
+        r = send(cid, tmp, 2);
+    } else {
+        tmp[1] = 126;
+        tmp[2] = (len >> 8) & 0xff;
+        tmp[3] = len & 0xff;
+        r = send(cid, tmp, 4);
+    }
+    if (r == 0) {
+        r = send(cid, buf, len);
+    }
+    return r;
+}
+
+void GSwifi::send_websocket_accept (int cid) {
+    char buf[100], buf2[20];
+    
+    DBG("websocket accept: %d\r\n", cid);
+
+    send(cid, "HTTP/1.1 101 Switching Protocols\r\n", 34);
+    send(cid, "Upgrade: websocket\r\n", 20);
+    send(cid, "Connection: Upgrade\r\n", 21);
+
+    send(cid, "Sec-WebSocket-Accept: ", 22);
+    strcpy(buf, _httpd[cid].websocket_key);
+    strcat(buf, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+    sha1(buf, strlen(buf), buf2);
+    base64encode(buf2, 20, buf, sizeof(buf));
+    send(cid, buf, strlen(buf));
+    send(cid, "\r\n", 2);
+
+//    send(cid, "Sec-WebSocket-Protocol: chat\r\n", 30);
+    send(cid, "\r\n", 2);
+    LOG("%d.%d.%d.%d ", _httpd[cid].host.getIp()[0], _httpd[cid].host.getIp()[1], _httpd[cid].host.getIp()[2], _httpd[cid].host.getIp()[3]);
+    LOG("%s %s %d 101 - %s\r\n", _httpd[cid].type == GSPROT_HTTPGET ? "GET" : "POST", _httpd[cid].uri, _httpd[cid].length, buf);
+}
 #endif
+
+#endif
--- a/GSwifi_net.h	Wed Dec 26 08:41:43 2012 +0000
+++ b/GSwifi_net.h	Mon Jan 21 05:58:28 2013 +0000
@@ -17,6 +17,7 @@
 #include "host.h"
 
 #define GS_USE_HTTPD  // comment out if not use httpd
+#define GS_USE_WEBSOCKET
 #define GS_USE_SMTP  // comment out if not use smtp
 #define GS_SYSLOG // log for stdout
 
@@ -46,8 +47,19 @@
 
 #define HTTPD_KEEPALIVE 10 // request count
 
+enum GSHTTPDMODE {
+    GSHTTPDMODE_REQUEST,
+    GSHTTPDMODE_HEAD,
+    GSHTTPDMODE_SPACE,
+    GSHTTPDMODE_BODY,
+    GSHTTPDMODE_ERROR,
+    GSHTTPDMODE_WEBSOCKET,
+    GSHTTPDMODE_WEBSOCKET_MASK,
+    GSHTTPDMODE_WEBSOCKET_BODY,
+};
+
 struct GS_httpd {
-    int mode;
+    GSHTTPDMODE mode;
     int type;
     char *buf;  // body
     int len;  // length of buf
@@ -57,6 +69,13 @@
     int length;  // content-length
     int keepalive;
     Host host;
+#ifdef GS_USE_WEBSOCKET
+    int websocket;
+    char *websocket_key;
+    int websocket_flg;
+    char websocket_mask[4];
+    int websocket_payload;
+#endif
 };
 
 typedef void (*onHttpdCgiFunc)(int cid, GS_httpd *gshttpd);
--- a/dbg.h	Wed Dec 26 08:41:43 2012 +0000
+++ b/dbg.h	Mon Jan 21 05:58:28 2013 +0000
@@ -1,4 +1,5 @@
 //#define DEBUG
+//#define DEBUG_VIEW
 
 #ifdef DEBUG 
 #define DBG(...) printf("" __VA_ARGS__) 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sha1.cpp	Mon Jan 21 05:58:28 2013 +0000
@@ -0,0 +1,364 @@
+/*
+ * source from http://www.ipa.go.jp/security/rfc/RFC3174JA.html
+ */
+#include "sha1.h"
+
+/*
+ *  Define the SHA1 circular left shift macro
+ */
+#define SHA1CircularShift(bits,word) \
+                (((word) << (bits)) | ((word) >> (32-(bits))))
+
+/* Local Function Prototyptes */
+void SHA1PadMessage(SHA1Context *);
+void SHA1ProcessMessageBlock(SHA1Context *);
+
+/*
+ *  SHA1Reset
+ *
+ *  Description:
+ *      This function will initialize the SHA1Context in preparation
+ *      for computing a new SHA1 message digest.
+ *
+ *  Parameters:
+ *      context: [in/out]
+ *          The context to reset.
+ *
+ *  Returns:
+ *      sha Error Code.
+ *
+ */
+int SHA1Reset(SHA1Context *context)
+{
+    if (!context)
+    {
+        return shaNull;
+    }
+
+    context->Length_Low             = 0;
+    context->Length_High            = 0;
+    context->Message_Block_Index    = 0;
+
+    context->Intermediate_Hash[0]   = 0x67452301;
+    context->Intermediate_Hash[1]   = 0xEFCDAB89;
+    context->Intermediate_Hash[2]   = 0x98BADCFE;
+    context->Intermediate_Hash[3]   = 0x10325476;
+    context->Intermediate_Hash[4]   = 0xC3D2E1F0;
+
+    context->Computed   = 0;
+    context->Corrupted  = 0;
+
+    return shaSuccess;
+}
+
+/*
+ *  SHA1Result
+ *
+ *  Description:
+ *      This function will return the 160-bit message digest into the
+ *      Message_Digest array  provided by the caller.
+ *      NOTE: The first octet of hash is stored in the 0th element,
+ *            the last octet of hash in the 19th element.
+ *
+ *  Parameters:
+ *      context: [in/out]
+ *          The context to use to calculate the SHA-1 hash.
+ *      Message_Digest: [out]
+ *          Where the digest is returned.
+ *
+ *  Returns:
+ *      sha Error Code.
+ *
+ */
+int SHA1Result( SHA1Context *context,
+                uint8_t Message_Digest[SHA1HashSize])
+{
+    int i;
+
+    if (!context || !Message_Digest)
+    {
+        return shaNull;
+    }
+
+    if (context->Corrupted)
+    {
+        return context->Corrupted;
+    }
+
+    if (!context->Computed)
+    {
+        SHA1PadMessage(context);
+        for(i=0; i<64; ++i)
+        {
+            /* message may be sensitive, clear it out */
+            context->Message_Block[i] = 0;
+        }
+        context->Length_Low = 0;    /* and clear length */
+        context->Length_High = 0;
+        context->Computed = 1;
+    }
+
+    for(i = 0; i < SHA1HashSize; ++i)
+    {
+        Message_Digest[i] = context->Intermediate_Hash[i>>2]
+                            >> 8 * ( 3 - ( i & 0x03 ) );
+    }
+
+    return shaSuccess;
+}
+
+/*
+ *  SHA1Input
+ *
+ *  Description:
+ *      This function accepts an array of octets as the next portion
+ *      of the message.
+ *
+ *  Parameters:
+ *      context: [in/out]
+ *          The SHA context to update
+ *      message_array: [in]
+ *          An array of characters representing the next portion of
+ *          the message.
+ *      length: [in]
+ *          The length of the message in message_array
+ *
+ *  Returns:
+ *      sha Error Code.
+ *
+ */
+int SHA1Input(    SHA1Context    *context,
+                  const uint8_t  *message_array,
+                  unsigned       length)
+{
+    if (!length)
+    {
+        return shaSuccess;
+    }
+
+    if (!context || !message_array)
+    {
+        return shaNull;
+    }
+
+    if (context->Computed)
+    {
+        context->Corrupted = shaStateError;
+        return shaStateError;
+    }
+
+    if (context->Corrupted)
+    {
+         return context->Corrupted;
+    }
+    while(length-- && !context->Corrupted)
+    {
+    context->Message_Block[context->Message_Block_Index++] =
+                    (*message_array & 0xFF);
+
+    context->Length_Low += 8;
+    if (context->Length_Low == 0)
+    {
+        context->Length_High++;
+        if (context->Length_High == 0)
+        {
+            /* Message is too long */
+            context->Corrupted = 1;
+        }
+    }
+
+    if (context->Message_Block_Index == 64)
+    {
+        SHA1ProcessMessageBlock(context);
+    }
+
+    message_array++;
+    }
+
+    return shaSuccess;
+}
+
+/*
+ *  SHA1ProcessMessageBlock
+ *
+ *  Description:
+ *      This function will process the next 512 bits of the message
+ *      stored in the Message_Block array.
+ *
+ *  Parameters:
+ *      None.
+ *
+ *  Returns:
+ *      Nothing.
+ *
+ *  Comments:
+ *      Many of the variable names in this code, especially the
+ *      single character names, were used because those were the
+ *      names used in the publication.
+ *
+ *
+ */
+void SHA1ProcessMessageBlock(SHA1Context *context)
+{
+    const uint32_t K[] =    {       /* Constants defined in SHA-1   */
+                            0x5A827999,
+                            0x6ED9EBA1,
+                            0x8F1BBCDC,
+                            0xCA62C1D6
+                            };
+    int           t;                 /* Loop counter                */
+    uint32_t      temp;              /* Temporary word value        */
+    uint32_t      W[80];             /* Word sequence               */
+    uint32_t      A, B, C, D, E;     /* Word buffers                */
+
+    /*
+     *  Initialize the first 16 words in the array W
+     */
+    for(t = 0; t < 16; t++)
+    {
+        W[t] = context->Message_Block[t * 4] << 24;
+        W[t] |= context->Message_Block[t * 4 + 1] << 16;
+        W[t] |= context->Message_Block[t * 4 + 2] << 8;
+        W[t] |= context->Message_Block[t * 4 + 3];
+    }
+
+    for(t = 16; t < 80; t++)
+    {
+       W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+    }
+
+    A = context->Intermediate_Hash[0];
+    B = context->Intermediate_Hash[1];
+    C = context->Intermediate_Hash[2];
+    D = context->Intermediate_Hash[3];
+    E = context->Intermediate_Hash[4];
+
+    for(t = 0; t < 20; t++)
+    {
+        temp =  SHA1CircularShift(5,A) +
+                ((B & C) | ((~B) & D)) + E + W[t] + K[0];
+        E = D;
+        D = C;
+        C = SHA1CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    for(t = 20; t < 40; t++)
+    {
+        temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
+        E = D;
+        D = C;
+        C = SHA1CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    for(t = 40; t < 60; t++)
+    {
+        temp = SHA1CircularShift(5,A) +
+               ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
+        E = D;
+        D = C;
+        C = SHA1CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    for(t = 60; t < 80; t++)
+    {
+        temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
+        E = D;
+        D = C;
+        C = SHA1CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    context->Intermediate_Hash[0] += A;
+    context->Intermediate_Hash[1] += B;
+    context->Intermediate_Hash[2] += C;
+    context->Intermediate_Hash[3] += D;
+    context->Intermediate_Hash[4] += E;
+
+    context->Message_Block_Index = 0;
+}
+
+
+/*
+ *  SHA1PadMessage
+ *
+ *  Description:
+ *      According to the standard, the message must be padded to an even
+ *      512 bits.  The first padding bit must be a '1'.  The last 64
+ *      bits represent the length of the original message.  All bits in
+ *      between should be 0.  This function will pad the message
+ *      according to those rules by filling the Message_Block array
+ *      accordingly.  It will also call the ProcessMessageBlock function
+ *      provided appropriately.  When it returns, it can be assumed that
+ *      the message digest has been computed.
+ *
+ *  Parameters:
+ *      context: [in/out]
+ *          The context to pad
+ *      ProcessMessageBlock: [in]
+ *          The appropriate SHA*ProcessMessageBlock function
+ *  Returns:
+ *      Nothing.
+ *
+ */
+
+void SHA1PadMessage(SHA1Context *context)
+{
+    /*
+     *  Check to see if the current message block is too small to hold
+     *  the initial padding bits and length.  If so, we will pad the
+     *  block, process it, and then continue padding into a second
+     *  block.
+     */
+    if (context->Message_Block_Index > 55)
+    {
+        context->Message_Block[context->Message_Block_Index++] = 0x80;
+        while(context->Message_Block_Index < 64)
+        {
+            context->Message_Block[context->Message_Block_Index++] = 0;
+        }
+
+        SHA1ProcessMessageBlock(context);
+
+        while(context->Message_Block_Index < 56)
+        {
+            context->Message_Block[context->Message_Block_Index++] = 0;
+        }
+    }
+    else
+    {
+        context->Message_Block[context->Message_Block_Index++] = 0x80;
+        while(context->Message_Block_Index < 56)
+        {
+            context->Message_Block[context->Message_Block_Index++] = 0;
+        }
+    }
+
+    /*
+     *  Store the message length as the last 8 octets
+     */
+    context->Message_Block[56] = context->Length_High >> 24;
+    context->Message_Block[57] = context->Length_High >> 16;
+    context->Message_Block[58] = context->Length_High >> 8;
+    context->Message_Block[59] = context->Length_High;
+    context->Message_Block[60] = context->Length_Low >> 24;
+    context->Message_Block[61] = context->Length_Low >> 16;
+    context->Message_Block[62] = context->Length_Low >> 8;
+    context->Message_Block[63] = context->Length_Low;
+
+    SHA1ProcessMessageBlock(context);
+}
+
+void sha1 (const char *input, int len, char *output) {
+     SHA1Context sha;
+ 
+    SHA1Reset(&sha);
+    SHA1Input(&sha, (unsigned char*)input, len);
+    SHA1Result(&sha, (uint8_t*)output);
+ }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sha1.h	Mon Jan 21 05:58:28 2013 +0000
@@ -0,0 +1,78 @@
+/*
+ * source from http://www.ipa.go.jp/security/rfc/RFC3174JA.html
+ */
+/*
+ *  sha1.h
+ *
+ *  Description:
+ *      This is the header file for code which implements the Secure
+ *      Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
+ *      April 17, 1995.
+ *
+ *      Many of the variable names in this code, especially the
+ *      single character names, were used because those were the names
+ *      used in the publication.
+ *
+ *      Please read the file sha1.c for more information.
+ *
+ */
+
+#ifndef _SHA1_H_
+#define _SHA1_H_
+
+#include "mbed.h"
+/*
+ * If you do not have the ISO standard stdint.h header file, then you
+ * must typdef the following:
+ *    name              meaning
+ *  uint32_t         unsigned 32 bit integer
+ *  uint8_t          unsigned 8 bit integer (i.e., unsigned char)
+ *  int_least16_t    integer of >= 16 bits
+ *
+ */
+
+#ifndef _SHA_enum_
+#define _SHA_enum_
+enum
+{
+    shaSuccess = 0,
+    shaNull,            /* Null pointer parameter */
+    shaInputTooLong,    /* input data too long */
+    shaStateError       /* called Input after Result */
+};
+#endif
+#define SHA1HashSize 20
+
+/*
+ *  This structure will hold context information for the SHA-1
+ *  hashing operation
+ */
+typedef struct SHA1Context
+{
+    uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest  */
+
+    uint32_t Length_Low;            /* Message length in bits      */
+    uint32_t Length_High;           /* Message length in bits      */
+
+                               /* Index into message block array   */
+    int_least16_t Message_Block_Index;
+    uint8_t Message_Block[64];      /* 512-bit message blocks      */
+
+    int Computed;               /* Is the digest computed?         */
+    int Corrupted;             /* Is the message digest corrupted? */
+} SHA1Context;
+
+/*
+ *  Function Prototypes
+ */
+
+int SHA1Reset(  SHA1Context *);
+int SHA1Input(  SHA1Context *,
+                const uint8_t *,
+                unsigned int);
+int SHA1Result( SHA1Context *,
+                uint8_t Message_Digest[SHA1HashSize]);
+
+
+void sha1 (const char *input, int len, char *output);
+#endif