HTTP Server, WebSocket support

Fork of HTTPD by Suga koubou

Committer:
dgriffin65
Date:
Thu Jun 15 20:17:24 2017 +0000
Revision:
1:b724fdb741e7
Parent:
0:d18dff347122
Updated to mbed-os

Who changed what in which revision?

UserRevisionLine numberNew contents of line
okini3939 0:d18dff347122 1 /* Copyright (C) 2013 Hiroshi Suga, MIT License
okini3939 0:d18dff347122 2 *
okini3939 0:d18dff347122 3 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
okini3939 0:d18dff347122 4 * and associated documentation files (the "Software"), to deal in the Software without restriction,
okini3939 0:d18dff347122 5 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
okini3939 0:d18dff347122 6 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
okini3939 0:d18dff347122 7 * furnished to do so, subject to the following conditions:
okini3939 0:d18dff347122 8 *
okini3939 0:d18dff347122 9 * The above copyright notice and this permission notice shall be included in all copies or
okini3939 0:d18dff347122 10 * substantial portions of the Software.
okini3939 0:d18dff347122 11 *
okini3939 0:d18dff347122 12 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
okini3939 0:d18dff347122 13 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
okini3939 0:d18dff347122 14 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
okini3939 0:d18dff347122 15 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
okini3939 0:d18dff347122 16 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
okini3939 0:d18dff347122 17 */
okini3939 0:d18dff347122 18
okini3939 0:d18dff347122 19 #include "HTTPD.h"
okini3939 0:d18dff347122 20
okini3939 0:d18dff347122 21
okini3939 0:d18dff347122 22 int HTTPD::httpdFile (int id, char *dir) {
okini3939 0:d18dff347122 23 FILE *fp;
okini3939 0:d18dff347122 24 int i, len;
okini3939 0:d18dff347122 25 char buf[HTTPD_BUF_SIZE];
okini3939 0:d18dff347122 26 char file[HTTPD_CMD_SIZE];
okini3939 0:d18dff347122 27
okini3939 0:d18dff347122 28 INFO("httpdFile %d %s", id, dir);
okini3939 0:d18dff347122 29
okini3939 0:d18dff347122 30 strcpy(file, dir);
okini3939 0:d18dff347122 31 strcat(file, _state[id].filename);
okini3939 0:d18dff347122 32 if (file[strlen(file) - 1] == '/') {
okini3939 0:d18dff347122 33 strcat(file, "index.html");
okini3939 0:d18dff347122 34 }
okini3939 0:d18dff347122 35 DBG("file: %s\r\n", file);
okini3939 0:d18dff347122 36
okini3939 0:d18dff347122 37 fp = fopen(file, "r");
okini3939 0:d18dff347122 38 if (fp) {
okini3939 0:d18dff347122 39 strcpy(buf, "HTTP/1.1 200 OK\r\n");
dgriffin65 1:b724fdb741e7 40 _state[id].client->send(buf, strlen(buf));
okini3939 0:d18dff347122 41 {
okini3939 0:d18dff347122 42 // file size
okini3939 0:d18dff347122 43 i = ftell(fp);
okini3939 0:d18dff347122 44 fseek(fp, 0, SEEK_END);
okini3939 0:d18dff347122 45 len = ftell(fp);
okini3939 0:d18dff347122 46 fseek(fp, i, SEEK_SET);
okini3939 0:d18dff347122 47 }
okini3939 0:d18dff347122 48
okini3939 0:d18dff347122 49 strcpy(buf, "Server: GSwifi httpd\r\n");
dgriffin65 1:b724fdb741e7 50 _state[id].client->send(buf, strlen(buf));
okini3939 0:d18dff347122 51 if (_state[id].keepalive) {
okini3939 0:d18dff347122 52 strcpy(buf, "Connection: Keep-Alive\r\n");
okini3939 0:d18dff347122 53 } else {
okini3939 0:d18dff347122 54 strcpy(buf, "Connection: close\r\n");
okini3939 0:d18dff347122 55 }
dgriffin65 1:b724fdb741e7 56 _state[id].client->send(buf, strlen(buf));
okini3939 0:d18dff347122 57 sprintf(buf, "Content-Type: %s\r\n", mimetype(file));
dgriffin65 1:b724fdb741e7 58 _state[id].client->send(buf, strlen(buf));
okini3939 0:d18dff347122 59 sprintf(buf, "Content-Length: %d\r\n\r\n", len);
dgriffin65 1:b724fdb741e7 60 _state[id].client->send(buf, strlen(buf));
okini3939 0:d18dff347122 61
okini3939 0:d18dff347122 62 for (;;) {
okini3939 0:d18dff347122 63 i = fread(buf, sizeof(char), sizeof(buf), fp);
okini3939 0:d18dff347122 64 if (i <= 0) break;
dgriffin65 1:b724fdb741e7 65 _state[id].client->send(buf, i);
okini3939 0:d18dff347122 66 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
okini3939 0:d18dff347122 67 if (feof(fp)) break;
okini3939 0:d18dff347122 68 #endif
okini3939 0:d18dff347122 69 }
okini3939 0:d18dff347122 70 fclose(fp);
okini3939 0:d18dff347122 71 return 0;
okini3939 0:d18dff347122 72 }
dgriffin65 1:b724fdb741e7 73
okini3939 0:d18dff347122 74 httpdError(id, 404);
okini3939 0:d18dff347122 75 return -1;
okini3939 0:d18dff347122 76 }
okini3939 0:d18dff347122 77
okini3939 0:d18dff347122 78 void HTTPD::httpdError (int id, int err) {
okini3939 0:d18dff347122 79 char buf[HTTPD_CMD_SIZE], msg[30];
okini3939 0:d18dff347122 80
okini3939 0:d18dff347122 81 switch (err) {
okini3939 0:d18dff347122 82 case 400:
okini3939 0:d18dff347122 83 strcpy(msg, "Bad Request");
okini3939 0:d18dff347122 84 break;
okini3939 0:d18dff347122 85 case 403:
okini3939 0:d18dff347122 86 strcpy(msg, "Forbidden");
okini3939 0:d18dff347122 87 break;
okini3939 0:d18dff347122 88 case 404:
okini3939 0:d18dff347122 89 strcpy(msg, "Not Found");
okini3939 0:d18dff347122 90 break;
okini3939 0:d18dff347122 91 case 500:
okini3939 0:d18dff347122 92 default:
okini3939 0:d18dff347122 93 strcpy(msg, "Internal Server Error");
okini3939 0:d18dff347122 94 break;
okini3939 0:d18dff347122 95 }
okini3939 0:d18dff347122 96 DBG("httpd error: %d %d %s\r\n", id, err, msg);
okini3939 0:d18dff347122 97
okini3939 0:d18dff347122 98 sprintf(buf, "HTTP/1.1 %d %s\r\n", err, msg);
dgriffin65 1:b724fdb741e7 99 _state[id].client->send(buf, strlen(buf));
okini3939 0:d18dff347122 100 strcpy(buf, "Content-Type: text/html\r\n\r\n");
dgriffin65 1:b724fdb741e7 101 _state[id].client->send(buf, strlen(buf));
okini3939 0:d18dff347122 102
okini3939 0:d18dff347122 103 sprintf(buf, "<html><head><title>%d %s</title></head>\r\n", err, msg);
dgriffin65 1:b724fdb741e7 104 _state[id].client->send(buf, strlen(buf));
okini3939 0:d18dff347122 105 sprintf(buf, "<body><h1>%s</h1></body></html>\r\n", msg);
dgriffin65 1:b724fdb741e7 106 _state[id].client->send(buf, strlen(buf));
okini3939 0:d18dff347122 107 wait_ms(100);
okini3939 0:d18dff347122 108 _state[id].client->close();
dgriffin65 1:b724fdb741e7 109 //WARN("%d.%d.%d.%d ", _httpd[cid].host.getIp()[0], _httpd[cid].host.getIp()[1], _httpd[cid].host.getIp()[2], _httpd[cid].host.getIp()[3]);
dgriffin65 1:b724fdb741e7 110 //WARN("%s %s %d %d -\r\n", _httpd[cid].type == GSPROT_HTTPGET ? "GET" : "POST", _httpd[cid].uri, _httpd[cid].length, err);
okini3939 0:d18dff347122 111 }
okini3939 0:d18dff347122 112
okini3939 0:d18dff347122 113 void HTTPD::recvData (int id, char c) {
okini3939 0:d18dff347122 114
okini3939 0:d18dff347122 115 switch (_state[id].mode) {
okini3939 0:d18dff347122 116 case MODE_REQUEST:
okini3939 0:d18dff347122 117 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
okini3939 0:d18dff347122 118 _state[id].buf->flush();
okini3939 0:d18dff347122 119 _state[id].buf->queue(c);
okini3939 0:d18dff347122 120 _state[id].mode = MODE_REQSTR;
okini3939 0:d18dff347122 121 } else {
okini3939 0:d18dff347122 122 _state[id].buf->flush();
okini3939 0:d18dff347122 123 }
okini3939 0:d18dff347122 124 break;
okini3939 0:d18dff347122 125 case MODE_REQSTR:
okini3939 0:d18dff347122 126 switch (c) {
okini3939 0:d18dff347122 127 case 0:
okini3939 0:d18dff347122 128 break;
okini3939 0:d18dff347122 129 case 0x0a: // LF
okini3939 0:d18dff347122 130 case 0x0d: // CR
okini3939 0:d18dff347122 131 if (parseRequest(id)) {
okini3939 0:d18dff347122 132 _state[id].mode = MODE_REQSTR;
okini3939 0:d18dff347122 133 } else {
okini3939 0:d18dff347122 134 _state[id].mode = MODE_HEADER;
okini3939 0:d18dff347122 135 }
okini3939 0:d18dff347122 136 _state[id].enter = 0;
okini3939 0:d18dff347122 137 break;
okini3939 0:d18dff347122 138 default:
okini3939 0:d18dff347122 139 _state[id].buf->queue(c);
okini3939 0:d18dff347122 140 break;
okini3939 0:d18dff347122 141 }
okini3939 0:d18dff347122 142 break;
okini3939 0:d18dff347122 143 case MODE_HEADER:
okini3939 0:d18dff347122 144 switch (c) {
okini3939 0:d18dff347122 145 case 0:
okini3939 0:d18dff347122 146 break;
okini3939 0:d18dff347122 147 case 0x0a: // LF
okini3939 0:d18dff347122 148 case 0x0d: // CR
okini3939 0:d18dff347122 149 if (_state[id].buf->available() == 0) {
okini3939 0:d18dff347122 150 if ((_state[id].enter == 0x0d && c == 0x0a) || (_state[id].enter == 0x0a && c == 0x0a)) {
okini3939 0:d18dff347122 151 _state[id].buf->flush();
okini3939 0:d18dff347122 152 if (_state[id].websocket) {
okini3939 0:d18dff347122 153 INFO("MODE_WEBSOCKET");
okini3939 0:d18dff347122 154 acceptWebsocket(id);
okini3939 0:d18dff347122 155 _state[id].mode = MODE_WEBSOCKET;
okini3939 0:d18dff347122 156 } else
okini3939 0:d18dff347122 157 if (_state[id].length) {
okini3939 0:d18dff347122 158 INFO("MODE_BODY");
okini3939 0:d18dff347122 159 _state[id].mode = MODE_BODY;
okini3939 0:d18dff347122 160 } else {
okini3939 0:d18dff347122 161 INFO("MODE_ENTER");
okini3939 0:d18dff347122 162 _state[id].mode = MODE_ENTER;
okini3939 0:d18dff347122 163 }
okini3939 0:d18dff347122 164 }
okini3939 0:d18dff347122 165 _state[id].enter = c;
okini3939 0:d18dff347122 166 _state[id].buf->flush();
okini3939 0:d18dff347122 167 break;
okini3939 0:d18dff347122 168 }
okini3939 0:d18dff347122 169
okini3939 0:d18dff347122 170 parseHeader(id);
okini3939 0:d18dff347122 171 _state[id].enter = 0;
okini3939 0:d18dff347122 172 break;
okini3939 0:d18dff347122 173 default:
okini3939 0:d18dff347122 174 _state[id].buf->queue(c);
okini3939 0:d18dff347122 175 _state[id].enter = 0;
okini3939 0:d18dff347122 176 break;
okini3939 0:d18dff347122 177 }
okini3939 0:d18dff347122 178 break;
okini3939 0:d18dff347122 179 case MODE_BODY:
okini3939 0:d18dff347122 180 _state[id].buf->queue(c);
okini3939 0:d18dff347122 181 if (_state[id].buf->available() >= _state[id].length) {
okini3939 0:d18dff347122 182 _state[id].mode = MODE_ENTER;
okini3939 0:d18dff347122 183 }
okini3939 0:d18dff347122 184 break;
okini3939 0:d18dff347122 185 case MODE_WEBSOCKET:
okini3939 0:d18dff347122 186 case MODE_WEBSOCKET_MASK:
okini3939 0:d18dff347122 187 case MODE_WEBSOCKET_BODY:
okini3939 0:d18dff347122 188 recvWS(id, c);
okini3939 0:d18dff347122 189 break;
okini3939 0:d18dff347122 190 }
okini3939 0:d18dff347122 191
okini3939 0:d18dff347122 192 if (_state[id].mode == MODE_ENTER) {
okini3939 0:d18dff347122 193 int i = getHandler(_state[id].uri);
dgriffin65 1:b724fdb741e7 194 printf("handler = %d, uri = %s\r\n", i, _state[id].uri);
okini3939 0:d18dff347122 195 if (i >= 0) {
okini3939 0:d18dff347122 196 if (_handler[i].dir) {
okini3939 0:d18dff347122 197 // file
okini3939 0:d18dff347122 198 httpdFile(id, _handler[i].dir);
dgriffin65 1:b724fdb741e7 199 } else if (_handler[i].funcCgi) {
okini3939 0:d18dff347122 200 // cgi
okini3939 0:d18dff347122 201 _handler[i].funcCgi(id);
dgriffin65 1:b724fdb741e7 202 _state[id].keepalive = 0;
okini3939 0:d18dff347122 203 } else {
okini3939 0:d18dff347122 204 httpdError(id, 403);
okini3939 0:d18dff347122 205 }
okini3939 0:d18dff347122 206 } else {
okini3939 0:d18dff347122 207 httpdError(id, 404);
okini3939 0:d18dff347122 208 }
okini3939 0:d18dff347122 209
okini3939 0:d18dff347122 210 if (_state[id].keepalive) {
okini3939 0:d18dff347122 211 DBG("keepalive %d", _state[id].keepalive);
dgriffin65 1:b724fdb741e7 212 _state[id].keepalive--;
okini3939 0:d18dff347122 213 } else {
okini3939 0:d18dff347122 214 _state[id].client->close();
okini3939 0:d18dff347122 215 }
okini3939 0:d18dff347122 216 _state[id].mode = MODE_REQUEST;
okini3939 0:d18dff347122 217 } else
okini3939 0:d18dff347122 218 if (_state[id].mode == MODE_WEBSOCKET_ENTER) {
okini3939 0:d18dff347122 219 parseWebsocket(id);
okini3939 0:d18dff347122 220 _state[id].mode = MODE_WEBSOCKET;
okini3939 0:d18dff347122 221 }
okini3939 0:d18dff347122 222 }
okini3939 0:d18dff347122 223
okini3939 0:d18dff347122 224 int HTTPD::parseRequest (int id) {
okini3939 0:d18dff347122 225 int i, j, len;
okini3939 0:d18dff347122 226 char buf[HTTPD_CMD_SIZE];
okini3939 0:d18dff347122 227
okini3939 0:d18dff347122 228 for (len = 0; len < sizeof(buf); len++) {
okini3939 0:d18dff347122 229 if (_state[id].buf->dequeue(&buf[len]) == false) break;
okini3939 0:d18dff347122 230 }
okini3939 0:d18dff347122 231 buf[len] = 0;
okini3939 0:d18dff347122 232
okini3939 0:d18dff347122 233 if (strnicmp(buf, "GET ", 4) == 0) {
okini3939 0:d18dff347122 234 _state[id].req = REQ_HTTPGET;
okini3939 0:d18dff347122 235 j = 4;
okini3939 0:d18dff347122 236 } else
okini3939 0:d18dff347122 237 if (strnicmp(buf, "POST ", 5) == 0) {
okini3939 0:d18dff347122 238 _state[id].req = REQ_HTTPPOST;
okini3939 0:d18dff347122 239 j = 5;
okini3939 0:d18dff347122 240 } else {
okini3939 0:d18dff347122 241 return -1;
okini3939 0:d18dff347122 242 }
okini3939 0:d18dff347122 243
okini3939 0:d18dff347122 244 for (i = 0; i < len - j; i ++) {
okini3939 0:d18dff347122 245 _state[id].uri[i] = buf[i + j];
okini3939 0:d18dff347122 246 if (buf[i + j] == ' ' || i >= sizeof(buf) - 1) {
okini3939 0:d18dff347122 247 _state[id].uri[i] = 0;
okini3939 0:d18dff347122 248 INFO("URI %d '%s'", _state[id].req, _state[id].uri);
okini3939 0:d18dff347122 249 _state[id].mode = MODE_HEADER;
okini3939 0:d18dff347122 250 _state[id].buf->flush();
okini3939 0:d18dff347122 251 _state[id].length = 0;
okini3939 0:d18dff347122 252 _state[id].n = 0;
okini3939 0:d18dff347122 253 _state[id].websocket = 0;
okini3939 0:d18dff347122 254 _state[id].filename = NULL;
okini3939 0:d18dff347122 255 _state[id].querystring = NULL;
okini3939 0:d18dff347122 256 break;
okini3939 0:d18dff347122 257 }
okini3939 0:d18dff347122 258 }
okini3939 0:d18dff347122 259
okini3939 0:d18dff347122 260 i = getHandler(_state[id].uri);
okini3939 0:d18dff347122 261 if (i >= 0) {
okini3939 0:d18dff347122 262 _state[id].filename = &_state[id].uri[strlen(_handler[i].uri)];
okini3939 0:d18dff347122 263 for (i = 0; i < strlen(_state[id].filename); i ++) {
okini3939 0:d18dff347122 264 if (_state[id].filename[i] == '?') {
okini3939 0:d18dff347122 265 _state[id].filename[i] = 0;
okini3939 0:d18dff347122 266 _state[id].querystring = _state[id].filename + i + 1;
okini3939 0:d18dff347122 267 break;
okini3939 0:d18dff347122 268 }
okini3939 0:d18dff347122 269 }
okini3939 0:d18dff347122 270 INFO("FILE '%s' QUERY '%s'", _state[id].filename, _state[id].querystring);
okini3939 0:d18dff347122 271 }
okini3939 0:d18dff347122 272 return 0;
okini3939 0:d18dff347122 273 }
okini3939 0:d18dff347122 274
okini3939 0:d18dff347122 275 #define HEADER_TABLE_NUM 5
okini3939 0:d18dff347122 276 int HTTPD::parseHeader (int id) {
okini3939 0:d18dff347122 277 int i;
okini3939 0:d18dff347122 278 char buf[HTTPD_CMD_SIZE];
okini3939 0:d18dff347122 279 static const struct HEADER_TABLE {
okini3939 0:d18dff347122 280 const char header[24];
okini3939 0:d18dff347122 281 void (HTTPD::*func)(int id, const char*);
okini3939 0:d18dff347122 282 } header_table[HEADER_TABLE_NUM] = {
okini3939 0:d18dff347122 283 {"Content-Length:", &HTTPD::reqContentLength},
okini3939 0:d18dff347122 284 {"Connection:", &HTTPD::reqConnection},
okini3939 0:d18dff347122 285 {"Upgrade: websocket", &HTTPD::reqUpgrade},
okini3939 0:d18dff347122 286 {"Sec-WebSocket-Version:", &HTTPD::reqWebSocketVersion},
okini3939 0:d18dff347122 287 {"Sec-WebSocket-Key:", &HTTPD::reqWebSocketKey},
okini3939 0:d18dff347122 288 };
okini3939 0:d18dff347122 289 for (i = 0; i < sizeof(buf); i++) {
okini3939 0:d18dff347122 290 if (_state[id].buf->dequeue(&buf[i]) == false) break;
okini3939 0:d18dff347122 291 }
okini3939 0:d18dff347122 292 buf[i] = 0;
okini3939 0:d18dff347122 293
okini3939 0:d18dff347122 294 for (i = 0; i < HEADER_TABLE_NUM; i ++) {
okini3939 0:d18dff347122 295 if (strnicmp(buf, header_table[i].header, strlen(header_table[i].header)) == 0) {
okini3939 0:d18dff347122 296 DBG("parse header %d '%s'\r\n", i, buf);
okini3939 0:d18dff347122 297 if (header_table[i].func != NULL) {
okini3939 0:d18dff347122 298 (this->*(header_table[i].func))(id, buf);
okini3939 0:d18dff347122 299 }
okini3939 0:d18dff347122 300 return 0;
okini3939 0:d18dff347122 301 }
okini3939 0:d18dff347122 302 }
okini3939 0:d18dff347122 303
okini3939 0:d18dff347122 304 return -1;
okini3939 0:d18dff347122 305 }
okini3939 0:d18dff347122 306
okini3939 0:d18dff347122 307 void HTTPD::reqContentLength (int id, const char *buf) {
okini3939 0:d18dff347122 308 _state[id].length = atoi(&buf[16]);
okini3939 0:d18dff347122 309 }
okini3939 0:d18dff347122 310
okini3939 0:d18dff347122 311 void HTTPD::reqConnection (int id, const char *buf) {
okini3939 0:d18dff347122 312 if (strnicmp(&buf[12], "Keep-Alive", 10) == 0 && _state[id].keepalive == 0) {
okini3939 0:d18dff347122 313 _state[id].keepalive = HTTPD_KEEPALIVE;
okini3939 0:d18dff347122 314 } else {
okini3939 0:d18dff347122 315 _state[id].keepalive = 0;
okini3939 0:d18dff347122 316 }
okini3939 0:d18dff347122 317 }
okini3939 0:d18dff347122 318
okini3939 0:d18dff347122 319 void HTTPD::reqUpgrade (int id, const char *buf) {
okini3939 0:d18dff347122 320 if (! _state[id].websocket) _state[id].websocket = 1;
okini3939 0:d18dff347122 321 }
okini3939 0:d18dff347122 322
okini3939 0:d18dff347122 323 void HTTPD::reqWebSocketVersion (int id, const char *buf) {
okini3939 0:d18dff347122 324 _state[id].websocket = atoi(&buf[23]);
okini3939 0:d18dff347122 325 }
okini3939 0:d18dff347122 326
okini3939 0:d18dff347122 327 void HTTPD::reqWebSocketKey (int id, const char *buf) {
okini3939 0:d18dff347122 328 if (_state[id].websocket_key == NULL) {
okini3939 0:d18dff347122 329 _state[id].websocket_key = (char*)malloc(30);
okini3939 0:d18dff347122 330 }
okini3939 0:d18dff347122 331 strncpy(_state[id].websocket_key, &buf[19], 30);
okini3939 0:d18dff347122 332 }