ESP8266WebServer.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. /*
  2. ESP8266WebServer.cpp - Dead simple web-server.
  3. Supports only one simultaneous client, knows how to handle GET and POST.
  4. Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
  5. This library is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU Lesser General Public
  7. License as published by the Free Software Foundation; either
  8. version 2.1 of the License, or (at your option) any later version.
  9. This library is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. Lesser General Public License for more details.
  13. You should have received a copy of the GNU Lesser General Public
  14. License along with this library; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
  17. */
  18. #include <Arduino.h>
  19. #include <libb64/cencode.h>
  20. #include "WiFiServer.h"
  21. #include "WiFiClient.h"
  22. #include "ESP8266WebServer.h"
  23. #include "FS.h"
  24. #include "detail/RequestHandlersImpl.h"
  25. //#define DEBUG_ESP_HTTP_SERVER
  26. #ifdef DEBUG_ESP_PORT
  27. #define DEBUG_OUTPUT DEBUG_ESP_PORT
  28. #else
  29. #define DEBUG_OUTPUT Serial
  30. #endif
  31. const char * AUTHORIZATION_HEADER = "Authorization";
  32. ESP8266WebServer::ESP8266WebServer(IPAddress addr, int port)
  33. : _server(addr, port)
  34. , _currentMethod(HTTP_ANY)
  35. , _currentHandler(0)
  36. , _firstHandler(0)
  37. , _lastHandler(0)
  38. , _currentArgCount(0)
  39. , _currentArgs(0)
  40. , _headerKeysCount(0)
  41. , _currentHeaders(0)
  42. , _contentLength(0)
  43. {
  44. }
  45. ESP8266WebServer::ESP8266WebServer(int port)
  46. : _server(port)
  47. , _currentMethod(HTTP_ANY)
  48. , _currentHandler(0)
  49. , _firstHandler(0)
  50. , _lastHandler(0)
  51. , _currentArgCount(0)
  52. , _currentArgs(0)
  53. , _headerKeysCount(0)
  54. , _currentHeaders(0)
  55. , _contentLength(0)
  56. {
  57. }
  58. ESP8266WebServer::~ESP8266WebServer() {
  59. if (_currentHeaders)
  60. delete[]_currentHeaders;
  61. _headerKeysCount = 0;
  62. RequestHandler* handler = _firstHandler;
  63. while (handler) {
  64. RequestHandler* next = handler->next();
  65. delete handler;
  66. handler = next;
  67. }
  68. close();
  69. }
  70. void ESP8266WebServer::begin() {
  71. _currentStatus = HC_NONE;
  72. _server.begin();
  73. if(!_headerKeysCount)
  74. collectHeaders(0, 0);
  75. }
  76. bool ESP8266WebServer::authenticate(const char * username, const char * password){
  77. if(hasHeader(AUTHORIZATION_HEADER)){
  78. String authReq = header(AUTHORIZATION_HEADER);
  79. if(authReq.startsWith("Basic")){
  80. authReq = authReq.substring(6);
  81. authReq.trim();
  82. char toencodeLen = strlen(username)+strlen(password)+1;
  83. char *toencode = new char[toencodeLen + 1];
  84. if(toencode == NULL){
  85. authReq = String();
  86. return false;
  87. }
  88. char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
  89. if(encoded == NULL){
  90. authReq = String();
  91. delete[] toencode;
  92. return false;
  93. }
  94. sprintf(toencode, "%s:%s", username, password);
  95. if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equals(encoded)){
  96. authReq = String();
  97. delete[] toencode;
  98. delete[] encoded;
  99. return true;
  100. }
  101. delete[] toencode;
  102. delete[] encoded;
  103. }
  104. authReq = String();
  105. }
  106. return false;
  107. }
  108. void ESP8266WebServer::requestAuthentication(){
  109. sendHeader("WWW-Authenticate", "Basic realm=\"Login Required\"");
  110. send(401);
  111. }
  112. void ESP8266WebServer::on(const char* uri, ESP8266WebServer::THandlerFunction handler) {
  113. on(uri, HTTP_ANY, handler);
  114. }
  115. void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn) {
  116. on(uri, method, fn, _fileUploadHandler);
  117. }
  118. void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn, ESP8266WebServer::THandlerFunction ufn) {
  119. _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method));
  120. }
  121. void ESP8266WebServer::addHandler(RequestHandler* handler) {
  122. _addRequestHandler(handler);
  123. }
  124. void ESP8266WebServer::_addRequestHandler(RequestHandler* handler) {
  125. if (!_lastHandler) {
  126. _firstHandler = handler;
  127. _lastHandler = handler;
  128. }
  129. else {
  130. _lastHandler->next(handler);
  131. _lastHandler = handler;
  132. }
  133. }
  134. void ESP8266WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) {
  135. _addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header));
  136. }
  137. void ESP8266WebServer::handleClient() {
  138. if (_currentStatus == HC_NONE) {
  139. WiFiClient client = _server.available();
  140. if (!client) {
  141. return;
  142. }
  143. #ifdef DEBUG_ESP_HTTP_SERVER
  144. DEBUG_OUTPUT.println("New client");
  145. #endif
  146. _currentClient = client;
  147. _currentStatus = HC_WAIT_READ;
  148. _statusChange = millis();
  149. }
  150. if (!_currentClient.connected()) {
  151. _currentClient = WiFiClient();
  152. _currentStatus = HC_NONE;
  153. return;
  154. }
  155. // Wait for data from client to become available
  156. if (_currentStatus == HC_WAIT_READ) {
  157. if (!_currentClient.available()) {
  158. if (millis() - _statusChange > HTTP_MAX_DATA_WAIT) {
  159. _currentClient = WiFiClient();
  160. _currentStatus = HC_NONE;
  161. }
  162. yield();
  163. return;
  164. }
  165. if (!_parseRequest(_currentClient)) {
  166. _currentClient = WiFiClient();
  167. _currentStatus = HC_NONE;
  168. return;
  169. }
  170. _contentLength = CONTENT_LENGTH_NOT_SET;
  171. _handleRequest();
  172. if (!_currentClient.connected()) {
  173. _currentClient = WiFiClient();
  174. _currentStatus = HC_NONE;
  175. return;
  176. } else {
  177. _currentStatus = HC_WAIT_CLOSE;
  178. _statusChange = millis();
  179. return;
  180. }
  181. }
  182. if (_currentStatus == HC_WAIT_CLOSE) {
  183. if (millis() - _statusChange > HTTP_MAX_CLOSE_WAIT) {
  184. _currentClient = WiFiClient();
  185. _currentStatus = HC_NONE;
  186. } else {
  187. yield();
  188. return;
  189. }
  190. }
  191. }
  192. void ESP8266WebServer::close() {
  193. _server.close();
  194. }
  195. void ESP8266WebServer::stop() {
  196. close();
  197. }
  198. void ESP8266WebServer::sendHeader(const String& name, const String& value, bool first) {
  199. String headerLine = name;
  200. headerLine += ": ";
  201. headerLine += value;
  202. headerLine += "\r\n";
  203. if (first) {
  204. _responseHeaders = headerLine + _responseHeaders;
  205. }
  206. else {
  207. _responseHeaders += headerLine;
  208. }
  209. }
  210. void ESP8266WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) {
  211. response = "HTTP/1.1 ";
  212. response += String(code);
  213. response += " ";
  214. response += _responseCodeToString(code);
  215. response += "\r\n";
  216. if (!content_type)
  217. content_type = "text/html";
  218. sendHeader("Content-Type", content_type, true);
  219. if (_contentLength == CONTENT_LENGTH_NOT_SET) {
  220. sendHeader("Content-Length", String(contentLength));
  221. } else if (_contentLength != CONTENT_LENGTH_UNKNOWN) {
  222. sendHeader("Content-Length", String(_contentLength));
  223. }
  224. sendHeader("Connection", "close");
  225. sendHeader("Access-Control-Allow-Origin", "*");
  226. response += _responseHeaders;
  227. response += "\r\n";
  228. _responseHeaders = String();
  229. }
  230. void ESP8266WebServer::send(int code, const char* content_type, const String& content) {
  231. String header;
  232. _prepareHeader(header, code, content_type, content.length());
  233. sendContent(header);
  234. sendContent(content);
  235. }
  236. void ESP8266WebServer::send_P(int code, PGM_P content_type, PGM_P content) {
  237. size_t contentLength = 0;
  238. if (content != NULL) {
  239. contentLength = strlen_P(content);
  240. }
  241. String header;
  242. char type[64];
  243. memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
  244. _prepareHeader(header, code, (const char* )type, contentLength);
  245. sendContent(header);
  246. sendContent_P(content);
  247. }
  248. void ESP8266WebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) {
  249. String header;
  250. char type[64];
  251. memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
  252. _prepareHeader(header, code, (const char* )type, contentLength);
  253. sendContent(header);
  254. sendContent_P(content, contentLength);
  255. }
  256. void ESP8266WebServer::send(int code, char* content_type, const String& content) {
  257. send(code, (const char*)content_type, content);
  258. }
  259. void ESP8266WebServer::send(int code, const String& content_type, const String& content) {
  260. send(code, (const char*)content_type.c_str(), content);
  261. }
  262. void ESP8266WebServer::sendContent(const String& content) {
  263. const size_t unit_size = HTTP_DOWNLOAD_UNIT_SIZE;
  264. size_t size_to_send = content.length();
  265. const char* send_start = content.c_str();
  266. while (size_to_send) {
  267. size_t will_send = (size_to_send < unit_size) ? size_to_send : unit_size;
  268. size_t sent = _currentClient.write(send_start, will_send);
  269. if (sent == 0) {
  270. break;
  271. }
  272. size_to_send -= sent;
  273. send_start += sent;
  274. }
  275. }
  276. void ESP8266WebServer::sendContent_P(PGM_P content) {
  277. char contentUnit[HTTP_DOWNLOAD_UNIT_SIZE + 1];
  278. contentUnit[HTTP_DOWNLOAD_UNIT_SIZE] = '\0';
  279. while (content != NULL) {
  280. size_t contentUnitLen;
  281. PGM_P contentNext;
  282. // due to the memccpy signature, lots of casts are needed
  283. contentNext = (PGM_P)memccpy_P((void*)contentUnit, (PGM_VOID_P)content, 0, HTTP_DOWNLOAD_UNIT_SIZE);
  284. if (contentNext == NULL) {
  285. // no terminator, more data available
  286. content += HTTP_DOWNLOAD_UNIT_SIZE;
  287. contentUnitLen = HTTP_DOWNLOAD_UNIT_SIZE;
  288. }
  289. else {
  290. // reached terminator. Do not send the terminator
  291. contentUnitLen = contentNext - contentUnit - 1;
  292. content = NULL;
  293. }
  294. // write is so overloaded, had to use the cast to get it pick the right one
  295. _currentClient.write((const char*)contentUnit, contentUnitLen);
  296. }
  297. }
  298. void ESP8266WebServer::sendContent_P(PGM_P content, size_t size) {
  299. char contentUnit[HTTP_DOWNLOAD_UNIT_SIZE + 1];
  300. contentUnit[HTTP_DOWNLOAD_UNIT_SIZE] = '\0';
  301. size_t remaining_size = size;
  302. while (content != NULL && remaining_size > 0) {
  303. size_t contentUnitLen = HTTP_DOWNLOAD_UNIT_SIZE;
  304. if (remaining_size < HTTP_DOWNLOAD_UNIT_SIZE) contentUnitLen = remaining_size;
  305. // due to the memcpy signature, lots of casts are needed
  306. memcpy_P((void*)contentUnit, (PGM_VOID_P)content, contentUnitLen);
  307. content += contentUnitLen;
  308. remaining_size -= contentUnitLen;
  309. // write is so overloaded, had to use the cast to get it pick the right one
  310. _currentClient.write((const char*)contentUnit, contentUnitLen);
  311. }
  312. }
  313. String ESP8266WebServer::arg(String name) {
  314. for (int i = 0; i < _currentArgCount; ++i) {
  315. if ( _currentArgs[i].key == name )
  316. return _currentArgs[i].value;
  317. }
  318. return String();
  319. }
  320. String ESP8266WebServer::arg(int i) {
  321. if (i < _currentArgCount)
  322. return _currentArgs[i].value;
  323. return String();
  324. }
  325. String ESP8266WebServer::argName(int i) {
  326. if (i < _currentArgCount)
  327. return _currentArgs[i].key;
  328. return String();
  329. }
  330. int ESP8266WebServer::args() {
  331. return _currentArgCount;
  332. }
  333. bool ESP8266WebServer::hasArg(String name) {
  334. for (int i = 0; i < _currentArgCount; ++i) {
  335. if (_currentArgs[i].key == name)
  336. return true;
  337. }
  338. return false;
  339. }
  340. String ESP8266WebServer::header(String name) {
  341. for (int i = 0; i < _headerKeysCount; ++i) {
  342. if (_currentHeaders[i].key == name)
  343. return _currentHeaders[i].value;
  344. }
  345. return String();
  346. }
  347. void ESP8266WebServer::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) {
  348. _headerKeysCount = headerKeysCount + 1;
  349. if (_currentHeaders)
  350. delete[]_currentHeaders;
  351. _currentHeaders = new RequestArgument[_headerKeysCount];
  352. _currentHeaders[0].key = AUTHORIZATION_HEADER;
  353. for (int i = 1; i < _headerKeysCount; i++){
  354. _currentHeaders[i].key = headerKeys[i-1];
  355. }
  356. }
  357. String ESP8266WebServer::header(int i) {
  358. if (i < _headerKeysCount)
  359. return _currentHeaders[i].value;
  360. return String();
  361. }
  362. String ESP8266WebServer::headerName(int i) {
  363. if (i < _headerKeysCount)
  364. return _currentHeaders[i].key;
  365. return String();
  366. }
  367. int ESP8266WebServer::headers() {
  368. return _headerKeysCount;
  369. }
  370. bool ESP8266WebServer::hasHeader(String name) {
  371. for (int i = 0; i < _headerKeysCount; ++i) {
  372. if ((_currentHeaders[i].key == name) && (_currentHeaders[i].value.length() > 0))
  373. return true;
  374. }
  375. return false;
  376. }
  377. String ESP8266WebServer::hostHeader() {
  378. return _hostHeader;
  379. }
  380. void ESP8266WebServer::onFileUpload(THandlerFunction fn) {
  381. _fileUploadHandler = fn;
  382. }
  383. void ESP8266WebServer::onNotFound(THandlerFunction fn) {
  384. _notFoundHandler = fn;
  385. }
  386. void ESP8266WebServer::_handleRequest() {
  387. bool handled = false;
  388. if (!_currentHandler){
  389. #ifdef DEBUG_ESP_HTTP_SERVER
  390. DEBUG_OUTPUT.println("request handler not found");
  391. #endif
  392. }
  393. else {
  394. handled = _currentHandler->handle(*this, _currentMethod, _currentUri);
  395. #ifdef DEBUG_ESP_HTTP_SERVER
  396. if (!handled) {
  397. DEBUG_OUTPUT.println("request handler failed to handle request");
  398. }
  399. #endif
  400. }
  401. if (!handled) {
  402. if(_notFoundHandler) {
  403. _notFoundHandler();
  404. }
  405. else {
  406. send(404, "text/plain", String("Not found: ") + _currentUri);
  407. }
  408. }
  409. _currentUri = String();
  410. }
  411. String ESP8266WebServer::_responseCodeToString(int code) {
  412. switch (code) {
  413. case 100: return F("Continue");
  414. case 101: return F("Switching Protocols");
  415. case 200: return F("OK");
  416. case 201: return F("Created");
  417. case 202: return F("Accepted");
  418. case 203: return F("Non-Authoritative Information");
  419. case 204: return F("No Content");
  420. case 205: return F("Reset Content");
  421. case 206: return F("Partial Content");
  422. case 300: return F("Multiple Choices");
  423. case 301: return F("Moved Permanently");
  424. case 302: return F("Found");
  425. case 303: return F("See Other");
  426. case 304: return F("Not Modified");
  427. case 305: return F("Use Proxy");
  428. case 307: return F("Temporary Redirect");
  429. case 400: return F("Bad Request");
  430. case 401: return F("Unauthorized");
  431. case 402: return F("Payment Required");
  432. case 403: return F("Forbidden");
  433. case 404: return F("Not Found");
  434. case 405: return F("Method Not Allowed");
  435. case 406: return F("Not Acceptable");
  436. case 407: return F("Proxy Authentication Required");
  437. case 408: return F("Request Time-out");
  438. case 409: return F("Conflict");
  439. case 410: return F("Gone");
  440. case 411: return F("Length Required");
  441. case 412: return F("Precondition Failed");
  442. case 413: return F("Request Entity Too Large");
  443. case 414: return F("Request-URI Too Large");
  444. case 415: return F("Unsupported Media Type");
  445. case 416: return F("Requested range not satisfiable");
  446. case 417: return F("Expectation Failed");
  447. case 500: return F("Internal Server Error");
  448. case 501: return F("Not Implemented");
  449. case 502: return F("Bad Gateway");
  450. case 503: return F("Service Unavailable");
  451. case 504: return F("Gateway Time-out");
  452. case 505: return F("HTTP Version not supported");
  453. default: return "";
  454. }
  455. }