private fork
Fork of GSwifiInterface by
Diff: GSwifi/GSwifi_httpd.cpp
- Revision:
- 12:057089026a20
- Child:
- 15:086d1a33a197
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GSwifi/GSwifi_httpd.cpp Fri Nov 22 14:03:07 2013 +0000 @@ -0,0 +1,294 @@ +/* Copyright (C) 2013 gsfan, MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "GSwifi.h" + +#ifdef CFG_ENABLE_HTTPD + +int GSwifi::httpd (int port) { + int i; + + if (!isAssociated() || _state.status != STAT_READY) return -1; + + memset(&_httpd, 0, sizeof(_httpd)); + for (i = 0; i < 16; i ++) { + _httpd[i].mode = HTTPDMODE_REQUEST; + } + _handler_count = 0; + + _httpd_cid = listen(PROTO_TCP, port); + if (_httpd_cid < 0) return -1; + + _con[_httpd_cid].protocol = PROTO_HTTPD; + return _httpd_cid; +} + +void GSwifi::httpdRecvData (int cid, char c) { + + switch (_httpd[cid].mode) { + case HTTPDMODE_REQUEST: + if (_con[cid].buf == NULL) + _con[cid].buf = new CircBuffer<char>(CFG_DATA_SIZE); + _httpd[cid].buf = _con[cid].buf; + _httpd[cid].buf->flush(); + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { + _httpd[cid].buf->queue(c); + _httpd[cid].mode = HTTPDMODE_REQUEST_STR; + } + break; + case HTTPDMODE_REQUEST_STR: + switch (c) { + case 0: + case 0x0d: // CR + break; + case 0x0a: // LF + if (httpdParseRequest(cid)) { + _httpd[cid].mode = HTTPDMODE_REQUEST_STR; + } else { + _httpd[cid].mode = HTTPDMODE_HEADER; + } + _httpd[cid].enter = 0; + break; + default: + _httpd[cid].buf->queue(c); + break; + } + break; + case HTTPDMODE_HEADER: + switch (c) { + case 0: + case 0x0d: // CR + break; + case 0x0a: // LF + if (_httpd[cid].buf->available() == 0) { +// if ((_httpd[cid].enter == 0x0d && c == 0x0a) || (_httpd[cid].enter == 0x0a && c == 0x0a)) { + if (_httpd[cid].enter == 0x0a && c == 0x0a) { + _httpd[cid].buf->flush(); +#ifdef CFG_ENABLE_WEBSOCKET + if (_httpd[cid].websocket) { + INFO("MODE_WEBSOCKET"); + wsAccept(cid); + _httpd[cid].mode = HTTPDMODE_WEBSOCKET; + } else +#endif + if (_httpd[cid].length) { + INFO("MODE_BODY"); + _httpd[cid].mode = HTTPDMODE_BODY; + } else { + INFO("MODE_ENTER"); + _httpd[cid].mode = HTTPDMODE_ENTER; + _con[cid].received = true; + } + } + _httpd[cid].buf->flush(); + _httpd[cid].enter = c; + break; + } + + httpdParseHeader(cid); + _httpd[cid].enter = c; + break; + default: + _httpd[cid].buf->queue(c); + _httpd[cid].enter = 0; + break; + } + break; + case HTTPDMODE_BODY: + _httpd[cid].buf->queue(c); + if (_httpd[cid].buf->available() >= _httpd[cid].length) { + _httpd[cid].mode = HTTPDMODE_ENTER; + _con[cid].received = true; + } + break; +#ifdef CFG_ENABLE_WEBSOCKET + case HTTPDMODE_WEBSOCKET: + case HTTPDMODE_WEBSOCKET_MASK: + case HTTPDMODE_WEBSOCKET_BODY: + wsRecvData(cid, c); + break; +#endif + } +} + +void GSwifi::httpdPoll () { + for (int cid = 0; cid < 16; cid ++) { + if (isConnected(cid) && _con[cid].protocol == PROTO_HTTPD) { + + if (_httpd[cid].mode == HTTPDMODE_ENTER) { + int i = httpdGetHandler(_httpd[cid].uri); + if (i >= 0) { + if (_httpd_handler[i].dir) { + // file + httpdFile(cid, _httpd_handler[i].dir); + } else + if (_httpd_handler[i].func) { + // cgi + _httpd_handler[i].func(cid); +// _httpd[cid].keepalive = 0; + } else { + httpdError(cid, 403); + } + } else { + httpdError(cid, 404); + } + + if (_httpd[cid].keepalive) { + DBG("keepalive %d", _httpd[cid].keepalive); + _httpd[cid].keepalive --; + } else { + close(cid); + } + _httpd[cid].mode = HTTPDMODE_REQUEST; +#ifdef CFG_ENABLE_WEBSOCKET + } else + if (_httpd[cid].mode == HTTPDMODE_WEBSOCKET_ENTER) { + wsParseRequest(cid); + _httpd[cid].mode = HTTPDMODE_WEBSOCKET; +#endif + } + + } + } +} + +int GSwifi::httpdParseRequest (int cid) { + int i, j, len; + char buf[CFG_CMD_SIZE]; + + for (len = 0; len < sizeof(buf); len++) { + if (_httpd[cid].buf->dequeue(&buf[len]) == false) break; + } + buf[len] = 0; + + if (strnicmp(buf, "GET ", 4) == 0) { + _httpd[cid].req = REQ_HTTPGET; + j = 4; + } else + if (strnicmp(buf, "POST ", 5) == 0) { + _httpd[cid].req = REQ_HTTPPOST; + j = 5; + } else { + return -1; + } + + if (_httpd[cid].uri == NULL) + _httpd[cid].uri = (char*)malloc(CFG_CMD_SIZE); + + for (i = 0; i < len - j; i ++) { + _httpd[cid].uri[i] = buf[i + j]; + if (buf[i + j] == ' ' || i >= CFG_CMD_SIZE - 1) { + _httpd[cid].uri[i] = 0; + INFO("URI %d '%s'", _httpd[cid].req, _httpd[cid].uri); + _httpd[cid].mode = HTTPDMODE_HEADER; + _httpd[cid].buf->flush(); + _httpd[cid].length = 0; + _httpd[cid].n = 0; + _httpd[cid].filename = NULL; + _httpd[cid].querystring = NULL; +#ifdef CFG_ENABLE_WEBSOCKET + _httpd[cid].websocket = 0; +#endif + break; + } + } + + i = httpdGetHandler(_httpd[cid].uri); + if (i >= 0) { + _httpd[cid].filename = &_httpd[cid].uri[strlen(_httpd_handler[i].uri)]; + for (i = 0; i < strlen(_httpd[cid].filename); i ++) { + if (_httpd[cid].filename[i] == '?') { + _httpd[cid].filename[i] = 0; + _httpd[cid].querystring = _httpd[cid].filename + i + 1; + break; + } + } + INFO("FILE '%s' QUERY '%s'", _httpd[cid].filename, _httpd[cid].querystring); + } + return 0; +} + +#define HEADER_TABLE_NUM 5 +int GSwifi::httpdParseHeader (int cid) { + int i; + char buf[CFG_CMD_SIZE]; + static const struct HEADER_TABLE { + const char header[24]; + void (GSwifi::*func)(int id, const char*); + } header_table[HEADER_TABLE_NUM] = { + {"Content-Length:", &GSwifi::reqContentLength}, + {"Connection:", &GSwifi::reqConnection}, + {"Upgrade: websocket", &GSwifi::reqUpgrade}, + {"Sec-WebSocket-Version:", &GSwifi::reqWebSocketVersion}, + {"Sec-WebSocket-Key:", &GSwifi::reqWebSocketKey}, + }; + for (i = 0; i < sizeof(buf); i++) { + if (_httpd[cid].buf->dequeue(&buf[i]) == false) break; + } + buf[i] = 0; + + for (i = 0; i < HEADER_TABLE_NUM; i ++) { + if (strnicmp(buf, header_table[i].header, strlen(header_table[i].header)) == 0) { + DBG("parse header %d '%s'\r\n", i, buf); + if (header_table[i].func != NULL) { + (this->*(header_table[i].func))(cid, buf); + } + return 0; + } + } + + return -1; +} + +void GSwifi::reqContentLength (int cid, const char *buf) { + _httpd[cid].length = atoi(&buf[16]); +} + +void GSwifi::reqConnection (int cid, const char *buf) { + if (strnicmp(&buf[12], "Keep-Alive", 10) == 0 && _httpd[cid].keepalive == 0) { + _httpd[cid].keepalive = CFG_HTTPD_KEEPALIVE; + } else { + _httpd[cid].keepalive = 0; + } +} + +#ifdef CFG_ENABLE_WEBSOCKET +void GSwifi::reqUpgrade (int cid, const char *buf) { + if (! _httpd[cid].websocket) _httpd[cid].websocket = 1; +} + +void GSwifi::reqWebSocketVersion (int cid, const char *buf) { + _httpd[cid].websocket = atoi(&buf[23]); +} + +void GSwifi::reqWebSocketKey (int cid, const char *buf) { + if (_httpd[cid].websocket_key == NULL) { + _httpd[cid].websocket_key = (char*)malloc(30); + } + strncpy(_httpd[cid].websocket_key, &buf[19], 30); +} +#else +void GSwifi::reqUpgrade (int cid, const char *buf) { +} +void GSwifi::reqWebSocketVersion (int cid, const char *buf) { +} +void GSwifi::reqWebSocketKey (int cid, const char *buf) { +} +#endif + +#endif