Skip to content

Commit

Permalink
ESP8266WebServer - StreamString parsing experiment
Browse files Browse the repository at this point in the history
impl based on esp8266#9005, but for existing Arduino methods
  • Loading branch information
mcspr committed Jul 26, 2024
1 parent c2f1365 commit f013f08
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 38 deletions.
54 changes: 33 additions & 21 deletions cores/esp8266/Stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
parsing functions based on TextFinder library by Michael Margolis
*/

#include <Arduino.h>
#include <Stream.h>
#include "Arduino.h"
#include "Stream.h"
#include "StreamString.h"

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

String Stream::readStringUntil(char terminator) {
String ret;
int c = timedRead();
while(c >= 0 && c != terminator) {
ret += (char) c;
c = timedRead();
}

S2Stream s2s(ret);
sendUntil(s2s, terminator, _timeout);

return ret;
}

String Stream::readStringUntil(const char* terminator, uint32_t untilTotalNumberOfOccurrences) {
String ret;
int c;
if (!untilTotalNumberOfOccurrences) {
return ret;
}

const size_t termLen = strlen_P(terminator);
if (!termLen) {
return ret;
}

S2Stream s2s(ret);
uint32_t occurrences = 0;
size_t termLen = strlen(terminator);
size_t termIndex = 0;
size_t index = 0;
const size_t tailLen = termLen - 1;

while ((c = timedRead()) > 0) {
ret += (char) c;
index++;
for (;;) {
sendUntil(s2s, terminator[tailLen], _timeout);
if (s2s.getLastSendReport() != Stream::Report::Success) {
break;
}

if ((ret.length() >= tailLen)
&& ((0 == tailLen) || (0 == memcmp_P(terminator, ret.end() - tailLen, tailLen))))
{
++occurrences;
}

if (terminator[termIndex] == c) {
if (++termIndex == termLen && ++occurrences == untilTotalNumberOfOccurrences) {
// don't include terminator in returned string
ret.remove(index - termIndex, termLen);
break;
if (untilTotalNumberOfOccurrences == occurrences) {
if (tailLen) {
ret.remove(ret.length() - tailLen);
}
} else {
termIndex = 0;
break;
}
}

Expand Down
26 changes: 9 additions & 17 deletions libraries/ESP8266WebServer/src/Parsing-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,8 @@ static bool readBytesWithTimeout(typename ServerType::ClientType& client, size_t
template <typename ServerType>
typename ESP8266WebServerTemplate<ServerType>::ClientFuture ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
// Read the first line of HTTP request
String req = client.readStringUntil('\r');
String req = client.readStringUntil("\r\n");
DBGWS("request: %s\n", req.c_str());
client.readStringUntil('\n');
//reset header value
for (int i = 0; i < _headerKeysCount; ++i) {
_currentHeaders[i].value.clear();
Expand Down Expand Up @@ -122,8 +121,7 @@ typename ESP8266WebServerTemplate<ServerType>::ClientFuture ESP8266WebServerTemp
uint32_t contentLength = 0;
//parse headers
while(1){
req = client.readStringUntil('\r');
client.readStringUntil('\n');
req = client.readStringUntil("\r\n");
if (req.isEmpty()) break; //no more headers
int headerDiv = req.indexOf(':');
if (headerDiv == -1){
Expand Down Expand Up @@ -198,8 +196,7 @@ typename ESP8266WebServerTemplate<ServerType>::ClientFuture ESP8266WebServerTemp
String headerValue;
//parse headers
while(1){
req = client.readStringUntil('\r');
client.readStringUntil('\n');
req = client.readStringUntil("\r\n");
if (req.isEmpty()) break;//no moar headers
int headerDiv = req.indexOf(':');
if (headerDiv == -1){
Expand Down Expand Up @@ -351,7 +348,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
String line;
int retry = 0;
do {
line = client.readStringUntil('\r');
line = client.readStringUntil("\r\n");
++retry;
} while (line.length() == 0 && retry < 3);

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

line = client.readStringUntil('\r');
client.readStringUntil('\n');
line = client.readStringUntil("\r\n");
if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))){
int nameStart = line.indexOf('=');
if (nameStart != -1){
Expand All @@ -388,19 +384,16 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
DBGWS("PostArg Name: %s\n", argName.c_str());
using namespace mime;
argType = FPSTR(mimeTable[txt].mimeType);
line = client.readStringUntil('\r');
client.readStringUntil('\n');
line = client.readStringUntil("\r\n");
if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))){
argType = line.substring(line.indexOf(':')+2);
//skip next line
client.readStringUntil('\r');
client.readStringUntil('\n');
client.readStringUntil("\r\n");
}
DBGWS("PostArg Type: %s\n", argType.c_str());
if (!argIsFile){
while(1){
line = client.readStringUntil('\r');
client.readStringUntil('\n');
line = client.readStringUntil("\r\n");
if (line.startsWith("--"+boundary)) break;
if (argValue.length() > 0) argValue += '\n';
argValue += line;
Expand Down Expand Up @@ -474,8 +467,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
_currentUpload->type.c_str(),
(int)_currentUpload->totalSize);
if (!client.connected()) return _parseFormUploadAborted();
line = client.readStringUntil('\r');
client.readStringUntil('\n');
line = client.readStringUntil("\r\n");
if (line == "--") { // extra two dashes mean we reached the end of all form fields
DBGWS("Done Parsing POST\n");
break;
Expand Down
25 changes: 25 additions & 0 deletions tests/host/core/test_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,31 @@ TEST_CASE("Issue #2736 - StreamString SSO fix", "[core][StreamString]")
REQUIRE(s == "{\"message\"");
}

TEST_CASE("Issue #9005 - StreamString for Stream->String conversion", "[core][StreamString]")
{
const char buffer[] =
"this is a test string"
"\r\n"
"delimited as if it was a http request"
"\r\n"
"\r\n";

StreamString input;
input.print(buffer);
REQUIRE(input == buffer);

String out = input.readStringUntil("\r\n");
REQUIRE(21 == out.length());
REQUIRE(out == "this is a test string");

out = input.readStringUntil("\r\n");
REQUIRE(37 == out.length());
REQUIRE(out == "delimited as if it was a http request");

out = input.readStringUntil("\r\n");
REQUIRE(0 == out.length());
}

TEST_CASE("Strings with NULs", "[core][String]")
{
// The following should never be done in a real app! This is only to inject 0s in the middle of
Expand Down

0 comments on commit f013f08

Please sign in to comment.