Subversion Repositories ESP8266_P1_Meter

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 raymond 1
// SPDX-License-Identifier: LGPL-3.0-or-later
2
// Copyright 2016-2026 Hristo Gochkov, Mathieu Carbou, Emil Muratov, Will Miles
3
 
4
#include "ESPAsyncWebServer.h"
5
#include "WebAuthentication.h"
6
#include "WebResponseImpl.h"
7
#include "AsyncWebServerLogging.h"
8
 
9
#include <algorithm>
10
#include <cstring>
11
#include <memory>
12
#include <utility>
13
 
14
#include "./literals.h"
15
 
16
static inline bool isParamChar(char c) {
17
  return ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '='));
18
}
19
 
20
static void doNotDelete(AsyncWebServerRequest *) {}
21
 
22
using namespace asyncsrv;
23
 
24
enum {
25
  PARSE_REQ_START = 0,
26
  PARSE_REQ_HEADERS = 1,
27
  PARSE_REQ_BODY = 2,
28
  PARSE_REQ_END = 3,
29
  PARSE_REQ_FAIL = 4
30
};
31
 
32
enum {
33
  CHUNK_NONE = 0,   // Body transfer encoding is not chunked
34
  CHUNK_LENGTH,     // Getting chunk length - HHHH[;...] CR LF
35
  CHUNK_EXTENSION,  // Getting chunk extension - ;... CR LF
36
  CHUNK_DATA,       // Handling chunk data
37
  CHUNK_ERROR,      // Invalid chunk header
38
  CHUNK_END,        // Getting chunk end marker  - CR LF
39
};
40
 
41
AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer *s, AsyncClient *c)
42
  : _client(c), _server(s), _handler(NULL), _response(NULL), _onDisconnectfn(NULL), _temp(), _parseState(PARSE_REQ_START), _version(0),
43
    _method(AsyncWebRequestMethod::HTTP_UNKNOWN), _url(), _host(), _contentType(), _boundary(), _authorization(), _reqconntype(RCT_HTTP),
44
    _authMethod(AsyncAuthType::AUTH_NONE), _isMultipart(false), _isPlainPost(false), _expectingContinue(false), _contentLength(0), _parsedLength(0),
45
    _multiParseState(0), _boundaryPosition(0), _itemStartIndex(0), _itemSize(0), _itemName(), _itemFilename(), _itemType(), _itemValue(), _itemBuffer(0),
46
    _itemBufferIndex(0), _itemIsFile(false), _chunkStartIndex(0), _chunkOffset(0), _chunkSize(0), _chunkedParseState(CHUNK_NONE), _chunkedLastChar(0),
47
    _tempObject(NULL) {
48
  c->onError(
49
    [](void *r, AsyncClient *c, int8_t error) {
50
      (void)c;
51
      // async_ws_log_e("AsyncWebServerRequest::_onError");
52
      static_cast<AsyncWebServerRequest *>(r)->_onError(error);
53
    },
54
    this
55
  );
56
  c->onAck(
57
    [](void *r, AsyncClient *c, size_t len, uint32_t time) {
58
      (void)c;
59
      // async_ws_log_e("AsyncWebServerRequest::_onAck");
60
      static_cast<AsyncWebServerRequest *>(r)->_onAck(len, time);
61
    },
62
    this
63
  );
64
  c->onDisconnect(
65
    [](void *r, AsyncClient *c) {
66
      // async_ws_log_e("AsyncWebServerRequest::_onDisconnect");
67
      static_cast<AsyncWebServerRequest *>(r)->_onDisconnect();
68
    },
69
    this
70
  );
71
  c->onTimeout(
72
    [](void *r, AsyncClient *c, uint32_t time) {
73
      (void)c;
74
      // async_ws_log_e("AsyncWebServerRequest::_onTimeout");
75
      static_cast<AsyncWebServerRequest *>(r)->_onTimeout(time);
76
    },
77
    this
78
  );
79
  c->onData(
80
    [](void *r, AsyncClient *c, void *buf, size_t len) {
81
      (void)c;
82
      // async_ws_log_e("AsyncWebServerRequest::_onData");
83
      static_cast<AsyncWebServerRequest *>(r)->_onData(buf, len);
84
    },
85
    this
86
  );
87
  c->onPoll(
88
    [](void *r, AsyncClient *c) {
89
      (void)c;
90
      // async_ws_log_e("AsyncWebServerRequest::_onPoll");
91
      static_cast<AsyncWebServerRequest *>(r)->_onPoll();
92
    },
93
    this
94
  );
95
}
96
 
97
AsyncWebServerRequest::~AsyncWebServerRequest() {
98
  if (_client) {
99
    // usually it is _client's disconnect triggers object destruct, but for completeness we define behavior
100
    // if for some reason *this will be destructed while client is still connected
101
    _client->onDisconnect(nullptr);
102
    delete _client;
103
    _client = nullptr;
104
  }
105
 
106
  if (_response) {
107
    delete _response;
108
    _response = nullptr;
109
  }
110
 
111
  _this.reset();
112
 
113
  if (_tempObject != NULL) {
114
    free(_tempObject);
115
  }
116
 
117
  if (_tempFile) {
118
    _tempFile.close();
119
  }
120
 
121
  if (_itemBuffer) {
122
    free(_itemBuffer);
123
  }
124
}
125
 
