Parsing.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. /*
  2. Parsing.cpp - HTTP request parsing.
  3. Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with this library; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  15. Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
  16. */
  17. #include <Arduino.h>
  18. #include "WiFiServer.h"
  19. #include "WiFiClient.h"
  20. #include "ESP8266WebServer.h"
  21. //#define DEBUG_ESP_HTTP_SERVER
  22. #ifdef DEBUG_ESP_PORT
  23. #define DEBUG_OUTPUT DEBUG_ESP_PORT
  24. #else
  25. #define DEBUG_OUTPUT Serial
  26. #endif
  27. static char* readBytesWithTimeout(WiFiClient& client, size_t maxLength, size_t& dataLength, int timeout_ms)
  28. {
  29. char *buf = nullptr;
  30. dataLength = 0;
  31. while (dataLength < maxLength) {
  32. int tries = timeout_ms;
  33. size_t newLength;
  34. while (!(newLength = client.available()) && tries--) delay(1);
  35. if (!newLength) {
  36. break;
  37. }
  38. if (!buf) {
  39. buf = (char *) malloc(newLength + 1);
  40. if (!buf) {
  41. return nullptr;
  42. }
  43. }
  44. else {
  45. char* newBuf = (char *) realloc(buf, dataLength + newLength + 1);
  46. if (!newBuf) {
  47. free(buf);
  48. return nullptr;
  49. }
  50. buf = newBuf;
  51. }
  52. client.readBytes(buf + dataLength, newLength);
  53. dataLength += newLength;
  54. buf[dataLength] = '\0';
  55. }
  56. return buf;
  57. }
  58. bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
  59. // Read the first line of HTTP request
  60. String req = client.readStringUntil('\r');
  61. client.readStringUntil('\n');
  62. //reset header value
  63. for (int i = 0; i < _headerKeysCount; ++i) {
  64. _currentHeaders[i].value =String();
  65. }
  66. // First line of HTTP request looks like "GET /path HTTP/1.1"
  67. // Retrieve the "/path" part by finding the spaces
  68. int addr_start = req.indexOf(' ');
  69. int addr_end = req.indexOf(' ', addr_start + 1);
  70. if (addr_start == -1 || addr_end == -1) {
  71. #ifdef DEBUG_ESP_HTTP_SERVER
  72. DEBUG_OUTPUT.print("Invalid request: ");
  73. DEBUG_OUTPUT.println(req);
  74. #endif
  75. return false;
  76. }
  77. String methodStr = req.substring(0, addr_start);
  78. String url = req.substring(addr_start + 1, addr_end);
  79. String searchStr = "";
  80. int hasSearch = url.indexOf('?');
  81. if (hasSearch != -1){
  82. searchStr = url.substring(hasSearch + 1);
  83. url = url.substring(0, hasSearch);
  84. }
  85. _currentUri = url;
  86. HTTPMethod method = HTTP_GET;
  87. if (methodStr == "POST") {
  88. method = HTTP_POST;
  89. } else if (methodStr == "DELETE") {
  90. method = HTTP_DELETE;
  91. } else if (methodStr == "OPTIONS") {
  92. method = HTTP_OPTIONS;
  93. } else if (methodStr == "PUT") {
  94. method = HTTP_PUT;
  95. } else if (methodStr == "PATCH") {
  96. method = HTTP_PATCH;
  97. }
  98. _currentMethod = method;
  99. #ifdef DEBUG_ESP_HTTP_SERVER
  100. DEBUG_OUTPUT.print("method: ");
  101. DEBUG_OUTPUT.print(methodStr);
  102. DEBUG_OUTPUT.print(" url: ");
  103. DEBUG_OUTPUT.print(url);
  104. DEBUG_OUTPUT.print(" search: ");
  105. DEBUG_OUTPUT.println(searchStr);
  106. #endif
  107. //attach handler
  108. RequestHandler* handler;
  109. for (handler = _firstHandler; handler; handler = handler->next()) {
  110. if (handler->canHandle(_currentMethod, _currentUri))
  111. break;
  112. }
  113. _currentHandler = handler;
  114. String formData;
  115. // below is needed only when POST type request
  116. if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){
  117. String boundaryStr;
  118. String headerName;
  119. String headerValue;
  120. bool isForm = false;
  121. uint32_t contentLength = 0;
  122. //parse headers
  123. while(1){
  124. req = client.readStringUntil('\r');
  125. client.readStringUntil('\n');
  126. if (req == "") break;//no moar headers
  127. int headerDiv = req.indexOf(':');
  128. if (headerDiv == -1){
  129. break;
  130. }
  131. headerName = req.substring(0, headerDiv);
  132. headerValue = req.substring(headerDiv + 1);
  133. headerValue.trim();
  134. _collectHeader(headerName.c_str(),headerValue.c_str());
  135. #ifdef DEBUG_ESP_HTTP_SERVER
  136. DEBUG_OUTPUT.print("headerName: ");
  137. DEBUG_OUTPUT.println(headerName);
  138. DEBUG_OUTPUT.print("headerValue: ");
  139. DEBUG_OUTPUT.println(headerValue);
  140. #endif
  141. if (headerName == "Content-Type"){
  142. if (headerValue.startsWith("text/plain")){
  143. isForm = false;
  144. } else if (headerValue.startsWith("multipart/form-data")){
  145. boundaryStr = headerValue.substring(headerValue.indexOf('=')+1);
  146. isForm = true;
  147. }
  148. } else if (headerName == "Content-Length"){
  149. contentLength = headerValue.toInt();
  150. } else if (headerName == "Host"){
  151. _hostHeader = headerValue;
  152. }
  153. }
  154. if (!isForm){
  155. size_t plainLength;
  156. char* plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT);
  157. if (plainLength < contentLength) {
  158. free(plainBuf);
  159. return false;
  160. }
  161. #ifdef DEBUG_ESP_HTTP_SERVER
  162. DEBUG_OUTPUT.print("Plain: ");
  163. DEBUG_OUTPUT.println(plainBuf);
  164. #endif
  165. if (contentLength > 0) {
  166. if (searchStr != "") searchStr += '&';
  167. if(plainBuf[0] == '{' || plainBuf[0] == '[' || strstr(plainBuf, "=") == NULL){
  168. //plain post json or other data
  169. searchStr += "plain=";
  170. searchStr += plainBuf;
  171. } else {
  172. searchStr += plainBuf;
  173. }
  174. free(plainBuf);
  175. }
  176. }
  177. _parseArguments(searchStr);
  178. if (isForm){
  179. if (!_parseForm(client, boundaryStr, contentLength)) {
  180. return false;
  181. }
  182. }
  183. } else {
  184. String headerName;
  185. String headerValue;
  186. //parse headers
  187. while(1){
  188. req = client.readStringUntil('\r');
  189. client.readStringUntil('\n');
  190. if (req == "") break;//no moar headers
  191. int headerDiv = req.indexOf(':');
  192. if (headerDiv == -1){
  193. break;
  194. }
  195. headerName = req.substring(0, headerDiv);
  196. headerValue = req.substring(headerDiv + 2);
  197. _collectHeader(headerName.c_str(),headerValue.c_str());
  198. #ifdef DEBUG_ESP_HTTP_SERVER
  199. DEBUG_OUTPUT.print("headerName: ");
  200. DEBUG_OUTPUT.println(headerName);
  201. DEBUG_OUTPUT.print("headerValue: ");
  202. DEBUG_OUTPUT.println(headerValue);
  203. #endif
  204. if (headerName == "Host"){
  205. _hostHeader = headerValue;
  206. }
  207. }
  208. _parseArguments(searchStr);
  209. }
  210. client.flush();
  211. #ifdef DEBUG_ESP_HTTP_SERVER
  212. DEBUG_OUTPUT.print("Request: ");
  213. DEBUG_OUTPUT.println(url);
  214. DEBUG_OUTPUT.print(" Arguments: ");
  215. DEBUG_OUTPUT.println(searchStr);
  216. #endif
  217. return true;
  218. }
  219. bool ESP8266WebServer::_collectHeader(const char* headerName, const char* headerValue) {
  220. for (int i = 0; i < _headerKeysCount; i++) {
  221. if (_currentHeaders[i].key==headerName) {
  222. _currentHeaders[i].value=headerValue;
  223. return true;
  224. }
  225. }
  226. return false;
  227. }
  228. void ESP8266WebServer::_parseArguments(String data) {
  229. #ifdef DEBUG_ESP_HTTP_SERVER
  230. DEBUG_OUTPUT.print("args: ");
  231. DEBUG_OUTPUT.println(data);
  232. #endif
  233. if (_currentArgs)
  234. delete[] _currentArgs;
  235. _currentArgs = 0;
  236. if (data.length() == 0) {
  237. _currentArgCount = 0;
  238. return;
  239. }
  240. _currentArgCount = 1;
  241. for (int i = 0; i < (int)data.length(); ) {
  242. i = data.indexOf('&', i);
  243. if (i == -1)
  244. break;
  245. ++i;
  246. ++_currentArgCount;
  247. }
  248. #ifdef DEBUG_ESP_HTTP_SERVER
  249. DEBUG_OUTPUT.print("args count: ");
  250. DEBUG_OUTPUT.println(_currentArgCount);
  251. #endif
  252. _currentArgs = new RequestArgument[_currentArgCount];
  253. int pos = 0;
  254. int iarg;
  255. for (iarg = 0; iarg < _currentArgCount;) {
  256. int equal_sign_index = data.indexOf('=', pos);
  257. int next_arg_index = data.indexOf('&', pos);
  258. #ifdef DEBUG_ESP_HTTP_SERVER
  259. DEBUG_OUTPUT.print("pos ");
  260. DEBUG_OUTPUT.print(pos);
  261. DEBUG_OUTPUT.print("=@ ");
  262. DEBUG_OUTPUT.print(equal_sign_index);
  263. DEBUG_OUTPUT.print(" &@ ");
  264. DEBUG_OUTPUT.println(next_arg_index);
  265. #endif
  266. if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) {
  267. #ifdef DEBUG_ESP_HTTP_SERVER
  268. DEBUG_OUTPUT.print("arg missing value: ");
  269. DEBUG_OUTPUT.println(iarg);
  270. #endif
  271. if (next_arg_index == -1)
  272. break;
  273. pos = next_arg_index + 1;
  274. continue;
  275. }
  276. RequestArgument& arg = _currentArgs[iarg];
  277. arg.key = data.substring(pos, equal_sign_index);
  278. arg.value = urlDecode(data.substring(equal_sign_index + 1, next_arg_index));
  279. #ifdef DEBUG_ESP_HTTP_SERVER
  280. DEBUG_OUTPUT.print("arg ");
  281. DEBUG_OUTPUT.print(iarg);
  282. DEBUG_OUTPUT.print(" key: ");
  283. DEBUG_OUTPUT.print(arg.key);
  284. DEBUG_OUTPUT.print(" value: ");
  285. DEBUG_OUTPUT.println(arg.value);
  286. #endif
  287. ++iarg;
  288. if (next_arg_index == -1)
  289. break;
  290. pos = next_arg_index + 1;
  291. }
  292. _currentArgCount = iarg;
  293. #ifdef DEBUG_ESP_HTTP_SERVER
  294. DEBUG_OUTPUT.print("args count: ");
  295. DEBUG_OUTPUT.println(_currentArgCount);
  296. #endif
  297. }
  298. void ESP8266WebServer::_uploadWriteByte(uint8_t b){
  299. if (_currentUpload.currentSize == HTTP_UPLOAD_BUFLEN){
  300. if(_currentHandler && _currentHandler->canUpload(_currentUri))
  301. _currentHandler->upload(*this, _currentUri, _currentUpload);
  302. _currentUpload.totalSize += _currentUpload.currentSize;
  303. _currentUpload.currentSize = 0;
  304. }
  305. _currentUpload.buf[_currentUpload.currentSize++] = b;
  306. }
  307. uint8_t ESP8266WebServer::_uploadReadByte(WiFiClient& client){
  308. int res = client.read();
  309. if(res == -1){
  310. while(!client.available() && client.connected())
  311. yield();
  312. res = client.read();
  313. }
  314. return (uint8_t)res;
  315. }
  316. bool ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){
  317. #ifdef DEBUG_ESP_HTTP_SERVER
  318. DEBUG_OUTPUT.print("Parse Form: Boundary: ");
  319. DEBUG_OUTPUT.print(boundary);
  320. DEBUG_OUTPUT.print(" Length: ");
  321. DEBUG_OUTPUT.println(len);
  322. #endif
  323. String line;
  324. int retry = 0;
  325. do {
  326. line = client.readStringUntil('\r');
  327. ++retry;
  328. } while (line.length() == 0 && retry < 3);
  329. client.readStringUntil('\n');
  330. //start reading the form
  331. if (line == ("--"+boundary)){
  332. RequestArgument* postArgs = new RequestArgument[32];
  333. int postArgsLen = 0;
  334. while(1){
  335. String argName;
  336. String argValue;
  337. String argType;
  338. String argFilename;
  339. bool argIsFile = false;
  340. line = client.readStringUntil('\r');
  341. client.readStringUntil('\n');
  342. if (line.startsWith("Content-Disposition")){
  343. int nameStart = line.indexOf('=');
  344. if (nameStart != -1){
  345. argName = line.substring(nameStart+2);
  346. nameStart = argName.indexOf('=');
  347. if (nameStart == -1){
  348. argName = argName.substring(0, argName.length() - 1);
  349. } else {
  350. argFilename = argName.substring(nameStart+2, argName.length() - 1);
  351. argName = argName.substring(0, argName.indexOf('"'));
  352. argIsFile = true;
  353. #ifdef DEBUG_ESP_HTTP_SERVER
  354. DEBUG_OUTPUT.print("PostArg FileName: ");
  355. DEBUG_OUTPUT.println(argFilename);
  356. #endif
  357. //use GET to set the filename if uploading using blob
  358. if (argFilename == "blob" && hasArg("filename")) argFilename = arg("filename");
  359. }
  360. #ifdef DEBUG_ESP_HTTP_SERVER
  361. DEBUG_OUTPUT.print("PostArg Name: ");
  362. DEBUG_OUTPUT.println(argName);
  363. #endif
  364. argType = "text/plain";
  365. line = client.readStringUntil('\r');
  366. client.readStringUntil('\n');
  367. if (line.startsWith("Content-Type")){
  368. argType = line.substring(line.indexOf(':')+2);
  369. //skip next line
  370. client.readStringUntil('\r');
  371. client.readStringUntil('\n');
  372. }
  373. #ifdef DEBUG_ESP_HTTP_SERVER
  374. DEBUG_OUTPUT.print("PostArg Type: ");
  375. DEBUG_OUTPUT.println(argType);
  376. #endif
  377. if (!argIsFile){
  378. while(1){
  379. line = client.readStringUntil('\r');
  380. client.readStringUntil('\n');
  381. if (line.startsWith("--"+boundary)) break;
  382. if (argValue.length() > 0) argValue += "\n";
  383. argValue += line;
  384. }
  385. #ifdef DEBUG_ESP_HTTP_SERVER
  386. DEBUG_OUTPUT.print("PostArg Value: ");
  387. DEBUG_OUTPUT.println(argValue);
  388. DEBUG_OUTPUT.println();
  389. #endif
  390. RequestArgument& arg = postArgs[postArgsLen++];
  391. arg.key = argName;
  392. arg.value = argValue;
  393. if (line == ("--"+boundary+"--")){
  394. #ifdef DEBUG_ESP_HTTP_SERVER
  395. DEBUG_OUTPUT.println("Done Parsing POST");
  396. #endif
  397. break;
  398. }
  399. } else {
  400. _currentUpload.status = UPLOAD_FILE_START;
  401. _currentUpload.name = argName;
  402. _currentUpload.filename = argFilename;
  403. _currentUpload.type = argType;
  404. _currentUpload.totalSize = 0;
  405. _currentUpload.currentSize = 0;
  406. #ifdef DEBUG_ESP_HTTP_SERVER
  407. DEBUG_OUTPUT.print("Start File: ");
  408. DEBUG_OUTPUT.print(_currentUpload.filename);
  409. DEBUG_OUTPUT.print(" Type: ");
  410. DEBUG_OUTPUT.println(_currentUpload.type);
  411. #endif
  412. if(_currentHandler && _currentHandler->canUpload(_currentUri))
  413. _currentHandler->upload(*this, _currentUri, _currentUpload);
  414. _currentUpload.status = UPLOAD_FILE_WRITE;
  415. uint8_t argByte = _uploadReadByte(client);
  416. readfile:
  417. while(argByte != 0x0D){
  418. if (!client.connected()) return _parseFormUploadAborted();
  419. _uploadWriteByte(argByte);
  420. argByte = _uploadReadByte(client);
  421. }
  422. argByte = _uploadReadByte(client);
  423. if (!client.connected()) return _parseFormUploadAborted();
  424. if (argByte == 0x0A){
  425. argByte = _uploadReadByte(client);
  426. if (!client.connected()) return _parseFormUploadAborted();
  427. if ((char)argByte != '-'){
  428. //continue reading the file
  429. _uploadWriteByte(0x0D);
  430. _uploadWriteByte(0x0A);
  431. goto readfile;
  432. } else {
  433. argByte = _uploadReadByte(client);
  434. if (!client.connected()) return _parseFormUploadAborted();
  435. if ((char)argByte != '-'){
  436. //continue reading the file
  437. _uploadWriteByte(0x0D);
  438. _uploadWriteByte(0x0A);
  439. _uploadWriteByte((uint8_t)('-'));
  440. goto readfile;
  441. }
  442. }
  443. uint8_t endBuf[boundary.length()];
  444. client.readBytes(endBuf, boundary.length());
  445. if (strstr((const char*)endBuf, boundary.c_str()) != NULL){
  446. if(_currentHandler && _currentHandler->canUpload(_currentUri))
  447. _currentHandler->upload(*this, _currentUri, _currentUpload);
  448. _currentUpload.totalSize += _currentUpload.currentSize;
  449. _currentUpload.status = UPLOAD_FILE_END;
  450. if(_currentHandler && _currentHandler->canUpload(_currentUri))
  451. _currentHandler->upload(*this, _currentUri, _currentUpload);
  452. #ifdef DEBUG_ESP_HTTP_SERVER
  453. DEBUG_OUTPUT.print("End File: ");
  454. DEBUG_OUTPUT.print(_currentUpload.filename);
  455. DEBUG_OUTPUT.print(" Type: ");
  456. DEBUG_OUTPUT.print(_currentUpload.type);
  457. DEBUG_OUTPUT.print(" Size: ");
  458. DEBUG_OUTPUT.println(_currentUpload.totalSize);
  459. #endif
  460. line = client.readStringUntil(0x0D);
  461. client.readStringUntil(0x0A);
  462. if (line == "--"){
  463. #ifdef DEBUG_ESP_HTTP_SERVER
  464. DEBUG_OUTPUT.println("Done Parsing POST");
  465. #endif
  466. break;
  467. }
  468. continue;
  469. } else {
  470. _uploadWriteByte(0x0D);
  471. _uploadWriteByte(0x0A);
  472. _uploadWriteByte((uint8_t)('-'));
  473. _uploadWriteByte((uint8_t)('-'));
  474. uint32_t i = 0;
  475. while(i < boundary.length()){
  476. _uploadWriteByte(endBuf[i++]);
  477. }
  478. argByte = _uploadReadByte(client);
  479. goto readfile;
  480. }
  481. } else {
  482. _uploadWriteByte(0x0D);
  483. goto readfile;
  484. }
  485. break;
  486. }
  487. }
  488. }
  489. }
  490. int iarg;
  491. int totalArgs = ((32 - postArgsLen) < _currentArgCount)?(32 - postArgsLen):_currentArgCount;
  492. for (iarg = 0; iarg < totalArgs; iarg++){
  493. RequestArgument& arg = postArgs[postArgsLen++];
  494. arg.key = _currentArgs[iarg].key;
  495. arg.value = _currentArgs[iarg].value;
  496. }
  497. if (_currentArgs) delete[] _currentArgs;
  498. _currentArgs = new RequestArgument[postArgsLen];
  499. for (iarg = 0; iarg < postArgsLen; iarg++){
  500. RequestArgument& arg = _currentArgs[iarg];
  501. arg.key = postArgs[iarg].key;
  502. arg.value = postArgs[iarg].value;
  503. }
  504. _currentArgCount = iarg;
  505. if (postArgs) delete[] postArgs;
  506. return true;
  507. }
  508. #ifdef DEBUG_ESP_HTTP_SERVER
  509. DEBUG_OUTPUT.print("Error: line: ");
  510. DEBUG_OUTPUT.println(line);
  511. #endif
  512. return false;
  513. }
  514. String ESP8266WebServer::urlDecode(const String& text)
  515. {
  516. String decoded = "";
  517. char temp[] = "0x00";
  518. unsigned int len = text.length();
  519. unsigned int i = 0;
  520. while (i < len)
  521. {
  522. char decodedChar;
  523. char encodedChar = text.charAt(i++);
  524. if ((encodedChar == '%') && (i + 1 < len))
  525. {
  526. temp[2] = text.charAt(i++);
  527. temp[3] = text.charAt(i++);
  528. decodedChar = strtol(temp, NULL, 16);
  529. }
  530. else {
  531. if (encodedChar == '+')
  532. {
  533. decodedChar = ' ';
  534. }
  535. else {
  536. decodedChar = encodedChar; // normal ascii char
  537. }
  538. }
  539. decoded += decodedChar;
  540. }
  541. return decoded;
  542. }
  543. bool ESP8266WebServer::_parseFormUploadAborted(){
  544. _currentUpload.status = UPLOAD_FILE_ABORTED;
  545. if(_currentHandler && _currentHandler->canUpload(_currentUri))
  546. _currentHandler->upload(*this, _currentUri, _currentUpload);
  547. return false;
  548. }