more handlers

Dependents:   bandwidth-meter-net mbedRail24v

Fork of Tiny-HTTPD by ban4jp -

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HTTPD_req.cpp Source File

HTTPD_req.cpp

00001 /* Copyright (C) 2013 Hiroshi Suga, MIT License
00002  *
00003  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00004  * and associated documentation files (the "Software"), to deal in the Software without restriction,
00005  * including without limitation the rights to use, copy, modify, merge, publish, distribute,
00006  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
00007  * furnished to do so, subject to the following conditions:
00008  *
00009  * The above copyright notice and this permission notice shall be included in all copies or
00010  * substantial portions of the Software.
00011  *
00012  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00013  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00014  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00015  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00016  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00017  */
00018 
00019 #include "HTTPD.h"
00020 
00021 
00022 int HTTPD::httpdFile (int id, char *dir) {
00023     FILE *fp;
00024     int i, len;
00025     char buf[HTTPD_BUF_SIZE];
00026     char file[HTTPD_CMD_SIZE];
00027 
00028     INFO("httpdFile %d %s", id, dir);
00029 
00030     strcpy(file, dir);
00031     strcat(file, _state[id].filename);
00032     if (file[strlen(file) - 1] == '/') {
00033         strcat(file, "index.html");
00034     }
00035     DBG("file: %s\r\n", file);
00036 
00037     fp = fopen(file, "r");
00038     if (fp) {
00039         strcpy(buf, "HTTP/1.1 200 OK\r\n");
00040         _state[id].client->send_all(buf, strlen(buf));
00041         {
00042             // file size
00043             i = ftell(fp);
00044             fseek(fp, 0, SEEK_END);
00045             len = ftell(fp);
00046             fseek(fp, i, SEEK_SET);
00047         }
00048 
00049         strcpy(buf, server_name);
00050         _state[id].client->send_all(buf, strlen(buf));
00051         if (_state[id].keepalive) {
00052             strcpy(buf, "Connection: Keep-Alive\r\n");
00053         } else {
00054             strcpy(buf, "Connection: close\r\n");
00055         }
00056         _state[id].client->send_all(buf, strlen(buf));
00057         sprintf(buf, "Content-Type: %s\r\n", mimetype(file));
00058         _state[id].client->send_all(buf, strlen(buf));
00059         sprintf(buf, "Content-Length: %d\r\n\r\n", len);
00060         _state[id].client->send_all(buf, strlen(buf));
00061 
00062         for (;;) {
00063             i = fread(buf, sizeof(char), sizeof(buf), fp);
00064             if (i <= 0) break;
00065             _state[id].client->send_all(buf, i);
00066 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
00067             if (feof(fp)) break;
00068 #endif
00069         }
00070         fclose(fp);
00071         return 0;
00072     }
00073 
00074     httpdError(id, 404);
00075     return -1;
00076 }
00077 
00078 void HTTPD::httpdError (int id, int err) {
00079     char buf[HTTPD_CMD_SIZE], msg[30];
00080     
00081     switch (err) {
00082     case 400:
00083         strcpy(msg, "Bad Request");
00084         break;
00085     case 403:
00086         strcpy(msg, "Forbidden");
00087         break;
00088     case 404:
00089         strcpy(msg, "Not Found");
00090         break;
00091     case 500:
00092     default:
00093         strcpy(msg, "Internal Server Error");
00094         break;
00095     }
00096     DBG("httpd error: %d %d %s\r\n", id, err, msg);
00097     
00098     sprintf(buf, "HTTP/1.1 %d %s\r\n", err, msg);
00099     _state[id].client->send_all(buf, strlen(buf));
00100     strcpy(buf, "Content-Type: text/html\r\n\r\n");
00101     _state[id].client->send_all(buf, strlen(buf));
00102 
00103     sprintf(buf, "<html><head><title>%d %s</title></head>\r\n", err, msg);
00104     _state[id].client->send_all(buf, strlen(buf));
00105     sprintf(buf, "<body><h1>%s</h1></body></html>\r\n", msg);
00106     _state[id].client->send_all(buf, strlen(buf));
00107     wait_ms(100);
00108     _state[id].client->close();
00109 //    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]);
00110 //    WARN("%s %s %d %d -\r\n", _httpd[cid].type == GSPROT_HTTPGET ? "GET" : "POST", _httpd[cid].uri, _httpd[cid].length, err);
00111 }
00112 
00113 
00114 void HTTPD::recvData (int id, char c) {
00115 
00116     switch (_state[id].mode) {
00117     case MODE_REQUEST:
00118         if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
00119             _state[id].buf->flush();
00120             _state[id].buf->queue(c);
00121             _state[id].mode = MODE_REQSTR;
00122         } else {
00123             _state[id].buf->flush();
00124         }
00125         break;
00126     case MODE_REQSTR:
00127         switch (c) {
00128         case 0:
00129             break;
00130         case 0x0a: // LF
00131         case 0x0d: // CR
00132             if (parseRequest(id)) {
00133                 _state[id].mode = MODE_REQSTR;
00134             } else {
00135                 _state[id].mode = MODE_HEADER;
00136             }
00137             _state[id].enter = 0;
00138             break;
00139         default:
00140             _state[id].buf->queue(c);
00141             break;
00142         }
00143         break;
00144     case MODE_HEADER:
00145         switch (c) {
00146         case 0:
00147             break;
00148         case 0x0a: // LF
00149         case 0x0d: // CR
00150             if (_state[id].buf->available() == 0) {
00151                 if ((_state[id].enter == 0x0d && c == 0x0a) || (_state[id].enter == 0x0a && c == 0x0a)) {
00152                     _state[id].buf->flush();
00153                     if (_state[id].websocket) {
00154                         INFO("MODE_WEBSOCKET");
00155                         acceptWebsocket(id);
00156                         _state[id].mode = MODE_WEBSOCKET;
00157                     } else
00158                     if (_state[id].length) {
00159                         INFO("MODE_BODY");
00160                         _state[id].mode = MODE_BODY;
00161                     } else {
00162                         INFO("MODE_ENTER");
00163                         _state[id].mode = MODE_ENTER;
00164                     }
00165                 }
00166                 _state[id].enter = c;
00167                 _state[id].buf->flush();
00168                 break;
00169             }
00170 
00171             parseHeader(id);
00172             _state[id].enter = 0;
00173             break;
00174         default:
00175             _state[id].buf->queue(c);
00176             _state[id].enter = 0;
00177             break;
00178         }
00179         break;
00180     case MODE_BODY:
00181         _state[id].buf->queue(c);
00182         if (_state[id].buf->available() >= _state[id].length) {
00183             _state[id].mode = MODE_ENTER;
00184         }
00185         break;
00186     case MODE_WEBSOCKET:
00187     case MODE_WEBSOCKET_MASK:
00188     case MODE_WEBSOCKET_BODY:
00189         recvWS(id, c);
00190         break;
00191     }
00192 
00193     if (_state[id].mode == MODE_ENTER) {
00194         int i = getHandler(_state[id].uri);
00195         if (i >= 0) { 
00196             if (_handler[i].dir) {
00197                 // file
00198                 httpdFile(id, _handler[i].dir);
00199             } else
00200             if (_handler[i].funcCgi) {
00201                 // cgi
00202                 _handler[i].funcCgi(id);
00203 //                _state[id].keepalive = 0;
00204             } else {
00205                 httpdError(id, 403);
00206             }
00207         } else {
00208             httpdError(id, 404);
00209         }
00210 
00211         if (_state[id].keepalive) {
00212             DBG("keepalive %d", _state[id].keepalive);
00213             _state[id].keepalive --;
00214         } else {
00215             _state[id].client->close();
00216         }
00217         _state[id].mode = MODE_REQUEST;
00218     } else
00219     if (_state[id].mode == MODE_WEBSOCKET_ENTER) {
00220         parseWebsocket(id);
00221         _state[id].mode = MODE_WEBSOCKET;
00222     }
00223 }
00224 
00225 int HTTPD::parseRequest (int id) {
00226     int i, j, len;
00227     char buf[HTTPD_CMD_SIZE];
00228 
00229     for (len = 0; len < sizeof(buf); len++) {
00230         if (_state[id].buf->dequeue(&buf[len]) == false) break;
00231     }
00232     buf[len] = 0;
00233 
00234     if (strnicmp(buf, "GET ", 4) == 0) {
00235         _state[id].req = REQ_HTTPGET;
00236         j = 4;
00237     } else
00238     if (strnicmp(buf, "POST ", 5) == 0) {
00239         _state[id].req = REQ_HTTPPOST;
00240         j = 5;
00241     } else {
00242         return -1;
00243     }
00244 
00245     for (i = 0; i < len - j; i ++) {
00246         _state[id].uri[i] = buf[i + j];
00247         if (buf[i + j] == ' ' || i >= sizeof(buf) - 1) {
00248             _state[id].uri[i] = 0;
00249             INFO("URI %d '%s'", _state[id].req, _state[id].uri);
00250             _state[id].mode = MODE_HEADER;
00251             _state[id].buf->flush();
00252             _state[id].length = 0;
00253             _state[id].n = 0;
00254             _state[id].websocket = 0;
00255             _state[id].filename = NULL;
00256             _state[id].querystring = NULL;
00257             break;
00258         }
00259     }
00260 
00261     i = getHandler(_state[id].uri);
00262     if (i >= 0) { 
00263         _state[id].filename = &_state[id].uri[strlen(_handler[i].uri)];
00264         for (i = 0; i < strlen(_state[id].filename); i ++) {
00265             if (_state[id].filename[i] == '?') {
00266                 _state[id].filename[i] = 0;
00267                 _state[id].querystring = _state[id].filename + i + 1;
00268                 break;
00269             }
00270         }
00271         INFO("FILE '%s' QUERY '%s'", _state[id].filename, _state[id].querystring);
00272     }
00273     return 0;
00274 }
00275 
00276 #define HEADER_TABLE_NUM 5
00277 int HTTPD::parseHeader (int id) {
00278     int i;
00279     char buf[HTTPD_CMD_SIZE];
00280     static const struct HEADER_TABLE {
00281         const char header[24];
00282         void (HTTPD::*func)(int id, const char*);
00283     } header_table[HEADER_TABLE_NUM] = {
00284       {"Content-Length:",         &HTTPD::reqContentLength},
00285       {"Connection:",             &HTTPD::reqConnection},
00286       {"Upgrade: websocket",      &HTTPD::reqUpgrade},
00287       {"Sec-WebSocket-Version:",  &HTTPD::reqWebSocketVersion},
00288       {"Sec-WebSocket-Key:",      &HTTPD::reqWebSocketKey},
00289     };
00290     for (i = 0; i < sizeof(buf); i++) {
00291         if (_state[id].buf->dequeue(&buf[i]) == false) break;
00292     }
00293     buf[i] = 0;
00294 
00295     for (i = 0; i < HEADER_TABLE_NUM; i ++) {
00296         if (strnicmp(buf, header_table[i].header, strlen(header_table[i].header)) == 0) {
00297             DBG("parse header %d '%s'\r\n", i, buf);
00298             if (header_table[i].func != NULL) {
00299                 (this->*(header_table[i].func))(id, buf);
00300             }
00301             return 0;
00302         }
00303     }
00304 
00305     return -1;
00306 }
00307 
00308 void HTTPD::reqContentLength (int id, const char *buf) {
00309     _state[id].length = atoi(&buf[16]);
00310 }
00311 
00312 void HTTPD::reqConnection (int id, const char *buf) {
00313     if (strnicmp(&buf[12], "Keep-Alive", 10) == 0 && _state[id].keepalive == 0) {
00314         _state[id].keepalive = HTTPD_KEEPALIVE;
00315     } else {
00316         _state[id].keepalive = 0;
00317     }
00318 }
00319 
00320 void HTTPD::reqUpgrade (int id, const char *buf) {
00321     if (! _state[id].websocket) _state[id].websocket = 1;
00322 }
00323 
00324 void HTTPD::reqWebSocketVersion (int id, const char *buf) {
00325     _state[id].websocket = atoi(&buf[23]);
00326 }
00327 
00328 void HTTPD::reqWebSocketKey (int id, const char *buf) {
00329     if (_state[id].websocket_key == NULL) {
00330         _state[id].websocket_key = (char*)malloc(30);
00331     }
00332     strncpy(_state[id].websocket_key, &buf[19], 30);
00333 }