126
void AsyncWebServerRequest::_onData(void *buf, size_t len) {
127
  // SSL/TLS handshake detection
128
#ifndef ASYNC_TCP_SSL_ENABLED
129
  if (_parseState == PARSE_REQ_START && len && ((uint8_t *)buf)[0] == 0x16) {  // 0x16 indicates a Handshake message (SSL/TLS).
130
    async_ws_log_d("SSL/TLS handshake detected: resetting connection");
131
    _parseState = PARSE_REQ_FAIL;
132
    abort();
133
    return;
134
  }
135
#endif
136
 
137
  size_t i = 0;
138
  while (true) {
139
 
140
    if (_parseState < PARSE_REQ_BODY) {
141
      // Find new line in buf
142
      char *str = (char *)buf;
143
      for (i = 0; i < len; i++) {
144
        // Check for null characters in header
145
        if (!str[i]) {
146
          _parseState = PARSE_REQ_FAIL;
147
          abort();
148
          return;
149
        }
150
        if (str[i] == '\n') {
151
          break;
152
        }
153
      }
154
      if (i == len) {  // No new line, just add the buffer in _temp
155
        char ch = str[len - 1];
156
        str[len - 1] = 0;
157
        if (!_temp.reserve(_temp.length() + len)) {
158
          async_ws_log_e("Failed to allocate");
159
          _parseState = PARSE_REQ_FAIL;
160
          abort();
161
          return;
162
        }
163
        _temp.concat(str);
164
        _temp.concat(ch);
165
      } else {       // Found new line - extract it and parse
166
        str[i] = 0;  // Terminate the string at the end of the line.
167
        _temp.concat(str);
168
        _temp.trim();
169
        _parseLine();
170
        if (++i < len) {
171
          // Still have more buffer to process
172
          buf = str + i;
173
          len -= i;
174
          continue;
175
        }
176
      }
177
    } else if (_parseState == PARSE_REQ_BODY) {
178
      if (_chunkedParseState != CHUNK_NONE) {
179
        if (_parseChunkedBytes((uint8_t *)buf, len)) {
180
          _parseState = PARSE_REQ_END;
181
          _runMiddlewareChain();
182
          _send();
183
        }
184
        break;
185
      }
186
      // A handler should be already attached at this point in _parseLine function.
187
      // If handler does nothing (_onRequest is NULL), we don't need to really parse the body.
188
      const bool needParse = _handler && !_handler->isRequestHandlerTrivial();
189
      // Discard any bytes after content length; handlers may overrun their buffers
190
      len = std::min(len, _contentLength - _parsedLength);
191
      if (_isMultipart) {
192
        if (needParse) {
193
          size_t i;
194
          for (i = 0; i < len; i++) {
195
            _parseMultipartPostByte(((uint8_t *)buf)[i], i == len - 1);
196
            _parsedLength++;
197
          }
198
        } else {
199
          _parsedLength += len;
200
        }
201
      } else {
202
        if (_parsedLength == 0) {
203
          if (_contentType.startsWith(T_app_xform_urlencoded)) {
204
            _isPlainPost = true;
205
          } else if (_contentType == T_text_plain && isParamChar(((char *)buf)[0])) {
206
            size_t i = 0;
207
            char ch;
208
            do {
209
              ch = ((char *)buf)[i];
210
            } while (i++ < len && isParamChar(ch));
211
            if (i < len && ((char *)buf)[i - 1] == '=') {
212
              _isPlainPost = true;
213
            }
214
          }
215
        }
216
        if (!_isPlainPost) {
217
          // ESP_LOGD("AsyncWebServer", "_isPlainPost: %d, _handler: %p", _isPlainPost, _handler);
218
          if (_handler) {
219
            _handler->handleBody(this, (uint8_t *)buf, len, _parsedLength, _contentLength);
220
          }
221
          _parsedLength += len;
222
        } else if (needParse) {
223
          size_t i;
224
          for (i = 0; i < len; i++) {
225
            _parsedLength++;
226
            _parsePlainPostChar(((uint8_t *)buf)[i]);
227
          }
228
        } else {
229
          _parsedLength += len;
230
        }
231
      }
232
      if (_parsedLength == _contentLength) {
233
        _parseState = PARSE_REQ_END;
234
        _runMiddlewareChain();
235
        _send();
236
      }
237
    }
238
    break;
239
  }
240
}
241
 
242
void AsyncWebServerRequest::_onPoll() {
243
  // os_printf("p\n");
244
  if (_response && _client && _client->canSend()) {
245
    _response->_ack(this, 0, 0);
246
  }
247
}
248
 
249
void AsyncWebServerRequest::_onAck(size_t len, uint32_t time) {
250
  // os_printf("a:%u:%u\n", len, time);
251
  if (!_response) {
252
    return;
253
  }
254
 
255
  if (!_response->_finished()) {
256
    _response->_ack(this, len, time);
257
    // recheck if response has just completed, close connection
258
    if (_response->_finished()) {
259
      _client->close();  // this will trigger _onDisconnect() and object destruction
260
    }
261
  } else {
262
    // this will close responses that were complete via a single _send() call
263
    _client->close();  // this will trigger _onDisconnect() and object destruction
264
  }
265
}
266
 
267
void AsyncWebServerRequest::_onError(int8_t error) {
268
  (void)error;
269
}
270
 
271
void AsyncWebServerRequest::_onTimeout(uint32_t time) {
272
  (void)time;
273
  // os_printf("TIMEOUT: %u, state: %s\n", time, _client->stateToString());
274
  _client->close();
275
}
276
 
277
void AsyncWebServerRequest::onDisconnect(ArDisconnectHandler fn) {
278
  _onDisconnectfn = fn;
279
}
280
 
281
void AsyncWebServerRequest::_onDisconnect() {
282
  // os_printf("d\n");
283
  if (_onDisconnectfn) {
284
    _onDisconnectfn();
285
  }
286
  _server->_handleDisconnect(this);
287
}
288
 
289
void AsyncWebServerRequest::_addGetParams(const String &params) {
290
  size_t start = 0;
291
  while (start < params.length()) {
292
    int end = params.indexOf('&', start);
293
    if (end < 0) {
294
      end = params.length();
295
    }
296
    int equal = params.indexOf('=', start);
297
    if (equal < 0 || equal > end) {
298
      equal = end;
299
    }
300
    String name = urlDecode(params.substring(start, equal));
301
    String value = urlDecode(equal + 1 < end ? params.substring(equal + 1, end) : asyncsrv::emptyString);
302
    if (name.length()) {
303
      _params.emplace_back(name, value);
304
    }
305
    start = end + 1;
306
  }
307
}
308
 
309
bool AsyncWebServerRequest::_parseReqHead() {
310
  // Split the head into method, url and version
311
  int index = _temp.indexOf(' ');
312
  String m = _temp.substring(0, index);
313
  index = _temp.indexOf(' ', index + 1);
314
  String u = _temp.substring(m.length() + 1, index);
315
  _temp = _temp.substring(index + 1);
316
 
317
  _method = asyncsrv::stringToMethod(m);
318
  if (_method == AsyncWebRequestMethod::HTTP_INVALID) {
319
    return false;
320
  }
321
 
322
  String g;
323
  index = u.indexOf('?');
324
  if (index > 0) {
325
    g = u.substring(index + 1);
326
    u = u.substring(0, index);
327
  }
328
  _url = urlDecode(u);
329
  _addGetParams(g);
330
 
331
  if (!_url.length()) {
332
    return false;
333
  }
334
 
335
  if (!_temp.startsWith(T_HTTP_1_0)) {
336
    _version = 1;
337
  }
338
 
339
  _temp = asyncsrv::emptyString;
340
  return true;
341
}
342
 
