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
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/
ゲインスパン Wi-Fi モジュール ライブラリ
ゲインスパン社の低電力 Wi-Fiモジュール(無線LAN) GS1011 シリーズ用のライブラリです。
解説: http://mbed.org/users/gsfan/notebook/gainspan_wifi/
Diff: GSwifi_httpd.cpp
- Revision:
- 23:a783c62c36d0
- Parent:
- 22:9b077e2823ce
- Child:
- 24:5c350ae2e703
diff -r 9b077e2823ce -r a783c62c36d0 GSwifi_httpd.cpp --- 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