Add StreamReader and rebuild Parser/LineParser/IMFHeader/IMFBoundary on it
Introduces StreamReader to accumulate socket data delivered in arbitrary chunks into one buffer, so a Parser can build an object (a line, a header block, a MIME boundary) that spans any number of packets without losing its place. Parser is redesigned around this: no longer inherits MString (was redundant with its own buffer member), takes a StreamReader&, and drives a resumable COMPLETE/INCOMPLETE/DATA_AVAIL state-machine protocol via doParse(). LineParser, IMFHeader, and IMFBoundary are rewritten as real, working resumable parsers against the shared buffer instead of the previous non-compiling stubs (mismatched constructors/return types, a syntax error, checkParse() never implemented). IMFFormData is left as the stub it already was, just restored to compiling against the new IMFHeader signature - fully wiring it up needs IMFMultipart/IMFText migrated to StreamReader too, out of scope here. Verified with real runtime tests (not just compilation) covering data split mid-line, mid-CRLF, mid-header-fold, and mid-boundary-marker across separate feed() calls, plus StreamReader::compact(). Along the way fixed a header-value bug (space after ':' wasn't skipped) and a double cursor-offset subtraction bug in two places caused by forgetting that ZString::getLength() is already cursor-relative, not the raw buffer size. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
94d4a30e88
commit
a033cc6718
13 files changed
+443
-136
No files matched your search
+65
-6
@@ -1,14 +1,73 @@
|
||||
#include <iostream>
|
||||
#include "../MString.h"
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include "../StreamReader.h"
|
||||
#include "../LineParser.h"
|
||||
#include "../IMFHeader.h"
|
||||
#include "../IMFBoundary.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
using namespace coreutils;
|
||||
|
||||
coreutils::LineParser line(0);
|
||||
Parse.Status status = line.doParse();
|
||||
|
||||
static int checks = 0;
|
||||
#define CHECK(cond) do { checks++; if(!(cond)) { std::cerr << "FAIL line " << __LINE__ << ": " #cond << std::endl; return 1; } } while(0)
|
||||
|
||||
|
||||
void feedStr(StreamReader &r, const char *s) {
|
||||
ZString z((char *)s, strlen(s));
|
||||
r.feed(z);
|
||||
}
|
||||
|
||||
// The whole point of StreamReader/Parser: a "line" (or header, or boundary)
|
||||
// can arrive split across any number of socket packets, and parsing must
|
||||
// resume cleanly from wherever it left off once more data shows up.
|
||||
|
||||
int test_lineparser_split_across_feeds() {
|
||||
StreamReader reader;
|
||||
feedStr(reader, "partial li");
|
||||
LineParser lp(reader);
|
||||
CHECK(lp.doParse() == Parser::Status::INCOMPLETE);
|
||||
feedStr(reader, "ne\r\n");
|
||||
CHECK(lp.doParse() == Parser::Status::COMPLETE);
|
||||
ZString line = lp.getLine();
|
||||
CHECK(line.getLength() == 12);
|
||||
CHECK(strncmp(line.getData(), "partial line", 12) == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_imfheader_folding_and_split_feeds() {
|
||||
StreamReader reader;
|
||||
IMFHeader header(reader);
|
||||
feedStr(reader, "Subject: line one\r\n ");
|
||||
CHECK(header.doParse() == Parser::Status::INCOMPLETE);
|
||||
feedStr(reader, "line two\r\n\r\n");
|
||||
CHECK(header.doParse() == Parser::Status::COMPLETE);
|
||||
auto &fields = header.getHeaders();
|
||||
CHECK(fields.size() == 1);
|
||||
MString subj = fields[MString("Subject")];
|
||||
CHECK(subj.getLength() == 17);
|
||||
CHECK(strncmp(subj.getData(), "line one line two", subj.getLength()) == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_imfboundary_split_across_feeds() {
|
||||
ZString b((char *)"XYZ", 3);
|
||||
StreamReader reader;
|
||||
IMFBoundary bound(reader, b);
|
||||
feedStr(reader, "\r\n--X");
|
||||
CHECK(bound.doParse() == Parser::Status::INCOMPLETE);
|
||||
feedStr(reader, "YZ--");
|
||||
CHECK(bound.doParse() == Parser::Status::COMPLETE);
|
||||
CHECK(bound.isLast() == true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int rc = 0;
|
||||
rc |= test_lineparser_split_across_feeds();
|
||||
rc |= test_imfheader_folding_and_split_feeds();
|
||||
rc |= test_imfboundary_split_across_feeds();
|
||||
if (rc == 0)
|
||||
std::cout << "ALL " << checks << " CHECKS PASSED" << std::endl;
|
||||
else
|
||||
std::cout << "SOME CHECKS FAILED (see above)" << std::endl;
|
||||
return rc;
|
||||
}
|
||||
Reference in new issue
Block a user