343
// Returns true when done
344
bool AsyncWebServerRequest::_parseChunkedBytes(uint8_t *buf, size_t len) {
345
  for (size_t i = 0; i < len;) {
346
    if (_chunkedParseState == CHUNK_DATA) {
347
      // In DATA state, we pass the bytes off to handleBody as a group
348
 
349
      // In order to avoid allocating an extra buffer, the data
350
      // blocks that we pass on do not necessarily correspond to
351
      // whole chunks.  We just send however much we already have,
352
      // anticipating that more will arrive later.  handleBody()
353
      // cannot assume that it receives entire chunks at once.
354
      // That should not be a problem because we do not attach
355
      // any semantic meaning to chunks.  That might change if
356
      // we were to support chunk extensions, but that seems
357
      // unlikely since RFC9112 suggests that they are only
358
      // useful for very specialized purposes.
359
      size_t curLen = std::min(_chunkSize - _chunkOffset, len - i);
360
 
361
      // On the final zero-length chunk, _chunkSize - _chunkOffset
362
      // will be zero, so we will call handleBody with a zero size,
363
      // marking the end of the data stream.
364
 
365
      if (_handler) {
366
        _handler->handleBody(this, buf + i, curLen, _chunkStartIndex, _contentLength);
367
      }
368
      _chunkOffset += curLen;
369
      _chunkStartIndex += curLen;
370
      i += curLen;
371
      if (_chunkOffset == _chunkSize) {
372
        _chunkedParseState = CHUNK_END;
373
      }
374
    } else {
375
      // In other states we process the bytes one by one
376
      uint8_t data = buf[i++];
377
 
378
      auto last_was_cr = _chunkedLastChar == '\r';
379
      _chunkedLastChar = data;
380
 
381
      if (_chunkedParseState == CHUNK_LENGTH) {
382
        // Incrementally decode a hex number
383
        if (data >= '0' && data <= '9') {
384
          if (_chunkSize >= 0x1000000) {
385
            _chunkedParseState = CHUNK_ERROR;
386
          } else {
387
            _chunkSize = (_chunkSize * 16) + (data - '0');
388
          }
389
        } else if (data >= 'A' && data <= 'F') {
390
          if (_chunkSize >= 0x1000000) {
391
            _chunkedParseState = CHUNK_ERROR;
392
          } else {
393
            _chunkSize = (_chunkSize * 16) + (data - 'A' + 10);
394
          }
395
        } else if (data >= 'a' && data <= 'f') {
396
          if (_chunkSize >= 0x1000000) {
397
            _chunkedParseState = CHUNK_ERROR;
398
          } else {
399
            _chunkSize = (_chunkSize * 16) + (data - 'a' + 10);
400
          }
401
        } else if (data == ';') {
402
          _chunkedParseState = CHUNK_EXTENSION;
403
        } else if (data == '\r') {
404
          // Wait for LF
405
        } else if (data == '\n') {
406
          if (last_was_cr) {
407
            _chunkOffset = 0;
408
            _chunkedParseState = CHUNK_DATA;
409
 
410
          } else {
411
            _chunkedParseState = CHUNK_ERROR;
412
          }
413
        } else {
414
          // Invalid hex character
415
          _chunkedParseState = CHUNK_ERROR;
416
        }
417
      } else if (_chunkedParseState == CHUNK_EXTENSION) {
418
        // Chunk extensions appear after a semicolon.
419
        // We ignore them because their use cases are
420
        // specialized and obscure.
421
        if (data == '\r') {
422
          // Wait for LF
423
        } else if (data == '\n') {
424
          if (last_was_cr) {
425
            _chunkOffset = 0;
426
            _chunkedParseState = CHUNK_DATA;
427
          } else {
428
            _chunkedParseState = CHUNK_ERROR;
429
          }
430
        }
431
      } else if (_chunkedParseState == CHUNK_END) {
432
        if (data == '\r') {
433
          // Wait for LF
434
        } else if (data == '\n') {
435
          if (last_was_cr) {
436
            // A zero length chunk marks the end of the chunk stream
437
            if (_chunkSize == 0) {
438
              // If we needed to support trailers, we would switch to
439
              // TRAILER state, but since we have no use case for them,
440
              // we just stop processing the body.
441
              return true;
442
            }
443
            _chunkSize = 0;
444
            _chunkedParseState = CHUNK_LENGTH;
445
          } else {
446
            _chunkedParseState = CHUNK_ERROR;
447
          }
448
        }
449
      }
450
 
451
      if (_chunkedParseState == CHUNK_ERROR) {
452
        // If there was an error when parsing the chunk length, the
453
        // rest of the data stream is unreliable.  Ideally we should
454
        // close the connection, but that risks leaving things dangling
455
        // (e.g. an open file), so it is probably best to just ignore
456
        // the rest of the data and give handleRequest a chance to
457
        // clean up.
458
        _chunkSize = 0;
459
        abort();
460
        return true;
461
      }
462
    }
463
  }
464
  return false;
465
}
466
 
467
bool AsyncWebServerRequest::_parseReqHeader() {
468
  AsyncWebHeader header = AsyncWebHeader::parse(_temp);
469
  if (header) {
470
    const String &name = header.name();
471
    const String &value = header.value();
472
    if (name.equalsIgnoreCase(T_Host)) {
473
      _host = value;
474
    } else if (name.equalsIgnoreCase(T_Content_Type)) {
475
      _contentType = value.substring(0, value.indexOf(';'));
476
      if (value.startsWith(T_MULTIPART_)) {
477
        _boundary = value.substring(value.indexOf('=') + 1);
478
        _boundary.replace(String('"'), String());
479
        _isMultipart = true;
480
      }
481
    } else if (name.equalsIgnoreCase(T_Content_Length) || name.equalsIgnoreCase(T_X_Expected_Entity_Length)) {
482
      // MacOS WebDAVFS uses X-Expected-Entity-Length to indicate the
483
      // total length of a chunked request body.  It is useful to
484
      // determine if a PUT can possibly fit in the available space.
485
      _contentLength = atoi(value.c_str());
486
    } else if (name.equalsIgnoreCase(T_EXPECT) && value.equalsIgnoreCase(T_100_CONTINUE)) {
487
      _expectingContinue = true;
488
    } else if (name.equalsIgnoreCase(T_AUTH)) {
489
      int space = value.indexOf(' ');
490
      if (space == -1) {
491
        _authorization = value;
492
        _authMethod = AsyncAuthType::AUTH_OTHER;
493
      } else {
494
        String method = value.substring(0, space);
495
        if (method.equalsIgnoreCase(T_BASIC)) {
496
          _authMethod = AsyncAuthType::AUTH_BASIC;
497
        } else if (method.equalsIgnoreCase(T_DIGEST)) {
498
          _authMethod = AsyncAuthType::AUTH_DIGEST;
499
        } else if (method.equalsIgnoreCase(T_BEARER)) {
500
          _authMethod = AsyncAuthType::AUTH_BEARER;
501
        } else {
502
          _authMethod = AsyncAuthType::AUTH_OTHER;
503
        }
504
        _authorization = value.substring(space + 1);
505
      }
506
    } else if (name.equalsIgnoreCase(T_UPGRADE) && value.equalsIgnoreCase(T_WS)) {
507
      // WebSocket request can be uniquely identified by header: [Upgrade: websocket]
508
      _reqconntype = RCT_WS;
509
    } else if (name.equalsIgnoreCase(T_ACCEPT)) {
510
      String lowcase(value);
511
      lowcase.toLowerCase();
512
#ifndef ESP8266
513
      const char *substr = std::strstr(lowcase.c_str(), T_text_event_stream);
514
#else
515
      const char *substr = std::strstr(lowcase.c_str(), String(T_text_event_stream).c_str());
516
#endif
517
      if (substr != NULL) {
518
        // WebEvent request can be uniquely identified by header:  [Accept: text/event-stream]
519
        _reqconntype = RCT_EVENT;
520
      }
521
    } else if (name.equalsIgnoreCase(T_Transfer_Encoding)) {
522
      String lowcase(value);
523
      lowcase.toLowerCase();
524
      String key;
525
 
526
      while (lowcase.length()) {
527
        auto pos = lowcase.indexOf(',');
528
        if (pos >= 0) {
529
          key = lowcase.substring(0, pos);
530
          lowcase = lowcase.substring(pos + 1);
531
        } else {
532
          key = lowcase;
533
          lowcase = "";
534
        }
535
        key.trim();
536
        if (key == "chunked") {
537
          _chunkSize = 0;
538
          _chunkStartIndex = 0;
539
          _chunkedParseState = CHUNK_LENGTH;
540
          break;
541
        }
542
      }
543
    }
544
    _headers.emplace_back(std::move(header));
545
  }
546
#if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) || defined(LIBRETINY) || defined(HOST)
547
  // ArduinoCore-API does not have String::clear() method 8-()
548
  _temp = asyncsrv::emptyString;
549
#else
550
  _temp.clear();
551
#endif
552
  return true;
553
}
554
 
