Parsing.cpp 19 KB

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