Parsing.cpp 23 KB

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