555
void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data) {
556
  if (data && (char)data != '&') {
557
    _temp += (char)data;
558
  }
559
  if (!data || (char)data == '&' || _parsedLength == _contentLength) {
560
    String name(T_BODY);
561
    String value(_temp);
562
    if (!(_temp.charAt(0) == '{') && !(_temp.charAt(0) == '[') && _temp.indexOf('=') > 0) {
563
      name = _temp.substring(0, _temp.indexOf('='));
564
      value = _temp.substring(_temp.indexOf('=') + 1);
565
    }
566
    name = urlDecode(name);
567
    if (name.length()) {
568
      _params.emplace_back(name, urlDecode(value), true);
569
    }
570
 
571
#if defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) || defined(LIBRETINY) || defined(HOST)
572
    // ArduinoCore-API does not have String::clear() method 8-()
573
    _temp = asyncsrv::emptyString;
574
#else
575
    _temp.clear();
576
#endif
577
  }
578
}
579
 
580
void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last) {
581
  _itemBuffer[_itemBufferIndex++] = data;
582
 
583
  if (last || _itemBufferIndex == RESPONSE_STREAM_BUFFER_SIZE) {
584
    // check if authenticated before calling the upload
585
    if (_handler) {
586
      _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false);
587
    }
588
    _itemBufferIndex = 0;
589
  }
590
}
591
 
592
enum {
593
  EXPECT_BOUNDARY,
594
  PARSE_HEADERS,
595
  WAIT_FOR_RETURN1,
596
  EXPECT_FEED1,
597
  EXPECT_DASH1,
598
  EXPECT_DASH2,
599
  BOUNDARY_OR_DATA,
600
  DASH3_OR_RETURN2,
601
  EXPECT_FEED2,
602
  PARSING_FINISHED,
603
  PARSE_ERROR
604
};
605
 
606
void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) {
607
#define itemWriteByte(b)          \
608
  do {                            \
609
    _itemSize++;                  \
610
    if (_itemIsFile)              \
611
      _handleUploadByte(b, last); \
612
    else                          \
613
      _itemValue += (char)(b);    \
614
  } while (0)
615
 
616
  if (!_parsedLength) {
617
    _multiParseState = EXPECT_BOUNDARY;
618
    _temp = asyncsrv::emptyString;
619
    _itemName = asyncsrv::emptyString;
620
    _itemFilename = asyncsrv::emptyString;
621
    _itemType = asyncsrv::emptyString;
622
  }
623
 
624
  if (_multiParseState == WAIT_FOR_RETURN1) {
625
    if (data != '\r') {
626
      itemWriteByte(data);
627
    } else {
628
      _multiParseState = EXPECT_FEED1;
629
    }
630
  } else if (_multiParseState == EXPECT_BOUNDARY) {
631
    if (_parsedLength < 2 && data != '-') {
632
      _multiParseState = PARSE_ERROR;
633
      return;
634
    } else if (_parsedLength - 2 < _boundary.length() && _boundary.c_str()[_parsedLength - 2] != data) {
635
      _multiParseState = PARSE_ERROR;
636
      return;
637
    } else if (_parsedLength - 2 == _boundary.length() && data != '\r') {
638
      _multiParseState = PARSE_ERROR;
639
      return;
640
    } else if (_parsedLength - 3 == _boundary.length()) {
641
      if (data != '\n') {
642
        _multiParseState = PARSE_ERROR;
643
        return;
644
      }
645
      _multiParseState = PARSE_HEADERS;
646
      _itemIsFile = false;
647
    }
648
  } else if (_multiParseState == PARSE_HEADERS) {
649
    if ((char)data != '\r' && (char)data != '\n') {
650
      _temp += (char)data;
651
    }
652
    if ((char)data == '\n') {
653
      if (_temp.length()) {
654
        if (_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase(T_Content_Type)) {
655
          _itemType = _temp.substring(14);
656
          _itemIsFile = true;
657
        } else if (_temp.length() > 19 && _temp.substring(0, 19).equalsIgnoreCase(T_Content_Disposition)) {
658
          _temp = _temp.substring(_temp.indexOf(';') + 2);
659
          while (_temp.indexOf(';') > 0) {
660
            String name = _temp.substring(0, _temp.indexOf('='));
661
            String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.indexOf(';') - 1);
662
            if (name == T_name) {
663
              _itemName = nameVal;
664
            } else if (name == T_filename) {
665
              _itemFilename = nameVal;
666
              _itemIsFile = true;
667
            }
668
            _temp = _temp.substring(_temp.indexOf(';') + 2);
669
          }
670
          String name = _temp.substring(0, _temp.indexOf('='));
671
          String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.length() - 1);
672
          if (name == T_name) {
673
            _itemName = nameVal;
674
          } else if (name == T_filename) {
675
            _itemFilename = nameVal;
676
            _itemIsFile = true;
677
          }
678
          // Add the parameters from the content-disposition header to the param list, flagged as POST and File,
679
          // so that they can be retrieved using getParam(name, isPost=true, isFile=true)
680
          // in the upload handler to correctly handle multiple file uploads within the same request.
681
          // Example: Content-Disposition: form-data; name="fw"; filename="firmware.bin"
682
          // See: https://github.com/ESP32Async/ESPAsyncWebServer/discussions/328
683
          if (_itemIsFile && _itemName.length() && _itemFilename.length()) {
684
            // add new parameters for this content-disposition
685
            _params.emplace_back(T_name, _itemName, true, true);
686
            _params.emplace_back(T_filename, _itemFilename, true, true);
687
          }
688
        }
689
        _temp = asyncsrv::emptyString;
690
      } else {
691
        _multiParseState = WAIT_FOR_RETURN1;
692
        // value starts from here
693
        _itemSize = 0;
694
        _itemStartIndex = _parsedLength;
695
        _itemValue = asyncsrv::emptyString;
696
        if (_itemIsFile) {
697
          if (_itemBuffer) {
698
            free(_itemBuffer);
699
          }
700
          _itemBuffer = (uint8_t *)malloc(RESPONSE_STREAM_BUFFER_SIZE);
701
          if (_itemBuffer == NULL) {
702
            async_ws_log_e("Failed to allocate");
703
            _multiParseState = PARSE_ERROR;
704
            abort();
705
            return;
706
          }
707
          _itemBufferIndex = 0;
708
        }
709
      }
710
    }
