summaryrefslogtreecommitdiff
path: root/libraries/ESP_Async_WebServer/src/WebRequest.cpp
diff options
context:
space:
mode:
authorschererleander <leander@schererleander.de>2026-01-20 08:34:54 +0100
committerschererleander <leander@schererleander.de>2026-01-20 08:34:54 +0100
commit85ea4e995a75abe061f6fc375ea0481084dddd43 (patch)
tree7eb5d57653ecd8f041aeac4e68d7d554c1168681 /libraries/ESP_Async_WebServer/src/WebRequest.cpp
initial commitHEADmain
Diffstat (limited to 'libraries/ESP_Async_WebServer/src/WebRequest.cpp')
-rw-r--r--libraries/ESP_Async_WebServer/src/WebRequest.cpp1185
1 files changed, 1185 insertions, 0 deletions
diff --git a/libraries/ESP_Async_WebServer/src/WebRequest.cpp b/libraries/ESP_Async_WebServer/src/WebRequest.cpp
new file mode 100644
index 0000000..8b735af
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/src/WebRequest.cpp
@@ -0,0 +1,1185 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+#include "ESPAsyncWebServer.h"
+#include "WebAuthentication.h"
+#include "WebResponseImpl.h"
+#include "literals.h"
+#include <cstring>
+
+#define __is_param_char(c) ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '='))
+
+static void doNotDelete(AsyncWebServerRequest *) {}
+
+using namespace asyncsrv;
+
+enum {
+ PARSE_REQ_START = 0,
+ PARSE_REQ_HEADERS = 1,
+ PARSE_REQ_BODY = 2,
+ PARSE_REQ_END = 3,
+ PARSE_REQ_FAIL = 4
+};
+
+AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer *s, AsyncClient *c)
+ : _client(c), _server(s), _handler(NULL), _response(NULL), _temp(), _parseState(PARSE_REQ_START), _version(0), _method(HTTP_ANY), _url(), _host(),
+ _contentType(), _boundary(), _authorization(), _reqconntype(RCT_HTTP), _authMethod(AsyncAuthType::AUTH_NONE), _isMultipart(false), _isPlainPost(false),
+ _expectingContinue(false), _contentLength(0), _parsedLength(0), _multiParseState(0), _boundaryPosition(0), _itemStartIndex(0), _itemSize(0), _itemName(),
+ _itemFilename(), _itemType(), _itemValue(), _itemBuffer(0), _itemBufferIndex(0), _itemIsFile(false), _tempObject(NULL) {
+ c->onError(
+ [](void *r, AsyncClient *c, int8_t error) {
+ (void)c;
+ // log_e("AsyncWebServerRequest::_onError");
+ AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
+ req->_onError(error);
+ },
+ this
+ );
+ c->onAck(
+ [](void *r, AsyncClient *c, size_t len, uint32_t time) {
+ (void)c;
+ // log_e("AsyncWebServerRequest::_onAck");
+ AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
+ req->_onAck(len, time);
+ },
+ this
+ );
+ c->onDisconnect(
+ [](void *r, AsyncClient *c) {
+ // log_e("AsyncWebServerRequest::_onDisconnect");
+ AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
+ req->_onDisconnect();
+ delete c;
+ },
+ this
+ );
+ c->onTimeout(
+ [](void *r, AsyncClient *c, uint32_t time) {
+ (void)c;
+ // log_e("AsyncWebServerRequest::_onTimeout");
+ AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
+ req->_onTimeout(time);
+ },
+ this
+ );
+ c->onData(
+ [](void *r, AsyncClient *c, void *buf, size_t len) {
+ (void)c;
+ // log_e("AsyncWebServerRequest::_onData");
+ AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
+ req->_onData(buf, len);
+ },
+ this
+ );
+ c->onPoll(
+ [](void *r, AsyncClient *c) {
+ (void)c;
+ // log_e("AsyncWebServerRequest::_onPoll");
+ AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
+ req->_onPoll();
+ },
+ this
+ );
+}
+
+AsyncWebServerRequest::~AsyncWebServerRequest() {
+ // log_e("AsyncWebServerRequest::~AsyncWebServerRequest");
+
+ _this.reset();
+
+ _headers.clear();
+
+ _pathParams.clear();
+
+ AsyncWebServerResponse *r = _response;
+ _response = NULL;
+ delete r;
+
+ if (_tempObject != NULL) {
+ free(_tempObject);
+ }
+
+ if (_tempFile) {
+ _tempFile.close();
+ }
+
+ if (_itemBuffer) {
+ free(_itemBuffer);
+ }
+}
+
+void AsyncWebServerRequest::_onData(void *buf, size_t len) {
+ // SSL/TLS handshake detection
+#ifndef ASYNC_TCP_SSL_ENABLED
+ if (_parseState == PARSE_REQ_START && len && ((uint8_t *)buf)[0] == 0x16) { // 0x16 indicates a Handshake message (SSL/TLS).
+#ifdef ESP32
+ log_d("SSL/TLS handshake detected: resetting connection");
+#endif
+ _parseState = PARSE_REQ_FAIL;
+ abort();
+ return;
+ }
+#endif
+
+ size_t i = 0;
+ while (true) {
+
+ if (_parseState < PARSE_REQ_BODY) {
+ // Find new line in buf
+ char *str = (char *)buf;
+ for (i = 0; i < len; i++) {
+ // Check for null characters in header
+ if (!str[i]) {
+ _parseState = PARSE_REQ_FAIL;
+ abort();
+ return;
+ }
+ if (str[i] == '\n') {
+ break;
+ }
+ }
+ if (i == len) { // No new line, just add the buffer in _temp
+ char ch = str[len - 1];
+ str[len - 1] = 0;
+ if (!_temp.reserve(_temp.length() + len)) {
+#ifdef ESP32
+ log_e("Failed to allocate");
+#endif
+ _parseState = PARSE_REQ_FAIL;
+ abort();
+ return;
+ }
+ _temp.concat(str);
+ _temp.concat(ch);
+ } else { // Found new line - extract it and parse
+ str[i] = 0; // Terminate the string at the end of the line.
+ _temp.concat(str);
+ _temp.trim();
+ _parseLine();
+ if (++i < len) {
+ // Still have more buffer to process
+ buf = str + i;
+ len -= i;
+ continue;
+ }
+ }
+ } else if (_parseState == PARSE_REQ_BODY) {
+ // A handler should be already attached at this point in _parseLine function.
+ // If handler does nothing (_onRequest is NULL), we don't need to really parse the body.
+ const bool needParse = _handler && !_handler->isRequestHandlerTrivial();
+ // Discard any bytes after content length; handlers may overrun their buffers
+ len = std::min(len, _contentLength - _parsedLength);
+ if (_isMultipart) {
+ if (needParse) {
+ size_t i;
+ for (i = 0; i < len; i++) {
+ _parseMultipartPostByte(((uint8_t *)buf)[i], i == len - 1);
+ _parsedLength++;
+ }
+ } else {
+ _parsedLength += len;
+ }
+ } else {
+ if (_parsedLength == 0) {
+ if (_contentType.startsWith(T_app_xform_urlencoded)) {
+ _isPlainPost = true;
+ } else if (_contentType == T_text_plain && __is_param_char(((char *)buf)[0])) {
+ size_t i = 0;
+ while (i < len && __is_param_char(((char *)buf)[i++]));
+ if (i < len && ((char *)buf)[i - 1] == '=') {
+ _isPlainPost = true;
+ }
+ }
+ }
+ if (!_isPlainPost) {
+ // ESP_LOGD("AsyncWebServer", "_isPlainPost: %d, _handler: %p", _isPlainPost, _handler);
+ if (_handler) {
+ _handler->handleBody(this, (uint8_t *)buf, len, _parsedLength, _contentLength);
+ }
+ _parsedLength += len;
+ } else if (needParse) {
+ size_t i;
+ for (i = 0; i < len; i++) {
+ _parsedLength++;
+ _parsePlainPostChar(((uint8_t *)buf)[i]);
+ }
+ } else {
+ _parsedLength += len;
+ }
+ }
+ if (_parsedLength == _contentLength) {
+ _parseState = PARSE_REQ_END;
+ _runMiddlewareChain();
+ _send();
+ }
+ }
+ break;
+ }
+}
+
+void AsyncWebServerRequest::_onPoll() {
+ // os_printf("p\n");
+ if (_response != NULL && _client != NULL && _client->canSend()) {
+ if (!_response->_finished()) {
+ _response->_ack(this, 0, 0);
+ } else {
+ AsyncWebServerResponse *r = _response;
+ _response = NULL;
+ delete r;
+
+ _client->close();
+ }
+ }
+}
+
+void AsyncWebServerRequest::_onAck(size_t len, uint32_t time) {
+ // os_printf("a:%u:%u\n", len, time);
+ if (_response != NULL) {
+ if (!_response->_finished()) {
+ _response->_ack(this, len, time);
+ } else if (_response->_finished()) {
+ AsyncWebServerResponse *r = _response;
+ _response = NULL;
+ delete r;
+
+ _client->close();
+ }
+ }
+}
+
+void AsyncWebServerRequest::_onError(int8_t error) {
+ (void)error;
+}
+
+void AsyncWebServerRequest::_onTimeout(uint32_t time) {
+ (void)time;
+ // os_printf("TIMEOUT: %u, state: %s\n", time, _client->stateToString());
+ _client->close();
+}
+
+void AsyncWebServerRequest::onDisconnect(ArDisconnectHandler fn) {
+ _onDisconnectfn = fn;
+}
+
+void AsyncWebServerRequest::_onDisconnect() {
+ // os_printf("d\n");
+ if (_onDisconnectfn) {
+ _onDisconnectfn();
+ }
+ _server->_handleDisconnect(this);
+}
+
+void AsyncWebServerRequest::_addPathParam(const char *p) {
+ _pathParams.emplace_back(p);
+}
+
+void AsyncWebServerRequest::_addGetParams(const String &params) {
+ size_t start = 0;
+ while (start < params.length()) {
+ int end = params.indexOf('&', start);
+ if (end < 0) {
+ end = params.length();
+ }
+ int equal = params.indexOf('=', start);
+ if (equal < 0 || equal > end) {
+ equal = end;
+ }
+ String name = urlDecode(params.substring(start, equal));
+ String value = urlDecode(equal + 1 < end ? params.substring(equal + 1, end) : emptyString);
+ if (name.length()) {
+ _params.emplace_back(name, value);
+ }
+ start = end + 1;
+ }
+}
+
+bool AsyncWebServerRequest::_parseReqHead() {
+ // Split the head into method, url and version
+ int index = _temp.indexOf(' ');
+ String m = _temp.substring(0, index);
+ index = _temp.indexOf(' ', index + 1);
+ String u = _temp.substring(m.length() + 1, index);
+ _temp = _temp.substring(index + 1);
+
+ if (m == T_GET) {
+ _method = HTTP_GET;
+ } else if (m == T_POST) {
+ _method = HTTP_POST;
+ } else if (m == T_DELETE) {
+ _method = HTTP_DELETE;
+ } else if (m == T_PUT) {
+ _method = HTTP_PUT;
+ } else if (m == T_PATCH) {
+ _method = HTTP_PATCH;
+ } else if (m == T_HEAD) {
+ _method = HTTP_HEAD;
+ } else if (m == T_OPTIONS) {
+ _method = HTTP_OPTIONS;
+ } else {
+ return false;
+ }
+
+ String g;
+ index = u.indexOf('?');
+ if (index > 0) {
+ g = u.substring(index + 1);
+ u = u.substring(0, index);
+ }
+ _url = urlDecode(u);
+ _addGetParams(g);
+
+ if (!_url.length()) {
+ return false;
+ }
+
+ if (!_temp.startsWith(T_HTTP_1_0)) {
+ _version = 1;
+ }
+
+ _temp = emptyString;
+ return true;
+}
+
+bool AsyncWebServerRequest::_parseReqHeader() {
+ int index = _temp.indexOf(':');
+ if (index) {
+ String name(_temp.substring(0, index));
+ String value(_temp.substring(index + 2));
+ if (name.equalsIgnoreCase(T_Host)) {
+ _host = value;
+ } else if (name.equalsIgnoreCase(T_Content_Type)) {
+ _contentType = value.substring(0, value.indexOf(';'));
+ if (value.startsWith(T_MULTIPART_)) {
+ _boundary = value.substring(value.indexOf('=') + 1);
+ _boundary.replace(String('"'), String());
+ _isMultipart = true;
+ }
+ } else if (name.equalsIgnoreCase(T_Content_Length)) {
+ _contentLength = atoi(value.c_str());
+ } else if (name.equalsIgnoreCase(T_EXPECT) && value.equalsIgnoreCase(T_100_CONTINUE)) {
+ _expectingContinue = true;
+ } else if (name.equalsIgnoreCase(T_AUTH)) {
+ int space = value.indexOf(' ');
+ if (space == -1) {
+ _authorization = value;
+ _authMethod = AsyncAuthType::AUTH_OTHER;
+ } else {
+ String method = value.substring(0, space);
+ if (method.equalsIgnoreCase(T_BASIC)) {
+ _authMethod = AsyncAuthType::AUTH_BASIC;
+ } else if (method.equalsIgnoreCase(T_DIGEST)) {
+ _authMethod = AsyncAuthType::AUTH_DIGEST;
+ } else if (method.equalsIgnoreCase(T_BEARER)) {
+ _authMethod = AsyncAuthType::AUTH_BEARER;
+ } else {
+ _authMethod = AsyncAuthType::AUTH_OTHER;
+ }
+ _authorization = value.substring(space + 1);
+ }
+ } else if (name.equalsIgnoreCase(T_UPGRADE) && value.equalsIgnoreCase(T_WS)) {
+ // WebSocket request can be uniquely identified by header: [Upgrade: websocket]
+ _reqconntype = RCT_WS;
+ } else if (name.equalsIgnoreCase(T_ACCEPT)) {
+ String lowcase(value);
+ lowcase.toLowerCase();
+#ifndef ESP8266
+ const char *substr = std::strstr(lowcase.c_str(), T_text_event_stream);
+#else
+ const char *substr = std::strstr(lowcase.c_str(), String(T_text_event_stream).c_str());
+#endif
+ if (substr != NULL) {
+ // WebEvent request can be uniquely identified by header: [Accept: text/event-stream]
+ _reqconntype = RCT_EVENT;
+ }
+ }
+ _headers.emplace_back(name, value);
+ }
+#if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+ // Ancient PRI core does not have String::clear() method 8-()
+ _temp = emptyString;
+#else
+ _temp.clear();
+#endif
+ return true;
+}
+
+void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data) {
+ if (data && (char)data != '&') {
+ _temp += (char)data;
+ }
+ if (!data || (char)data == '&' || _parsedLength == _contentLength) {
+ String name(T_BODY);
+ String value(_temp);
+ if (!(_temp.charAt(0) == '{') && !(_temp.charAt(0) == '[') && _temp.indexOf('=') > 0) {
+ name = _temp.substring(0, _temp.indexOf('='));
+ value = _temp.substring(_temp.indexOf('=') + 1);
+ }
+ name = urlDecode(name);
+ if (name.length()) {
+ _params.emplace_back(name, urlDecode(value), true);
+ }
+
+#if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+ // Ancient PRI core does not have String::clear() method 8-()
+ _temp = emptyString;
+#else
+ _temp.clear();
+#endif
+ }
+}
+
+void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last) {
+ _itemBuffer[_itemBufferIndex++] = data;
+
+ if (last || _itemBufferIndex == RESPONSE_STREAM_BUFFER_SIZE) {
+ // check if authenticated before calling the upload
+ if (_handler) {
+ _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false);
+ }
+ _itemBufferIndex = 0;
+ }
+}
+
+enum {
+ EXPECT_BOUNDARY,
+ PARSE_HEADERS,
+ WAIT_FOR_RETURN1,
+ EXPECT_FEED1,
+ EXPECT_DASH1,
+ EXPECT_DASH2,
+ BOUNDARY_OR_DATA,
+ DASH3_OR_RETURN2,
+ EXPECT_FEED2,
+ PARSING_FINISHED,
+ PARSE_ERROR
+};
+
+void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) {
+#define itemWriteByte(b) \
+ do { \
+ _itemSize++; \
+ if (_itemIsFile) \
+ _handleUploadByte(b, last); \
+ else \
+ _itemValue += (char)(b); \
+ } while (0)
+
+ if (!_parsedLength) {
+ _multiParseState = EXPECT_BOUNDARY;
+ _temp = emptyString;
+ _itemName = emptyString;
+ _itemFilename = emptyString;
+ _itemType = emptyString;
+ }
+
+ if (_multiParseState == WAIT_FOR_RETURN1) {
+ if (data != '\r') {
+ itemWriteByte(data);
+ } else {
+ _multiParseState = EXPECT_FEED1;
+ }
+ } else if (_multiParseState == EXPECT_BOUNDARY) {
+ if (_parsedLength < 2 && data != '-') {
+ _multiParseState = PARSE_ERROR;
+ return;
+ } else if (_parsedLength - 2 < _boundary.length() && _boundary.c_str()[_parsedLength - 2] != data) {
+ _multiParseState = PARSE_ERROR;
+ return;
+ } else if (_parsedLength - 2 == _boundary.length() && data != '\r') {
+ _multiParseState = PARSE_ERROR;
+ return;
+ } else if (_parsedLength - 3 == _boundary.length()) {
+ if (data != '\n') {
+ _multiParseState = PARSE_ERROR;
+ return;
+ }
+ _multiParseState = PARSE_HEADERS;
+ _itemIsFile = false;
+ }
+ } else if (_multiParseState == PARSE_HEADERS) {
+ if ((char)data != '\r' && (char)data != '\n') {
+ _temp += (char)data;
+ }
+ if ((char)data == '\n') {
+ if (_temp.length()) {
+ if (_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase(T_Content_Type)) {
+ _itemType = _temp.substring(14);
+ _itemIsFile = true;
+ } else if (_temp.length() > 19 && _temp.substring(0, 19).equalsIgnoreCase(T_Content_Disposition)) {
+ _temp = _temp.substring(_temp.indexOf(';') + 2);
+ while (_temp.indexOf(';') > 0) {
+ String name = _temp.substring(0, _temp.indexOf('='));
+ String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.indexOf(';') - 1);
+ if (name == T_name) {
+ _itemName = nameVal;
+ } else if (name == T_filename) {
+ _itemFilename = nameVal;
+ _itemIsFile = true;
+ }
+ _temp = _temp.substring(_temp.indexOf(';') + 2);
+ }
+ String name = _temp.substring(0, _temp.indexOf('='));
+ String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.length() - 1);
+ if (name == T_name) {
+ _itemName = nameVal;
+ } else if (name == T_filename) {
+ _itemFilename = nameVal;
+ _itemIsFile = true;
+ }
+ }
+ _temp = emptyString;
+ } else {
+ _multiParseState = WAIT_FOR_RETURN1;
+ // value starts from here
+ _itemSize = 0;
+ _itemStartIndex = _parsedLength;
+ _itemValue = emptyString;
+ if (_itemIsFile) {
+ if (_itemBuffer) {
+ free(_itemBuffer);
+ }
+ _itemBuffer = (uint8_t *)malloc(RESPONSE_STREAM_BUFFER_SIZE);
+ if (_itemBuffer == NULL) {
+#ifdef ESP32
+ log_e("Failed to allocate");
+#endif
+ _multiParseState = PARSE_ERROR;
+ abort();
+ return;
+ }
+ _itemBufferIndex = 0;
+ }
+ }
+ }
+ } else if (_multiParseState == EXPECT_FEED1) {
+ if (data != '\n') {
+ _multiParseState = WAIT_FOR_RETURN1;
+ itemWriteByte('\r');
+ _parseMultipartPostByte(data, last);
+ } else {
+ _multiParseState = EXPECT_DASH1;
+ }
+ } else if (_multiParseState == EXPECT_DASH1) {
+ if (data != '-') {
+ _multiParseState = WAIT_FOR_RETURN1;
+ itemWriteByte('\r');
+ itemWriteByte('\n');
+ _parseMultipartPostByte(data, last);
+ } else {
+ _multiParseState = EXPECT_DASH2;
+ }
+ } else if (_multiParseState == EXPECT_DASH2) {
+ if (data != '-') {
+ _multiParseState = WAIT_FOR_RETURN1;
+ itemWriteByte('\r');
+ itemWriteByte('\n');
+ itemWriteByte('-');
+ _parseMultipartPostByte(data, last);
+ } else {
+ _multiParseState = BOUNDARY_OR_DATA;
+ _boundaryPosition = 0;
+ }
+ } else if (_multiParseState == BOUNDARY_OR_DATA) {
+ if (_boundaryPosition < _boundary.length() && _boundary.c_str()[_boundaryPosition] != data) {
+ _multiParseState = WAIT_FOR_RETURN1;
+ itemWriteByte('\r');
+ itemWriteByte('\n');
+ itemWriteByte('-');
+ itemWriteByte('-');
+ uint8_t i;
+ for (i = 0; i < _boundaryPosition; i++) {
+ itemWriteByte(_boundary.c_str()[i]);
+ }
+ _parseMultipartPostByte(data, last);
+ } else if (_boundaryPosition == _boundary.length() - 1) {
+ _multiParseState = DASH3_OR_RETURN2;
+ if (!_itemIsFile) {
+ _params.emplace_back(_itemName, _itemValue, true);
+ } else {
+ if (_itemSize) {
+ if (_handler) {
+ _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true);
+ }
+ _itemBufferIndex = 0;
+ _params.emplace_back(_itemName, _itemFilename, true, true, _itemSize);
+ }
+ free(_itemBuffer);
+ _itemBuffer = NULL;
+ }
+
+ } else {
+ _boundaryPosition++;
+ }
+ } else if (_multiParseState == DASH3_OR_RETURN2) {
+ if (data == '-' && (_contentLength - _parsedLength - 4) != 0) {
+ // os_printf("ERROR: The parser got to the end of the POST but is expecting %u bytes more!\nDrop an issue so we can have more info on the matter!\n", _contentLength - _parsedLength - 4);
+ _contentLength = _parsedLength + 4; // lets close the request gracefully
+ }
+ if (data == '\r') {
+ _multiParseState = EXPECT_FEED2;
+ } else if (data == '-' && _contentLength == (_parsedLength + 4)) {
+ _multiParseState = PARSING_FINISHED;
+ } else {
+ _multiParseState = WAIT_FOR_RETURN1;
+ itemWriteByte('\r');
+ itemWriteByte('\n');
+ itemWriteByte('-');
+ itemWriteByte('-');
+ uint8_t i;
+ for (i = 0; i < _boundary.length(); i++) {
+ itemWriteByte(_boundary.c_str()[i]);
+ }
+ _parseMultipartPostByte(data, last);
+ }
+ } else if (_multiParseState == EXPECT_FEED2) {
+ if (data == '\n') {
+ _multiParseState = PARSE_HEADERS;
+ _itemIsFile = false;
+ } else {
+ _multiParseState = WAIT_FOR_RETURN1;
+ itemWriteByte('\r');
+ itemWriteByte('\n');
+ itemWriteByte('-');
+ itemWriteByte('-');
+ uint8_t i;
+ for (i = 0; i < _boundary.length(); i++) {
+ itemWriteByte(_boundary.c_str()[i]);
+ }
+ itemWriteByte('\r');
+ _parseMultipartPostByte(data, last);
+ }
+ }
+}
+
+void AsyncWebServerRequest::_parseLine() {
+ if (_parseState == PARSE_REQ_START) {
+ if (!_temp.length()) {
+ _parseState = PARSE_REQ_FAIL;
+ abort();
+ } else {
+ if (_parseReqHead()) {
+ _parseState = PARSE_REQ_HEADERS;
+ } else {
+ _parseState = PARSE_REQ_FAIL;
+ abort();
+ }
+ }
+ return;
+ }
+
+ if (_parseState == PARSE_REQ_HEADERS) {
+ if (!_temp.length()) {
+ // end of headers
+ _server->_rewriteRequest(this);
+ _server->_attachHandler(this);
+ if (_expectingContinue) {
+ String response(T_HTTP_100_CONT);
+ _client->write(response.c_str(), response.length());
+ }
+ if (_contentLength) {
+ _parseState = PARSE_REQ_BODY;
+ } else {
+ _parseState = PARSE_REQ_END;
+ _runMiddlewareChain();
+ _send();
+ }
+ } else {
+ _parseReqHeader();
+ }
+ }
+}
+
+void AsyncWebServerRequest::_runMiddlewareChain() {
+ if (_handler && _handler->mustSkipServerMiddlewares()) {
+ _handler->_runChain(this, [this]() {
+ _handler->handleRequest(this);
+ });
+ } else {
+ _server->_runChain(this, [this]() {
+ if (_handler) {
+ _handler->_runChain(this, [this]() {
+ _handler->handleRequest(this);
+ });
+ }
+ });
+ }
+}
+
+void AsyncWebServerRequest::_send() {
+ if (!_sent && !_paused) {
+ // log_d("AsyncWebServerRequest::_send()");
+
+ // user did not create a response ?
+ if (!_response) {
+ send(501, T_text_plain, "Handler did not handle the request");
+ }
+
+ // response is not valid ?
+ if (!_response->_sourceValid()) {
+ send(500, T_text_plain, "Invalid data in handler");
+ }
+
+ // here, we either have a response give nfrom user or one of the two above
+ _client->setRxTimeout(0);
+ _response->_respond(this);
+ _sent = true;
+ }
+}
+
+AsyncWebServerRequestPtr AsyncWebServerRequest::pause() {
+ if (_paused) {
+ return _this;
+ }
+ client()->setRxTimeout(0);
+ // this shared ptr will hold the request pointer until it gets destroyed following a disconnect.
+ // this is just used as a holder providing weak observers, so the deleter is a no-op.
+ _this = std::shared_ptr<AsyncWebServerRequest>(this, doNotDelete);
+ _paused = true;
+ return _this;
+}
+
+void AsyncWebServerRequest::abort() {
+ if (!_sent) {
+ _sent = true;
+ _paused = false;
+ _this.reset();
+ // log_e("AsyncWebServerRequest::abort");
+ _client->abort();
+ }
+}
+
+size_t AsyncWebServerRequest::headers() const {
+ return _headers.size();
+}
+
+bool AsyncWebServerRequest::hasHeader(const char *name) const {
+ for (const auto &h : _headers) {
+ if (h.name().equalsIgnoreCase(name)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+#ifdef ESP8266
+bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper *data) const {
+ return hasHeader(String(data));
+}
+#endif
+
+const AsyncWebHeader *AsyncWebServerRequest::getHeader(const char *name) const {
+ auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader &header) {
+ return header.name().equalsIgnoreCase(name);
+ });
+ return (iter == std::end(_headers)) ? nullptr : &(*iter);
+}
+
+#ifdef ESP8266
+const AsyncWebHeader *AsyncWebServerRequest::getHeader(const __FlashStringHelper *data) const {
+ PGM_P p = reinterpret_cast<PGM_P>(data);
+ size_t n = strlen_P(p);
+ char *name = (char *)malloc(n + 1);
+ if (name) {
+ strcpy_P(name, p);
+ const AsyncWebHeader *result = getHeader(String(name));
+ free(name);
+ return result;
+ } else {
+ return nullptr;
+ }
+}
+#endif
+
+const AsyncWebHeader *AsyncWebServerRequest::getHeader(size_t num) const {
+ if (num >= _headers.size()) {
+ return nullptr;
+ }
+ return &(*std::next(_headers.cbegin(), num));
+}
+
+size_t AsyncWebServerRequest::getHeaderNames(std::vector<const char *> &names) const {
+ const size_t size = names.size();
+ for (const auto &h : _headers) {
+ names.push_back(h.name().c_str());
+ }
+ return names.size() - size;
+}
+
+bool AsyncWebServerRequest::removeHeader(const char *name) {
+ const size_t size = _headers.size();
+ _headers.remove_if([name](const AsyncWebHeader &header) {
+ return header.name().equalsIgnoreCase(name);
+ });
+ return size != _headers.size();
+}
+
+size_t AsyncWebServerRequest::params() const {
+ return _params.size();
+}
+
+bool AsyncWebServerRequest::hasParam(const char *name, bool post, bool file) const {
+ for (const auto &p : _params) {
+ if (p.name().equals(name) && p.isPost() == post && p.isFile() == file) {
+ return true;
+ }
+ }
+ return false;
+}
+
+const AsyncWebParameter *AsyncWebServerRequest::getParam(const char *name, bool post, bool file) const {
+ for (const auto &p : _params) {
+ if (p.name() == name && p.isPost() == post && p.isFile() == file) {
+ return &p;
+ }
+ }
+ return nullptr;
+}
+
+#ifdef ESP8266
+const AsyncWebParameter *AsyncWebServerRequest::getParam(const __FlashStringHelper *data, bool post, bool file) const {
+ return getParam(String(data), post, file);
+}
+#endif
+
+const AsyncWebParameter *AsyncWebServerRequest::getParam(size_t num) const {
+ if (num >= _params.size()) {
+ return nullptr;
+ }
+ return &(*std::next(_params.cbegin(), num));
+}
+
+const String &AsyncWebServerRequest::getAttribute(const char *name, const String &defaultValue) const {
+ auto it = _attributes.find(name);
+ return it != _attributes.end() ? it->second : defaultValue;
+}
+bool AsyncWebServerRequest::getAttribute(const char *name, bool defaultValue) const {
+ auto it = _attributes.find(name);
+ return it != _attributes.end() ? it->second == "1" : defaultValue;
+}
+long AsyncWebServerRequest::getAttribute(const char *name, long defaultValue) const {
+ auto it = _attributes.find(name);
+ return it != _attributes.end() ? it->second.toInt() : defaultValue;
+}
+float AsyncWebServerRequest::getAttribute(const char *name, float defaultValue) const {
+ auto it = _attributes.find(name);
+ return it != _attributes.end() ? it->second.toFloat() : defaultValue;
+}
+double AsyncWebServerRequest::getAttribute(const char *name, double defaultValue) const {
+ auto it = _attributes.find(name);
+ return it != _attributes.end() ? it->second.toDouble() : defaultValue;
+}
+
+AsyncWebServerResponse *AsyncWebServerRequest::beginResponse(int code, const char *contentType, const char *content, AwsTemplateProcessor callback) {
+ if (callback) {
+ return new AsyncProgmemResponse(code, contentType, (const uint8_t *)content, strlen(content), callback);
+ }
+ return new AsyncBasicResponse(code, contentType, content);
+}
+
+AsyncWebServerResponse *
+ AsyncWebServerRequest::beginResponse(int code, const char *contentType, const uint8_t *content, size_t len, AwsTemplateProcessor callback) {
+ return new AsyncProgmemResponse(code, contentType, content, len, callback);
+}
+
+AsyncWebServerResponse *
+ AsyncWebServerRequest::beginResponse(FS &fs, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback) {
+ if (fs.exists(path) || (!download && fs.exists(path + T__gz))) {
+ return new AsyncFileResponse(fs, path, contentType, download, callback);
+ }
+ return NULL;
+}
+
+AsyncWebServerResponse *
+ AsyncWebServerRequest::beginResponse(File content, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback) {
+ if (content == true) {
+ return new AsyncFileResponse(content, path, contentType, download, callback);
+ }
+ return NULL;
+}
+
+AsyncWebServerResponse *AsyncWebServerRequest::beginResponse(Stream &stream, const char *contentType, size_t len, AwsTemplateProcessor callback) {
+ return new AsyncStreamResponse(stream, contentType, len, callback);
+}
+
+AsyncWebServerResponse *
+ AsyncWebServerRequest::beginResponse(const char *contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) {
+ return new AsyncCallbackResponse(contentType, len, callback, templateCallback);
+}
+
+AsyncWebServerResponse *
+ AsyncWebServerRequest::beginChunkedResponse(const char *contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) {
+ if (_version) {
+ return new AsyncChunkedResponse(contentType, callback, templateCallback);
+ }
+ return new AsyncCallbackResponse(contentType, 0, callback, templateCallback);
+}
+
+AsyncResponseStream *AsyncWebServerRequest::beginResponseStream(const char *contentType, size_t bufferSize) {
+ return new AsyncResponseStream(contentType, bufferSize);
+}
+
+AsyncWebServerResponse *AsyncWebServerRequest::beginResponse_P(int code, const String &contentType, PGM_P content, AwsTemplateProcessor callback) {
+ return new AsyncProgmemResponse(code, contentType, (const uint8_t *)content, strlen_P(content), callback);
+}
+
+void AsyncWebServerRequest::send(AsyncWebServerResponse *response) {
+ // request is already sent on the wire ?
+ if (_sent) {
+ return;
+ }
+
+ // if we already had a response, delete it and replace it with the new one
+ if (_response) {
+ delete _response;
+ }
+ _response = response;
+
+ // if request was paused, we need to send the response now
+ if (_paused) {
+ _paused = false;
+ _send();
+ }
+}
+
+void AsyncWebServerRequest::redirect(const char *url, int code) {
+ AsyncWebServerResponse *response = beginResponse(code);
+ response->addHeader(T_LOCATION, url);
+ send(response);
+}
+
+bool AsyncWebServerRequest::authenticate(const char *username, const char *password, const char *realm, bool passwordIsHash) const {
+ if (_authorization.length()) {
+ if (_authMethod == AsyncAuthType::AUTH_DIGEST) {
+ return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL);
+ } else if (!passwordIsHash) {
+ return checkBasicAuthentication(_authorization.c_str(), username, password);
+ } else {
+ return _authorization.equals(password);
+ }
+ }
+ return false;
+}
+
+bool AsyncWebServerRequest::authenticate(const char *hash) const {
+ if (!_authorization.length() || hash == NULL) {
+ return false;
+ }
+
+ if (_authMethod == AsyncAuthType::AUTH_DIGEST) {
+ String hStr = String(hash);
+ int separator = hStr.indexOf(':');
+ if (separator <= 0) {
+ return false;
+ }
+ String username = hStr.substring(0, separator);
+ hStr = hStr.substring(separator + 1);
+ separator = hStr.indexOf(':');
+ if (separator <= 0) {
+ return false;
+ }
+ String realm = hStr.substring(0, separator);
+ hStr = hStr.substring(separator + 1);
+ return checkDigestAuthentication(_authorization.c_str(), methodToString(), username.c_str(), hStr.c_str(), realm.c_str(), true, NULL, NULL, NULL);
+ }
+
+ // Basic Auth, Bearer Auth, or other
+ return (_authorization.equals(hash));
+}
+
+void AsyncWebServerRequest::requestAuthentication(AsyncAuthType method, const char *realm, const char *_authFailMsg) {
+ if (!realm) {
+ realm = T_LOGIN_REQ;
+ }
+
+ AsyncWebServerResponse *r = _authFailMsg ? beginResponse(401, T_text_html, _authFailMsg) : beginResponse(401);
+
+ switch (method) {
+ case AsyncAuthType::AUTH_BASIC:
+ {
+ String header;
+ if (header.reserve(strlen(T_BASIC_REALM) + strlen(realm) + 1)) {
+ header.concat(T_BASIC_REALM);
+ header.concat(realm);
+ header.concat('"');
+ r->addHeader(T_WWW_AUTH, header.c_str());
+ } else {
+#ifdef ESP32
+ log_e("Failed to allocate");
+#endif
+ abort();
+ }
+
+ break;
+ }
+ case AsyncAuthType::AUTH_DIGEST:
+ {
+ size_t len = strlen(T_DIGEST_) + strlen(T_realm__) + strlen(T_auth_nonce) + 32 + strlen(T__opaque) + 32 + 1;
+ String header;
+ if (header.reserve(len + strlen(realm))) {
+ const String nonce = genRandomMD5();
+ const String opaque = genRandomMD5();
+ if (nonce.length() && opaque.length()) {
+ header.concat(T_DIGEST_);
+ header.concat(T_realm__);
+ header.concat(realm);
+ header.concat(T_auth_nonce);
+ header.concat(nonce);
+ header.concat(T__opaque);
+ header.concat(opaque);
+ header.concat((char)0x22); // '"'
+ r->addHeader(T_WWW_AUTH, header.c_str());
+ } else {
+#ifdef ESP32
+ log_e("Failed to allocate");
+#endif
+ abort();
+ }
+ }
+ break;
+ }
+ default: break;
+ }
+
+ send(r);
+}
+
+bool AsyncWebServerRequest::hasArg(const char *name) const {
+ for (const auto &arg : _params) {
+ if (arg.name() == name) {
+ return true;
+ }
+ }
+ return false;
+}
+
+#ifdef ESP8266
+bool AsyncWebServerRequest::hasArg(const __FlashStringHelper *data) const {
+ return hasArg(String(data).c_str());
+}
+#endif
+
+const String &AsyncWebServerRequest::arg(const char *name) const {
+ for (const auto &arg : _params) {
+ if (arg.name() == name) {
+ return arg.value();
+ }
+ }
+ return emptyString;
+}
+
+#ifdef ESP8266
+const String &AsyncWebServerRequest::arg(const __FlashStringHelper *data) const {
+ return arg(String(data).c_str());
+}
+#endif
+
+const String &AsyncWebServerRequest::arg(size_t i) const {
+ return getParam(i)->value();
+}
+
+const String &AsyncWebServerRequest::argName(size_t i) const {
+ return getParam(i)->name();
+}
+
+const String &AsyncWebServerRequest::pathArg(size_t i) const {
+ if (i >= _pathParams.size()) {
+ return emptyString;
+ }
+ auto it = _pathParams.begin();
+ std::advance(it, i);
+ return *it;
+}
+
+const String &AsyncWebServerRequest::header(const char *name) const {
+ const AsyncWebHeader *h = getHeader(name);
+ return h ? h->value() : emptyString;
+}
+
+#ifdef ESP8266
+const String &AsyncWebServerRequest::header(const __FlashStringHelper *data) const {
+ return header(String(data).c_str());
+};
+#endif
+
+const String &AsyncWebServerRequest::header(size_t i) const {
+ const AsyncWebHeader *h = getHeader(i);
+ return h ? h->value() : emptyString;
+}
+
+const String &AsyncWebServerRequest::headerName(size_t i) const {
+ const AsyncWebHeader *h = getHeader(i);
+ return h ? h->name() : emptyString;
+}
+
+String AsyncWebServerRequest::urlDecode(const String &text) const {
+ char temp[] = "0x00";
+ unsigned int len = text.length();
+ unsigned int i = 0;
+ String decoded;
+ // Allocate the string internal buffer - never longer from source text
+ if (!decoded.reserve(len)) {
+#ifdef ESP32
+ log_e("Failed to allocate");
+#endif
+ return emptyString;
+ }
+ while (i < len) {
+ char decodedChar;
+ char encodedChar = text.charAt(i++);
+ if ((encodedChar == '%') && (i + 1 < len)) {
+ temp[2] = text.charAt(i++);
+ temp[3] = text.charAt(i++);
+ decodedChar = strtol(temp, NULL, 16);
+ } else if (encodedChar == '+') {
+ decodedChar = ' ';
+ } else {
+ decodedChar = encodedChar; // normal ascii char
+ }
+ decoded.concat(decodedChar);
+ }
+ return decoded;
+}
+
+const char *AsyncWebServerRequest::methodToString() const {
+ if (_method == HTTP_ANY) {
+ return T_ANY;
+ }
+ if (_method & HTTP_GET) {
+ return T_GET;
+ }
+ if (_method & HTTP_POST) {
+ return T_POST;
+ }
+ if (_method & HTTP_DELETE) {
+ return T_DELETE;
+ }
+ if (_method & HTTP_PUT) {
+ return T_PUT;
+ }
+ if (_method & HTTP_PATCH) {
+ return T_PATCH;
+ }
+ if (_method & HTTP_HEAD) {
+ return T_HEAD;
+ }
+ if (_method & HTTP_OPTIONS) {
+ return T_OPTIONS;
+ }
+ return T_UNKNOWN;
+}
+
+const char *AsyncWebServerRequest::requestedConnTypeToString() const {
+ switch (_reqconntype) {
+ case RCT_NOT_USED: return T_RCT_NOT_USED;
+ case RCT_DEFAULT: return T_RCT_DEFAULT;
+ case RCT_HTTP: return T_RCT_HTTP;
+ case RCT_WS: return T_RCT_WS;
+ case RCT_EVENT: return T_RCT_EVENT;
+ default: return T_ERROR;
+ }
+}
+
+bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) const {
+ return ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) || ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype))
+ || ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype));
+}