22#include "../../Defines.h"
23#ifdef USE_LIB_WEBSERVER
26#include "WiFiServer.h"
27#include "WiFiClient.h"
32#define DEBUG_OUTPUT DEBUG_ESP_PORT
34#define DEBUG_OUTPUT Serial
37static char* readBytesWithTimeout(WiFiClient& client,
size_t maxLength,
size_t& dataLength,
int timeout_ms)
41 while (dataLength < maxLength) {
42 int tries = timeout_ms;
44 while (!(newLength = client.available()) && tries--) delay(1);
49 buf = (
char *) malloc(newLength + 1);
55 char* newBuf = (
char *) realloc(buf, dataLength + newLength + 1);
62 client.readBytes(buf + dataLength, newLength);
63 dataLength += newLength;
64 buf[dataLength] =
'\0';
69bool WebServer::_parseRequest(WiFiClient& client) {
71 String req = client.readStringUntil(
'\r');
72 client.readStringUntil(
'\n');
74 for (
int i = 0; i < _headerKeysCount; ++i) {
75 _currentHeaders[i].value =String();
80 int addr_start = req.indexOf(
' ');
81 int addr_end = req.indexOf(
' ', addr_start + 1);
82 if (addr_start == -1 || addr_end == -1) {
83#ifdef DEBUG_ESP_HTTP_SERVER
84 DEBUG_OUTPUT.print(
"Invalid request: ");
85 DEBUG_OUTPUT.println(req);
90 String methodStr = req.substring(0, addr_start);
91 String url = req.substring(addr_start + 1, addr_end);
92 String versionEnd = req.substring(addr_end + 8);
93 _currentVersion = atoi(versionEnd.c_str());
94 String searchStr =
"";
95 int hasSearch = url.indexOf(
'?');
97 searchStr = urlDecode(url.substring(hasSearch + 1));
98 url = url.substring(0, hasSearch);
103 HTTPMethod method = HTTP_GET;
104 if (methodStr ==
"POST") {
106 }
else if (methodStr ==
"DELETE") {
107 method = HTTP_DELETE;
108 }
else if (methodStr ==
"OPTIONS") {
109 method = HTTP_OPTIONS;
110 }
else if (methodStr ==
"PUT") {
112 }
else if (methodStr ==
"PATCH") {
115 _currentMethod = method;
117#ifdef DEBUG_ESP_HTTP_SERVER
118 DEBUG_OUTPUT.print(
"method: ");
119 DEBUG_OUTPUT.print(methodStr);
120 DEBUG_OUTPUT.print(
" url: ");
121 DEBUG_OUTPUT.print(url);
122 DEBUG_OUTPUT.print(
" search: ");
123 DEBUG_OUTPUT.println(searchStr);
127 RequestHandler* handler;
128 for (handler = _firstHandler; handler; handler = handler->next()) {
129 if (handler->canHandle(_currentMethod, _currentUri))
132 _currentHandler = handler;
136 if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){
141 bool isEncoded =
false;
142 uint32_t contentLength = 0;
145 req = client.readStringUntil(
'\r');
146 client.readStringUntil(
'\n');
147 if (req ==
"")
break;
148 int headerDiv = req.indexOf(
':');
149 if (headerDiv == -1){
152 headerName = req.substring(0, headerDiv);
153 headerValue = req.substring(headerDiv + 1);
155 _collectHeader(headerName.c_str(),headerValue.c_str());
157 #ifdef DEBUG_ESP_HTTP_SERVER
158 DEBUG_OUTPUT.print(
"headerName: ");
159 DEBUG_OUTPUT.println(headerName);
160 DEBUG_OUTPUT.print(
"headerValue: ");
161 DEBUG_OUTPUT.println(headerValue);
164 if (headerName.equalsIgnoreCase(
"Content-Type")){
165 if (headerValue.startsWith(
"text/plain")){
167 }
else if (headerValue.startsWith(
"application/x-www-form-urlencoded")){
170 }
else if (headerValue.startsWith(
"multipart/")){
171 boundaryStr = headerValue.substring(headerValue.indexOf(
'=')+1);
174 }
else if (headerName.equalsIgnoreCase(
"Content-Length")){
175 contentLength = headerValue.toInt();
176 }
else if (headerName.equalsIgnoreCase(
"Host")){
177 _hostHeader = headerValue;
183 char* plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT);
184 if (plainLength < contentLength) {
188 if (contentLength > 0) {
189 if (searchStr !=
"") searchStr +=
'&';
192 String decoded = urlDecode(plainBuf);
193 size_t decodedLen = decoded.length();
194 memcpy(plainBuf, decoded.c_str(), decodedLen);
195 plainBuf[decodedLen] = 0;
196 searchStr += plainBuf;
198 _parseArguments(searchStr);
201 RequestArgument& arg = _currentArgs[_currentArgCount++];
203 arg.value = String(plainBuf);
206 #ifdef DEBUG_ESP_HTTP_SERVER
207 DEBUG_OUTPUT.print(
"Plain: ");
208 DEBUG_OUTPUT.println(plainBuf);
215 _parseArguments(searchStr);
216 if (!_parseForm(client, boundaryStr, contentLength)) {
225 req = client.readStringUntil(
'\r');
226 client.readStringUntil(
'\n');
227 if (req ==
"")
break;
228 int headerDiv = req.indexOf(
':');
229 if (headerDiv == -1){
232 headerName = req.substring(0, headerDiv);
233 headerValue = req.substring(headerDiv + 2);
234 _collectHeader(headerName.c_str(),headerValue.c_str());
236 #ifdef DEBUG_ESP_HTTP_SERVER
237 DEBUG_OUTPUT.print(
"headerName: ");
238 DEBUG_OUTPUT.println(headerName);
239 DEBUG_OUTPUT.print(
"headerValue: ");
240 DEBUG_OUTPUT.println(headerValue);
243 if (headerName.equalsIgnoreCase(
"Host")){
244 _hostHeader = headerValue;
247 _parseArguments(searchStr);
251#ifdef DEBUG_ESP_HTTP_SERVER
252 DEBUG_OUTPUT.print(
"Request: ");
253 DEBUG_OUTPUT.println(url);
254 DEBUG_OUTPUT.print(
" Arguments: ");
255 DEBUG_OUTPUT.println(searchStr);
261bool WebServer::_collectHeader(
const char* headerName,
const char* headerValue) {
262 for (
int i = 0; i < _headerKeysCount; i++) {
263 if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) {
264 _currentHeaders[i].value=headerValue;
271void WebServer::_parseArguments(String data) {
272#ifdef DEBUG_ESP_HTTP_SERVER
273 DEBUG_OUTPUT.print(
"args: ");
274 DEBUG_OUTPUT.println(data);
277 delete[] _currentArgs;
279 if (data.length() == 0) {
280 _currentArgCount = 0;
281 _currentArgs =
new RequestArgument[1];
284 _currentArgCount = 1;
286 for (
int i = 0; i < (int)data.length(); ) {
287 i = data.indexOf(
'&', i);
293#ifdef DEBUG_ESP_HTTP_SERVER
294 DEBUG_OUTPUT.print(
"args count: ");
295 DEBUG_OUTPUT.println(_currentArgCount);
298 _currentArgs =
new RequestArgument[_currentArgCount+1];
301 for (iarg = 0; iarg < _currentArgCount;) {
302 int equal_sign_index = data.indexOf(
'=', pos);
303 int next_arg_index = data.indexOf(
'&', pos);
304#ifdef DEBUG_ESP_HTTP_SERVER
305 DEBUG_OUTPUT.print(
"pos ");
306 DEBUG_OUTPUT.print(pos);
307 DEBUG_OUTPUT.print(
"=@ ");
308 DEBUG_OUTPUT.print(equal_sign_index);
309 DEBUG_OUTPUT.print(
" &@ ");
310 DEBUG_OUTPUT.println(next_arg_index);
312 if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) {
313#ifdef DEBUG_ESP_HTTP_SERVER
314 DEBUG_OUTPUT.print(
"arg missing value: ");
315 DEBUG_OUTPUT.println(iarg);
317 if (next_arg_index == -1)
319 pos = next_arg_index + 1;
322 RequestArgument& arg = _currentArgs[iarg];
323 arg.key = data.substring(pos, equal_sign_index);
324 arg.value = data.substring(equal_sign_index + 1, next_arg_index);
325#ifdef DEBUG_ESP_HTTP_SERVER
326 DEBUG_OUTPUT.print(
"arg ");
327 DEBUG_OUTPUT.print(iarg);
328 DEBUG_OUTPUT.print(
" key: ");
329 DEBUG_OUTPUT.print(arg.key);
330 DEBUG_OUTPUT.print(
" value: ");
331 DEBUG_OUTPUT.println(arg.value);
334 if (next_arg_index == -1)
336 pos = next_arg_index + 1;
338 _currentArgCount = iarg;
339#ifdef DEBUG_ESP_HTTP_SERVER
340 DEBUG_OUTPUT.print(
"args count: ");
341 DEBUG_OUTPUT.println(_currentArgCount);
346void WebServer::_uploadWriteByte(uint8_t b){
347 if (_currentUpload.currentSize == HTTP_UPLOAD_BUFLEN){
348 if(_currentHandler && _currentHandler->canUpload(_currentUri))
349 _currentHandler->upload(*
this, _currentUri, _currentUpload);
350 _currentUpload.totalSize += _currentUpload.currentSize;
351 _currentUpload.currentSize = 0;
353 _currentUpload.buf[_currentUpload.currentSize++] = b;
356uint8_t WebServer::_uploadReadByte(WiFiClient& client){
357 int res = client.read();
359 while(!client.available() && client.connected())
366bool WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){
368#ifdef DEBUG_ESP_HTTP_SERVER
369 DEBUG_OUTPUT.print(
"Parse Form: Boundary: ");
370 DEBUG_OUTPUT.print(boundary);
371 DEBUG_OUTPUT.print(
" Length: ");
372 DEBUG_OUTPUT.println(len);
377 line = client.readStringUntil(
'\r');
379 }
while (line.length() == 0 && retry < 3);
381 client.readStringUntil(
'\n');
383 if (line == (
"--"+boundary)){
384 RequestArgument* postArgs =
new RequestArgument[32];
391 bool argIsFile =
false;
393 line = client.readStringUntil(
'\r');
394 client.readStringUntil(
'\n');
395 if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(
"Content-Disposition")){
396 int nameStart = line.indexOf(
'=');
397 if (nameStart != -1){
398 argName = line.substring(nameStart+2);
399 nameStart = argName.indexOf(
'=');
400 if (nameStart == -1){
401 argName = argName.substring(0, argName.length() - 1);
403 argFilename = argName.substring(nameStart+2, argName.length() - 1);
404 argName = argName.substring(0, argName.indexOf(
'"'));
406#ifdef DEBUG_ESP_HTTP_SERVER
407 DEBUG_OUTPUT.print(
"PostArg FileName: ");
408 DEBUG_OUTPUT.println(argFilename);
411 if (argFilename ==
"blob" && hasArg(
"filename")) argFilename = arg(
"filename");
413#ifdef DEBUG_ESP_HTTP_SERVER
414 DEBUG_OUTPUT.print(
"PostArg Name: ");
415 DEBUG_OUTPUT.println(argName);
417 argType =
"text/plain";
418 line = client.readStringUntil(
'\r');
419 client.readStringUntil(
'\n');
420 if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(
"Content-Type")){
421 argType = line.substring(line.indexOf(
':')+2);
423 client.readStringUntil(
'\r');
424 client.readStringUntil(
'\n');
426#ifdef DEBUG_ESP_HTTP_SERVER
427 DEBUG_OUTPUT.print(
"PostArg Type: ");
428 DEBUG_OUTPUT.println(argType);
432 line = client.readStringUntil(
'\r');
433 client.readStringUntil(
'\n');
434 if (line.startsWith(
"--"+boundary))
break;
435 if (argValue.length() > 0) argValue +=
"\n";
438#ifdef DEBUG_ESP_HTTP_SERVER
439 DEBUG_OUTPUT.print(
"PostArg Value: ");
440 DEBUG_OUTPUT.println(argValue);
441 DEBUG_OUTPUT.println();
444 RequestArgument& arg = postArgs[postArgsLen++];
446 arg.value = argValue;
448 if (line == (
"--"+boundary+
"--")){
449#ifdef DEBUG_ESP_HTTP_SERVER
450 DEBUG_OUTPUT.println(
"Done Parsing POST");
455 _currentUpload.status = UPLOAD_FILE_START;
456 _currentUpload.name = argName;
457 _currentUpload.filename = argFilename;
458 _currentUpload.type = argType;
459 _currentUpload.totalSize = 0;
460 _currentUpload.currentSize = 0;
461#ifdef DEBUG_ESP_HTTP_SERVER
462 DEBUG_OUTPUT.print(
"Start File: ");
463 DEBUG_OUTPUT.print(_currentUpload.filename);
464 DEBUG_OUTPUT.print(
" Type: ");
465 DEBUG_OUTPUT.println(_currentUpload.type);
467 if(_currentHandler && _currentHandler->canUpload(_currentUri))
468 _currentHandler->upload(*
this, _currentUri, _currentUpload);
469 _currentUpload.status = UPLOAD_FILE_WRITE;
470 uint8_t argByte = _uploadReadByte(client);
472 while(argByte != 0x0D){
473 if (!client.connected())
return _parseFormUploadAborted();
474 _uploadWriteByte(argByte);
475 argByte = _uploadReadByte(client);
478 argByte = _uploadReadByte(client);
479 if (!client.connected())
return _parseFormUploadAborted();
480 if (argByte == 0x0A){
481 argByte = _uploadReadByte(client);
482 if (!client.connected())
return _parseFormUploadAborted();
483 if ((
char)argByte !=
'-'){
485 _uploadWriteByte(0x0D);
486 _uploadWriteByte(0x0A);
489 argByte = _uploadReadByte(client);
490 if (!client.connected())
return _parseFormUploadAborted();
491 if ((
char)argByte !=
'-'){
493 _uploadWriteByte(0x0D);
494 _uploadWriteByte(0x0A);
495 _uploadWriteByte((uint8_t)(
'-'));
500 uint8_t endBuf[boundary.length()];
501 client.readBytes(endBuf, boundary.length());
503 if (strstr((
const char*)endBuf, boundary.c_str()) != NULL){
504 if(_currentHandler && _currentHandler->canUpload(_currentUri))
505 _currentHandler->upload(*
this, _currentUri, _currentUpload);
506 _currentUpload.totalSize += _currentUpload.currentSize;
507 _currentUpload.status = UPLOAD_FILE_END;
508 if(_currentHandler && _currentHandler->canUpload(_currentUri))
509 _currentHandler->upload(*
this, _currentUri, _currentUpload);
510#ifdef DEBUG_ESP_HTTP_SERVER
511 DEBUG_OUTPUT.print(
"End File: ");
512 DEBUG_OUTPUT.print(_currentUpload.filename);
513 DEBUG_OUTPUT.print(
" Type: ");
514 DEBUG_OUTPUT.print(_currentUpload.type);
515 DEBUG_OUTPUT.print(
" Size: ");
516 DEBUG_OUTPUT.println(_currentUpload.totalSize);
518 line = client.readStringUntil(0x0D);
519 client.readStringUntil(0x0A);
521#ifdef DEBUG_ESP_HTTP_SERVER
522 DEBUG_OUTPUT.println(
"Done Parsing POST");
528 _uploadWriteByte(0x0D);
529 _uploadWriteByte(0x0A);
530 _uploadWriteByte((uint8_t)(
'-'));
531 _uploadWriteByte((uint8_t)(
'-'));
533 while(i < boundary.length()){
534 _uploadWriteByte(endBuf[i++]);
536 argByte = _uploadReadByte(client);
540 _uploadWriteByte(0x0D);
550 int totalArgs = ((32 - postArgsLen) < _currentArgCount)?(32 - postArgsLen):_currentArgCount;
551 for (iarg = 0; iarg < totalArgs; iarg++){
552 RequestArgument& arg = postArgs[postArgsLen++];
553 arg.key = _currentArgs[iarg].key;
554 arg.value = _currentArgs[iarg].value;
556 if (_currentArgs)
delete[] _currentArgs;
557 _currentArgs =
new RequestArgument[postArgsLen];
558 for (iarg = 0; iarg < postArgsLen; iarg++){
559 RequestArgument& arg = _currentArgs[iarg];
560 arg.key = postArgs[iarg].key;
561 arg.value = postArgs[iarg].value;
563 _currentArgCount = iarg;
564 if (postArgs)
delete[] postArgs;
567#ifdef DEBUG_ESP_HTTP_SERVER
568 DEBUG_OUTPUT.print(
"Error: line: ");
569 DEBUG_OUTPUT.println(line);
574String WebServer::urlDecode(
const String& text)
577 char temp[] =
"0x00";
578 unsigned int len = text.length();
583 char encodedChar = text.charAt(i++);
584 if ((encodedChar ==
'%') && (i + 1 < len))
586 temp[2] = text.charAt(i++);
587 temp[3] = text.charAt(i++);
589 decodedChar = strtol(temp, NULL, 16);
592 if (encodedChar ==
'+')
597 decodedChar = encodedChar;
600 decoded += decodedChar;
605bool WebServer::_parseFormUploadAborted(){
606 _currentUpload.status = UPLOAD_FILE_ABORTED;
607 if(_currentHandler && _currentHandler->canUpload(_currentUri))
608 _currentHandler->upload(*
this, _currentUri, _currentUpload);