711
  } else if (_multiParseState == EXPECT_FEED1) {
712
    if (data != '\n') {
713
      _multiParseState = WAIT_FOR_RETURN1;
714
      itemWriteByte('\r');
715
      _parseMultipartPostByte(data, last);
716
    } else {
717
      _multiParseState = EXPECT_DASH1;
718
    }
719
  } else if (_multiParseState == EXPECT_DASH1) {
720
    if (data != '-') {
721
      _multiParseState = WAIT_FOR_RETURN1;
722
      itemWriteByte('\r');
723
      itemWriteByte('\n');
724
      _parseMultipartPostByte(data, last);
725
    } else {
726
      _multiParseState = EXPECT_DASH2;
727
    }
728
  } else if (_multiParseState == EXPECT_DASH2) {
729
    if (data != '-') {
730
      _multiParseState = WAIT_FOR_RETURN1;
731
      itemWriteByte('\r');
732
      itemWriteByte('\n');
733
      itemWriteByte('-');
734
      _parseMultipartPostByte(data, last);
735
    } else {
736
      _multiParseState = BOUNDARY_OR_DATA;
737
      _boundaryPosition = 0;
738
    }
739
  } else if (_multiParseState == BOUNDARY_OR_DATA) {
740
    if (_boundaryPosition < _boundary.length() && _boundary.c_str()[_boundaryPosition] != data) {
741
      _multiParseState = WAIT_FOR_RETURN1;
742
      itemWriteByte('\r');
743
      itemWriteByte('\n');
744
      itemWriteByte('-');
745
      itemWriteByte('-');
746
      uint8_t i;
747
      for (i = 0; i < _boundaryPosition; i++) {
748
        itemWriteByte(_boundary.c_str()[i]);
749
      }
750
      _parseMultipartPostByte(data, last);
751
    } else if (_boundaryPosition == _boundary.length() - 1) {
752
      _multiParseState = DASH3_OR_RETURN2;
753
      if (!_itemIsFile) {
754
        _params.emplace_back(_itemName, _itemValue, true);
755
      } else {
756
        if (_handler) {
757
          _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true);
758
        }
759
        _itemBufferIndex = 0;
760
        _params.emplace_back(_itemName, _itemFilename, true, true, _itemSize);
761
        // remove previous occurrence(s) of content-disposition parameters for this upload
762
        _params.remove_if([this](const AsyncWebParameter &p) {
763
          return p.isPost() && p.isFile() && (p.name() == T_name || p.name() == T_filename);
764
        });
765
        free(_itemBuffer);
766
        _itemBuffer = NULL;
767
      }
768
 
769
    } else {
770
      _boundaryPosition++;
771
    }
772
  } else if (_multiParseState == DASH3_OR_RETURN2) {
773
    if (data == '-' && (_contentLength - _parsedLength - 4) != 0) {
774
      // 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);
775
      _contentLength = _parsedLength + 4;  // lets close the request gracefully
776
    }
777
    if (data == '\r') {
778
      _multiParseState = EXPECT_FEED2;
779
    } else if (data == '-' && _contentLength == (_parsedLength + 4)) {
780
      _multiParseState = PARSING_FINISHED;
781
    } else {
782
      _multiParseState = WAIT_FOR_RETURN1;
783
      itemWriteByte('\r');
784
      itemWriteByte('\n');
785
      itemWriteByte('-');
786
      itemWriteByte('-');
787
      uint8_t i;
788
      for (i = 0; i < _boundary.length(); i++) {
789
        itemWriteByte(_boundary.c_str()[i]);
790
      }
791
      _parseMultipartPostByte(data, last);
792
    }
793
  } else if (_multiParseState == EXPECT_FEED2) {
794
    if (data == '\n') {
795
      _multiParseState = PARSE_HEADERS;
796
      _itemIsFile = false;
797
    } else {
798
      _multiParseState = WAIT_FOR_RETURN1;
799
      itemWriteByte('\r');
800
      itemWriteByte('\n');
801
      itemWriteByte('-');
802
      itemWriteByte('-');
803
      uint8_t i;
804
      for (i = 0; i < _boundary.length(); i++) {
805
        itemWriteByte(_boundary.c_str()[i]);
806
      }
807
      itemWriteByte('\r');
808
      _parseMultipartPostByte(data, last);
809
    }
810
  }
811
}
812
 
813
void AsyncWebServerRequest::_parseLine() {
814
  if (_parseState == PARSE_REQ_START) {
815
    if (!_temp.length()) {
816
      _parseState = PARSE_REQ_FAIL;
817
      abort();
818
    } else {
819
      if (_parseReqHead()) {
820
        _parseState = PARSE_REQ_HEADERS;
821
      } else {
822
        _parseState = PARSE_REQ_FAIL;
823
        abort();
824
      }
825
    }
826
    return;
827
  }
828
 
829
  if (_parseState == PARSE_REQ_HEADERS) {
830
    if (!_temp.length()) {
831
      // end of headers
832
      _server->_rewriteRequest(this);
833
      _server->_attachHandler(this);
834
      if (_expectingContinue) {
835
        String response(T_HTTP_100_CONT);
836
        _client->write(response.c_str(), response.length());
837
      }
838
      if (_contentLength || _chunkedParseState != CHUNK_NONE) {
839
        _parseState = PARSE_REQ_BODY;
840
      } else {
841
        _parseState = PARSE_REQ_END;
842
        _runMiddlewareChain();
843
        _send();
844
      }
845
    } else {
846
      _parseReqHeader();
847
    }
848
  }
849
}
850
 
851
void AsyncWebServerRequest::_runMiddlewareChain() {
852
  if (_handler && _handler->mustSkipServerMiddlewares()) {
853
    _handler->_runChain(this, [this]() {
854
      _handler->handleRequest(this);
855
    });
856
  } else {
857
    _server->_runChain(this, [this]() {
858
      if (_handler) {
859
        _handler->_runChain(this, [this]() {
860
          _handler->handleRequest(this);
861
        });
862
      }
863
    });
864
  }
865
}
866
 
867
void AsyncWebServerRequest::_send() {
868
  if (!_sent && !_paused) {
869
    // async_ws_log_d("AsyncWebServerRequest::_send()");
870
 
871
    // user did not create a response ?
872
    if (!_response) {
873
      send(501, T_text_plain, "Handler did not handle the request");
874
    }
875
 
876
    // response is not valid ?
877
    if (!_response->_sourceValid()) {
878
      send(500, T_text_plain, "Invalid data in handler");
879
    }
880
 
881
    // here, we either have a response given from user or one of the two above
882
    _client->setRxTimeout(0);
883
    _response->_respond(this);
884
    _sent = true;
885
  }
886
}
887
 
888
AsyncWebServerRequestPtr AsyncWebServerRequest::pause() {
889
  if (_paused) {
890
    return _this;
891
  }
892
  client()->setRxTimeout(0);
893
  // this shared ptr will hold the request pointer until it gets destroyed following a disconnect.
894
  // this is just used as a holder providing weak observers, so the deleter is a no-op.
895
  _this = std::shared_ptr<AsyncWebServerRequest>(this, doNotDelete);
896
  _paused = true;
897
  return _this;
898
}
899
 
900
void AsyncWebServerRequest::abort() {
901
  if (!_sent) {
902
    _sent = true;
903
    _paused = false;
904
    _this.reset();
905
    // async_ws_log_e("AsyncWebServerRequest::abort");
906
    _client->abort();
907
  }
908
}
909
 
910
size_t AsyncWebServerRequest::headers() const {
911
  return _headers.size();
912
}
913
 
