Skip to content

Commit f013f08

Browse files
committed
ESP8266WebServer - StreamString parsing experiment
impl based on esp8266#9005, but for existing Arduino methods
1 parent c2f1365 commit f013f08

File tree

3 files changed

+67
-38
lines changed

3 files changed

+67
-38
lines changed

cores/esp8266/Stream.cpp

+33-21
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020
parsing functions based on TextFinder library by Michael Margolis
2121
*/
2222

23-
#include <Arduino.h>
24-
#include <Stream.h>
23+
#include "Arduino.h"
24+
#include "Stream.h"
25+
#include "StreamString.h"
2526

2627
#define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait
2728
#define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field
@@ -254,34 +255,45 @@ String Stream::readString() {
254255

255256
String Stream::readStringUntil(char terminator) {
256257
String ret;
257-
int c = timedRead();
258-
while(c >= 0 && c != terminator) {
259-
ret += (char) c;
260-
c = timedRead();
261-
}
258+
259+
S2Stream s2s(ret);
260+
sendUntil(s2s, terminator, _timeout);
261+
262262
return ret;
263263
}
264264

265265
String Stream::readStringUntil(const char* terminator, uint32_t untilTotalNumberOfOccurrences) {
266266
String ret;
267-
int c;
267+
if (!untilTotalNumberOfOccurrences) {
268+
return ret;
269+
}
270+
271+
const size_t termLen = strlen_P(terminator);
272+
if (!termLen) {
273+
return ret;
274+
}
275+
276+
S2Stream s2s(ret);
268277
uint32_t occurrences = 0;
269-
size_t termLen = strlen(terminator);
270-
size_t termIndex = 0;
271-
size_t index = 0;
278+
const size_t tailLen = termLen - 1;
272279

273-
while ((c = timedRead()) > 0) {
274-
ret += (char) c;
275-
index++;
280+
for (;;) {
281+
sendUntil(s2s, terminator[tailLen], _timeout);
282+
if (s2s.getLastSendReport() != Stream::Report::Success) {
283+
break;
284+
}
285+
286+
if ((ret.length() >= tailLen)
287+
&& ((0 == tailLen) || (0 == memcmp_P(terminator, ret.end() - tailLen, tailLen))))
288+
{
289+
++occurrences;
290+
}
276291

277-
if (terminator[termIndex] == c) {
278-
if (++termIndex == termLen && ++occurrences == untilTotalNumberOfOccurrences) {
279-
// don't include terminator in returned string
280-
ret.remove(index - termIndex, termLen);
281-
break;
292+
if (untilTotalNumberOfOccurrences == occurrences) {
293+
if (tailLen) {
294+
ret.remove(ret.length() - tailLen);
282295
}
283-
} else {
284-
termIndex = 0;
296+
break;
285297
}
286298
}
287299

libraries/ESP8266WebServer/src/Parsing-impl.h

+9-17
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,8 @@ static bool readBytesWithTimeout(typename ServerType::ClientType& client, size_t
4444
template <typename ServerType>
4545
typename ESP8266WebServerTemplate<ServerType>::ClientFuture ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
4646
// Read the first line of HTTP request
47-
String req = client.readStringUntil('\r');
47+
String req = client.readStringUntil("\r\n");
4848
DBGWS("request: %s\n", req.c_str());
49-
client.readStringUntil('\n');
5049
//reset header value
5150
for (int i = 0; i < _headerKeysCount; ++i) {
5251
_currentHeaders[i].value.clear();
@@ -122,8 +121,7 @@ typename ESP8266WebServerTemplate<ServerType>::ClientFuture ESP8266WebServerTemp
122121
uint32_t contentLength = 0;
123122
//parse headers
124123
while(1){
125-
req = client.readStringUntil('\r');
126-
client.readStringUntil('\n');
124+
req = client.readStringUntil("\r\n");
127125
if (req.isEmpty()) break; //no more headers
128126
int headerDiv = req.indexOf(':');
129127
if (headerDiv == -1){
@@ -198,8 +196,7 @@ typename ESP8266WebServerTemplate<ServerType>::ClientFuture ESP8266WebServerTemp
198196
String headerValue;
199197
//parse headers
200198
while(1){
201-
req = client.readStringUntil('\r');
202-
client.readStringUntil('\n');
199+
req = client.readStringUntil("\r\n");
203200
if (req.isEmpty()) break;//no moar headers
204201
int headerDiv = req.indexOf(':');
205202
if (headerDiv == -1){
@@ -351,7 +348,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
351348
String line;
352349
int retry = 0;
353350
do {
354-
line = client.readStringUntil('\r');
351+
line = client.readStringUntil("\r\n");
355352
++retry;
356353
} while (line.length() == 0 && retry < 3);
357354

@@ -367,8 +364,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
367364
String argFilename;
368365
bool argIsFile = false;
369366

370-
line = client.readStringUntil('\r');
371-
client.readStringUntil('\n');
367+
line = client.readStringUntil("\r\n");
372368
if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))){
373369
int nameStart = line.indexOf('=');
374370
if (nameStart != -1){
@@ -388,19 +384,16 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
388384
DBGWS("PostArg Name: %s\n", argName.c_str());
389385
using namespace mime;
390386
argType = FPSTR(mimeTable[txt].mimeType);
391-
line = client.readStringUntil('\r');
392-
client.readStringUntil('\n');
387+
line = client.readStringUntil("\r\n");
393388
if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))){
394389
argType = line.substring(line.indexOf(':')+2);
395390
//skip next line
396-
client.readStringUntil('\r');
397-
client.readStringUntil('\n');
391+
client.readStringUntil("\r\n");
398392
}
399393
DBGWS("PostArg Type: %s\n", argType.c_str());
400394
if (!argIsFile){
401395
while(1){
402-
line = client.readStringUntil('\r');
403-
client.readStringUntil('\n');
396+
line = client.readStringUntil("\r\n");
404397
if (line.startsWith("--"+boundary)) break;
405398
if (argValue.length() > 0) argValue += '\n';
406399
argValue += line;
@@ -474,8 +467,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
474467
_currentUpload->type.c_str(),
475468
(int)_currentUpload->totalSize);
476469
if (!client.connected()) return _parseFormUploadAborted();
477-
line = client.readStringUntil('\r');
478-
client.readStringUntil('\n');
470+
line = client.readStringUntil("\r\n");
479471
if (line == "--") { // extra two dashes mean we reached the end of all form fields
480472
DBGWS("Done Parsing POST\n");
481473
break;

tests/host/core/test_string.cpp

+25
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,31 @@ TEST_CASE("Issue #2736 - StreamString SSO fix", "[core][StreamString]")
512512
REQUIRE(s == "{\"message\"");
513513
}
514514

515+
TEST_CASE("Issue #9005 - StreamString for Stream->String conversion", "[core][StreamString]")
516+
{
517+
const char buffer[] =
518+
"this is a test string"
519+
"\r\n"
520+
"delimited as if it was a http request"
521+
"\r\n"
522+
"\r\n";
523+
524+
StreamString input;
525+
input.print(buffer);
526+
REQUIRE(input == buffer);
527+
528+
String out = input.readStringUntil("\r\n");
529+
REQUIRE(21 == out.length());
530+
REQUIRE(out == "this is a test string");
531+
532+
out = input.readStringUntil("\r\n");
533+
REQUIRE(37 == out.length());
534+
REQUIRE(out == "delimited as if it was a http request");
535+
536+
out = input.readStringUntil("\r\n");
537+
REQUIRE(0 == out.length());
538+
}
539+
515540
TEST_CASE("Strings with NULs", "[core][String]")
516541
{
517542
// The following should never be done in a real app! This is only to inject 0s in the middle of

0 commit comments

Comments
 (0)