914
bool AsyncWebServerRequest::hasHeader(const char *name) const {
915
  for (const auto &h : _headers) {
916
    if (h.name().equalsIgnoreCase(name)) {
917
      return true;
918
    }
919
  }
920
  return false;
921
}
922
 
923
#ifdef ESP8266
924
bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper *data) const {
925
  return hasHeader(String(data));
926
}
927
#endif
928
 
929
const AsyncWebHeader *AsyncWebServerRequest::getHeader(const char *name) const {
930
  auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader &header) {
931
    return header.name().equalsIgnoreCase(name);
932
  });
933
  return (iter == std::end(_headers)) ? nullptr : &(*iter);
934
}
935
 
936
#ifdef ESP8266
937
const AsyncWebHeader *AsyncWebServerRequest::getHeader(const __FlashStringHelper *data) const {
938
  PGM_P p = reinterpret_cast<PGM_P>(data);
939
  size_t n = strlen_P(p);
940
  char *name = (char *)malloc(n + 1);
941
  if (name) {
942
    strcpy_P(name, p);
943
    const AsyncWebHeader *result = getHeader(String(name));
944
    free(name);
945
    return result;
946
  } else {
947
    return nullptr;
948
  }
949
}
950
#endif
951
 
952
const AsyncWebHeader *AsyncWebServerRequest::getHeader(size_t num) const {
953
  if (num >= _headers.size()) {
954
    return nullptr;
955
  }
956
  return &(*std::next(_headers.cbegin(), num));
957
}
958
 
959
size_t AsyncWebServerRequest::getHeaderNames(std::vector<const char *> &names) const {
960
  const size_t size = names.size();
961
  for (const auto &h : _headers) {
962
    names.push_back(h.name().c_str());
963
  }
964
  return names.size() - size;
965
}
966
 
967
bool AsyncWebServerRequest::removeHeader(const char *name) {
968
  const size_t size = _headers.size();
969
  _headers.remove_if([name](const AsyncWebHeader &header) {
970
    return header.name().equalsIgnoreCase(name);
971
  });
972
  return size != _headers.size();
973
}
974
 
975
size_t AsyncWebServerRequest::params() const {
976
  return _params.size();
977
}
978
 
979
bool AsyncWebServerRequest::hasParam(const char *name, bool post, bool file) const {
980
  for (const auto &p : _params) {
981
    if (p.name().equals(name) && p.isPost() == post && p.isFile() == file) {
982
      return true;
983
    }
984
  }
985
  return false;
986
}
987
 
988
const AsyncWebParameter *AsyncWebServerRequest::getParam(const char *name, bool post, bool file) const {
989
  for (const auto &p : _params) {
990
    if (p.name() == name && p.isPost() == post && p.isFile() == file) {
991
      return &p;
992
    }
993
  }
994
  return nullptr;
995
}
996
 
997
#ifdef ESP8266
998
const AsyncWebParameter *AsyncWebServerRequest::getParam(const __FlashStringHelper *data, bool post, bool file) const {
999
  return getParam(String(data), post, file);
1000
}
1001
#endif
1002
 
1003
const AsyncWebParameter *AsyncWebServerRequest::getParam(size_t num) const {
1004
  if (num >= _params.size()) {
1005
    return nullptr;
1006
  }
1007
  return &(*std::next(_params.cbegin(), num));
1008
}
1009
 
1010
const String &AsyncWebServerRequest::getAttribute(const char *name, const String &defaultValue) const {
1011
  auto it = _attributes.find(name);
1012
  return it != _attributes.end() ? it->second : defaultValue;
1013
}
1014
bool AsyncWebServerRequest::getAttribute(const char *name, bool defaultValue) const {
1015
  auto it = _attributes.find(name);
1016
  return it != _attributes.end() ? it->second == "1" : defaultValue;
1017
}
1018
long AsyncWebServerRequest::getAttribute(const char *name, long defaultValue) const {
1019
  auto it = _attributes.find(name);
1020
  return it != _attributes.end() ? it->second.toInt() : defaultValue;
1021
}
1022
float AsyncWebServerRequest::getAttribute(const char *name, float defaultValue) const {
1023
  auto it = _attributes.find(name);
1024
  return it != _attributes.end() ? it->second.toFloat() : defaultValue;
1025
}
1026
double AsyncWebServerRequest::getAttribute(const char *name, double defaultValue) const {
1027
  auto it = _attributes.find(name);
1028
  return it != _attributes.end() ? it->second.toDouble() : defaultValue;
1029
}
1030
 
1031
AsyncWebServerResponse *AsyncWebServerRequest::beginResponse(int code, const char *contentType, const char *content, AwsTemplateProcessor callback) {
1032
  if (callback) {
1033
    return new AsyncProgmemResponse(code, contentType, (const uint8_t *)content, strlen(content), callback);
1034
  }
1035
  return new AsyncBasicResponse(code, contentType, content);
1036
}
1037
 
1038
AsyncWebServerResponse *
1039
  AsyncWebServerRequest::beginResponse(int code, const char *contentType, const uint8_t *content, size_t len, AwsTemplateProcessor callback) {
1040
  return new AsyncProgmemResponse(code, contentType, content, len, callback);
1041
}
1042
 
1043
AsyncWebServerResponse *
1044
  AsyncWebServerRequest::beginResponse(FS &fs, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback) {
1045
  if (fs.exists(path) || (!download && fs.exists(path + T__gz))) {
1046
    return new AsyncFileResponse(fs, path, contentType, download, callback);
1047
  }
1048
  return NULL;
1049
}
1050
 
1051
AsyncWebServerResponse *
1052
  AsyncWebServerRequest::beginResponse(File content, const String &path, const char *contentType, bool download, AwsTemplateProcessor callback) {
1053
  if (content == true) {
1054
    return new AsyncFileResponse(content, path, contentType, download, callback);
1055
  }
1056
  return NULL;
1057
}
1058
 
1059
AsyncWebServerResponse *AsyncWebServerRequest::beginResponse(Stream &stream, const char *contentType, size_t len, AwsTemplateProcessor callback) {
1060
  return new AsyncStreamResponse(stream, contentType, len, callback);
1061
}
1062
 
1063
AsyncWebServerResponse *
1064
  AsyncWebServerRequest::beginResponse(const char *contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) {
1065
  return new AsyncCallbackResponse(contentType, len, callback, templateCallback);
1066
}
1067
 
1068
AsyncWebServerResponse *
1069
  AsyncWebServerRequest::beginChunkedResponse(const char *contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) {
1070
  if (_version) {
1071
    return new AsyncChunkedResponse(contentType, callback, templateCallback);
1072
  }
1073
  return new AsyncCallbackResponse(contentType, 0, callback, templateCallback);
1074
}
1075
 
1076
AsyncResponseStream *AsyncWebServerRequest::beginResponseStream(const char *contentType, size_t bufferSize) {
1077
  return new AsyncResponseStream(contentType, bufferSize);
1078
}
1079
 
1080
AsyncWebServerResponse *AsyncWebServerRequest::beginResponse_P(int code, const String &contentType, PGM_P content, AwsTemplateProcessor callback) {
1081
  return new AsyncProgmemResponse(code, contentType, (const uint8_t *)content, strlen_P(content), callback);
1082
}
1083
 
1084
void AsyncWebServerRequest::send(AsyncWebServerResponse *response) {
1085
  // request is already sent on the wire ?
1086
  if (_sent) {
1087
    return;
1088
  }
1089
 
1090
  // if we already had a response, delete it and replace it with the new one
1091
  if (_response) {
1092
    delete _response;
1093
  }
1094
  _response = response;
1095
 
1096
  // if request was paused, we need to send the response now
1097
  if (_paused) {
1098
    _paused = false;
1099
    _send();
1100
  }
1101
}
1102
 
1103
void AsyncWebServerRequest::redirect(const char *url, int code) {
1104
  AsyncWebServerResponse *response = beginResponse(code);
1105
  response->addHeader(T_LOCATION, url);
1106
  send(response);
1107
}
1108
 
1109
bool AsyncWebServerRequest::authenticate(const char *username, const char *password, const char *realm, bool passwordIsHash) const {
1110
  if (_authorization.length()) {
1111
    if (_authMethod == AsyncAuthType::AUTH_DIGEST) {
1112
      return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL);
1113
    } else if (!passwordIsHash) {
1114
      return checkBasicAuthentication(_authorization.c_str(), username, password);
1115
    } else {
1116
      return _authorization.equals(password);
1117
    }
1118
  }
1119
  return false;
1120
}
1121
 
1122
bool AsyncWebServerRequest::authenticate(const char *hash) const {
1123
  if (!_authorization.length() || hash == NULL) {
1124
    return false;
1125
  }
1126
 
1127
  if (_authMethod == AsyncAuthType::AUTH_DIGEST) {
1128
    String hStr = String(hash);
1129
    int separator = hStr.indexOf(':');
1130
    if (separator <= 0) {
1131
      return false;
1132
    }
1133
    String username = hStr.substring(0, separator);
1134
    hStr = hStr.substring(separator + 1);
1135
    separator = hStr.indexOf(':');
1136
    if (separator <= 0) {
1137
      return false;
1138
    }
1139
    String realm = hStr.substring(0, separator);
1140
    hStr = hStr.substring(separator + 1);
1141
    return checkDigestAuthentication(_authorization.c_str(), methodToString(), username.c_str(), hStr.c_str(), realm.c_str(), true, NULL, NULL, NULL);
1142
  }
1143
 
1144
  // Basic Auth, Bearer Auth, or other
1145
  return (_authorization.equals(hash));
1146
}
1147
 
1148
void AsyncWebServerRequest::requestAuthentication(AsyncAuthType method, const char *realm, const char *_authFailMsg) {
1149
  if (!realm) {
1150
    realm = T_LOGIN_REQ;
1151
  }
1152
 
1153
  AsyncWebServerResponse *r = _authFailMsg ? beginResponse(401, T_text_html, _authFailMsg) : beginResponse(401);
1154
 
1155
  switch (method) {
1156
    case AsyncAuthType::AUTH_BASIC:
1157
    {
1158
      String header;
1159
      if (header.reserve(sizeof(T_BASIC_REALM) - 1 + strlen(realm) + 1)) {
1160
        header.concat(T_BASIC_REALM);
1161
        header.concat(realm);
1162
        header.concat('"');
1163
        r->addHeader(T_WWW_AUTH, header.c_str());
1164
      } else {
1165
        async_ws_log_e("Failed to allocate");
1166
        abort();
1167
      }
1168
 
1169
      break;
1170
    }
1171
    case AsyncAuthType::AUTH_DIGEST:
1172
    {
1173
      size_t len = sizeof(T_DIGEST_) - 1 + sizeof(T_realm__) - 1 + sizeof(T_auth_nonce) - 1 + 32 + sizeof(T__opaque) - 1 + 32 + 1;
1174
      String header;
1175
      if (header.reserve(len + strlen(realm))) {
1176
        const String nonce = genRandomMD5();
1177
        const String opaque = genRandomMD5();
1178
        if (nonce.length() && opaque.length()) {
1179
          header.concat(T_DIGEST_);
1180
          header.concat(T_realm__);
1181
          header.concat(realm);
1182
          header.concat(T_auth_nonce);
1183
          header.concat(nonce);
1184
          header.concat(T__opaque);
1185
          header.concat(opaque);
1186
          header.concat((char)0x22);  // '"'
1187
          r->addHeader(T_WWW_AUTH, header.c_str());
1188
        } else {
1189
          async_ws_log_e("Failed to allocate");
1190
          abort();
1191
        }
1192
      }
1193
      break;
1194
    }
1195
    default: break;
1196
  }
1197
 
1198
  send(r);
1199
}
1200
 
1201
bool AsyncWebServerRequest::hasArg(const char *name) const {
1202
  for (const auto &arg : _params) {
1203
    if (arg.name() == name) {
1204
      return true;
1205
    }
1206
  }
1207
  return false;
1208
}
1209
 
1210
#ifdef ESP8266
1211
bool AsyncWebServerRequest::hasArg(const __FlashStringHelper *data) const {
1212
  return hasArg(String(data).c_str());
1213
}
1214
#endif
1215
 
1216
const String &AsyncWebServerRequest::arg(const char *name) const {
1217
  for (const auto &arg : _params) {
1218
    if (arg.name() == name) {
1219
      return arg.value();
1220
    }
1221
  }
1222
  return asyncsrv::emptyString;
1223
}
1224
 
1225
#ifdef ESP8266
1226
const String &AsyncWebServerRequest::arg(const __FlashStringHelper *data) const {
1227
  return arg(String(data).c_str());
1228
}
1229
#endif
1230
 
1231
const String &AsyncWebServerRequest::arg(size_t i) const {
1232
  return getParam(i)->value();
1233
}
1234
 
1235
const String &AsyncWebServerRequest::argName(size_t i) const {
1236
  return getParam(i)->name();
1237
}
1238
 
1239
const String &AsyncWebServerRequest::header(const char *name) const {
1240
  const AsyncWebHeader *h = getHeader(name);
1241
  return h ? h->value() : asyncsrv::emptyString;
1242
}
1243
 
1244
#ifdef ESP8266
1245
const String &AsyncWebServerRequest::header(const __FlashStringHelper *data) const {
1246
  return header(String(data).c_str());
1247
};
1248
#endif
1249
 
1250
const String &AsyncWebServerRequest::header(size_t i) const {
1251
  const AsyncWebHeader *h = getHeader(i);
1252
  return h ? h->value() : asyncsrv::emptyString;
1253
}
1254
 
1255
const String &AsyncWebServerRequest::headerName(size_t i) const {
1256
  const AsyncWebHeader *h = getHeader(i);
1257
  return h ? h->name() : asyncsrv::emptyString;
1258
}
1259
 
1260
String AsyncWebServerRequest::urlDecode(const String &text) const {
1261
  char temp[] = "0x00";
1262
  unsigned int len = text.length();
1263
  unsigned int i = 0;
1264
  String decoded;
1265
  // Allocate the string internal buffer - never longer from source text
1266
  if (!decoded.reserve(len)) {
1267
    async_ws_log_e("Failed to allocate");
1268
    return asyncsrv::emptyString;
1269
  }
1270
  while (i < len) {
1271
    char decodedChar;
1272
    char encodedChar = text.charAt(i++);
1273
    if ((encodedChar == '%') && (i + 1 < len)) {
1274
      temp[2] = text.charAt(i++);
1275
      temp[3] = text.charAt(i++);
1276
      decodedChar = strtol(temp, NULL, 16);
1277
    } else if (encodedChar == '+') {
1278
      decodedChar = ' ';
1279
    } else {
1280
      decodedChar = encodedChar;  // normal ascii char
1281
    }
1282
    decoded.concat(decodedChar);
1283
  }
1284
  return decoded;
1285
}
1286
 
1287
const char *AsyncWebServerRequest::requestedConnTypeToString() const {
1288
  switch (_reqconntype) {
1289
    case RCT_NOT_USED: return T_RCT_NOT_USED;
1290
    case RCT_DEFAULT:  return T_RCT_DEFAULT;
1291
    case RCT_HTTP:     return T_RCT_HTTP;
1292
    case RCT_WS:       return T_RCT_WS;
1293
    case RCT_EVENT:    return T_RCT_EVENT;
1294
    default:           return T_ERROR;
1295
  }
1296
}
1297
 
1298
bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) const {
1299
  return ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) || ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype))
1300
         || ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype));
1301
}
1302
 
1303
AsyncClient *AsyncWebServerRequest::clientRelease() {
1304
  AsyncClient *c = _client;
1305
  _client = nullptr;
1306
  return c;
1307
}
1308
 
1309
namespace asyncsrv {
1310
// WebRequestMethod conversions
1311
WebRequestMethod stringToMethod(const String &m) {
1312
  if (m == T_GET) {
1313
    return AsyncWebRequestMethod::HTTP_GET;
1314
  } else if (m == T_POST) {
1315
    return AsyncWebRequestMethod::HTTP_POST;
1316
  } else if (m == T_DELETE) {
1317
    return AsyncWebRequestMethod::HTTP_DELETE;
1318
  } else if (m == T_PUT) {
1319
    return AsyncWebRequestMethod::HTTP_PUT;
1320
  } else if (m == T_PATCH) {
1321
    return AsyncWebRequestMethod::HTTP_PATCH;
1322
  } else if (m == T_HEAD) {
1323
    return AsyncWebRequestMethod::HTTP_HEAD;
1324
  } else if (m == T_OPTIONS) {
1325
    return AsyncWebRequestMethod::HTTP_OPTIONS;
1326
  } else if (m == T_TRACE) {
1327
    return AsyncWebRequestMethod::HTTP_TRACE;
1328
  } else if (m == T_CONNECT) {
1329
    return AsyncWebRequestMethod::HTTP_CONNECT;
1330
  } else if (m == T_PURGE) {
1331
    return AsyncWebRequestMethod::HTTP_PURGE;
1332
  } else if (m == T_LINK) {
1333
    return AsyncWebRequestMethod::HTTP_LINK;
1334
  } else if (m == T_UNLINK) {
1335
    return AsyncWebRequestMethod::HTTP_UNLINK;
1336
  } else if (m == T_PROPFIND) {
1337
    return AsyncWebRequestMethod::HTTP_PROPFIND;
1338
  } else if (m == T_LOCK) {
1339
    return AsyncWebRequestMethod::HTTP_LOCK;
1340
  } else if (m == T_UNLOCK) {
1341
    return AsyncWebRequestMethod::HTTP_UNLOCK;
1342
  } else if (m == T_PROPPATCH) {
1343
    return AsyncWebRequestMethod::HTTP_PROPPATCH;
1344
  } else if (m == T_MKCOL) {
1345
    return AsyncWebRequestMethod::HTTP_MKCOL;
1346
  } else if (m == T_MOVE) {
1347
    return AsyncWebRequestMethod::HTTP_MOVE;
1348
  } else if (m == T_COPY) {
1349
    return AsyncWebRequestMethod::HTTP_COPY;
1350
  } else if (m == T_SEARCH) {
1351
    return AsyncWebRequestMethod::HTTP_SEARCH;
1352
  } else if (m == T_BIND) {
1353
    return AsyncWebRequestMethod::HTTP_BIND;
1354
  } else if (m == T_REBIND) {
1355
    return AsyncWebRequestMethod::HTTP_REBIND;
1356
  } else if (m == T_UNBIND) {
1357
    return AsyncWebRequestMethod::HTTP_UNBIND;
1358
  } else if (m == T_ACL) {
1359
    return AsyncWebRequestMethod::HTTP_ACL;
1360
  } else {
1361
    return AsyncWebRequestMethod::HTTP_INVALID;
1362
  }
1363
}
1364
 
1365
const char *methodToString(WebRequestMethod method) {
1366
  switch (method) {
1367
    case AsyncWebRequestMethod::HTTP_DELETE: return T_DELETE;
1368
    case AsyncWebRequestMethod::HTTP_GET:    return T_GET;
1369
    case AsyncWebRequestMethod::HTTP_HEAD:   return T_HEAD;
1370
    case AsyncWebRequestMethod::HTTP_POST:   return T_POST;
1371
    case AsyncWebRequestMethod::HTTP_PUT:    return T_PUT;
1372
    /* pathological */
1373
    case AsyncWebRequestMethod::HTTP_CONNECT: return T_CONNECT;
1374
    case AsyncWebRequestMethod::HTTP_OPTIONS: return T_OPTIONS;
1375
    case AsyncWebRequestMethod::HTTP_TRACE:   return T_TRACE;
1376
    /* WebDAV */
1377
    case AsyncWebRequestMethod::HTTP_COPY:      return T_COPY;
1378
    case AsyncWebRequestMethod::HTTP_LOCK:      return T_LOCK;
1379
    case AsyncWebRequestMethod::HTTP_MKCOL:     return T_MKCOL;
1380
    case AsyncWebRequestMethod::HTTP_MOVE:      return T_MOVE;
1381
    case AsyncWebRequestMethod::HTTP_PROPFIND:  return T_PROPFIND;
1382
    case AsyncWebRequestMethod::HTTP_PROPPATCH: return T_PROPPATCH;
1383
    case AsyncWebRequestMethod::HTTP_SEARCH:    return T_SEARCH;
1384
    case AsyncWebRequestMethod::HTTP_UNLOCK:    return T_UNLOCK;
1385
    case AsyncWebRequestMethod::HTTP_BIND:      return T_BIND;
1386
    case AsyncWebRequestMethod::HTTP_REBIND:    return T_REBIND;
1387
    case AsyncWebRequestMethod::HTTP_UNBIND:    return T_UNBIND;
1388
    case AsyncWebRequestMethod::HTTP_ACL:       return T_ACL;
1389
    /* RFC-5789 */
1390
    case AsyncWebRequestMethod::HTTP_PATCH: return T_PATCH;
1391
    case AsyncWebRequestMethod::HTTP_PURGE: return T_PURGE;
1392
    /* RFC-2068, section 19.6.1.2 */
1393
    case AsyncWebRequestMethod::HTTP_LINK:   return T_LINK;
1394
    case AsyncWebRequestMethod::HTTP_UNLINK: return T_UNLINK;
1395
    // Unsupported
1396
    default: return T_UNKNOWN;
1397
  }
1398
}
1399
}  // namespace asyncsrv