void
Http::One::Parser::clear()
{
- completedState_ = HTTP_PARSE_NONE;
+ parsingStage_ = HTTP_PARSE_NONE;
buf = NULL;
bufsiz = 0;
parseOffset_ = 0;
Http::One::Parser::reset(const char *aBuf, int len)
{
clear(); // empty the state.
- completedState_ = HTTP_PARSE_NEW;
+ parsingStage_ = HTTP_PARSE_NEW;
+ parseOffset_ = 0;
buf = aBuf;
bufsiz = len;
debugs(74, DBG_DATA, "Parse " << Raw("buf", buf, bufsiz));
Http::One::RequestParser::noteBufferShift(int64_t n)
{
// if parsing done, ignore buffer changes.
- if (completedState_ == HTTP_PARSE_DONE)
+ if (parsingStage_ == HTTP_PARSE_DONE)
return;
// shift the parser resume point to match buffer content change
* "
*
* Parsing state is stored between calls to avoid repeating buffer scans.
- * \return true if garbage whitespace was found
+ * If garbage is found the parsing offset is incremented.
*/
-bool
+void
Http::One::RequestParser::skipGarbageLines()
{
- req.start = parseOffset_; // avoid re-parsing any portion we managed to complete
-
#if WHEN_RFC_COMPLIANT // CRLF or bare-LF is what RFC 2616 tolerant parsers do ...
if (Config.onoff.relaxed_header_parser) {
- if (Config.onoff.relaxed_header_parser < 0 && (buf[req.start] == '\r' || buf[req.start] == '\n'))
+ if (Config.onoff.relaxed_header_parser < 0 && (buf[parseOffset_] == '\r' || buf[parseOffset_] == '\n'))
debugs(74, DBG_IMPORTANT, "WARNING: Invalid HTTP Request: " <<
"CRLF bytes received ahead of request-line. " <<
"Ignored due to relaxed_header_parser.");
// Be tolerant of prefix empty lines
- for (; req.start < bufsiz && (buf[req.start] == '\n' || ((buf[req.start] == '\r' && (buf[req.start+1] == '\n')); ++req.start);
- parseOffset_ = req.start;
+ // ie any series of either \n or \r\n with no other characters and no repeated \r
+ for (; parseOffset_ < (size_t)bufsiz && (buf[parseOffset_] == '\n' || ((buf[parseOffset_] == '\r' && (buf[parseOffset_+1] == '\n')); ++parseOffset_);
}
#endif
*/
#if USE_HTTP_VIOLATIONS
if (Config.onoff.relaxed_header_parser) {
- if (Config.onoff.relaxed_header_parser < 0 && buf[req.start] == ' ')
+ if (Config.onoff.relaxed_header_parser < 0 && buf[parseOffset_] == ' ')
debugs(74, DBG_IMPORTANT, "WARNING: Invalid HTTP Request: " <<
"Whitespace bytes received ahead of method. " <<
"Ignored due to relaxed_header_parser.");
// Be tolerant of prefix spaces (other bytes are valid method values)
- for (; req.start < bufsiz && buf[req.start] == ' '; ++req.start);
- parseOffset_ = req.start;
+ for (; parseOffset_ < (size_t)bufsiz && buf[parseOffset_] == ' '; ++parseOffset_);
}
#endif
-
- return (parseOffset_ > 0);
}
/**
msgProtocol_ = Http::ProtocolVersion(0,9);
req.u_end = line_end;
request_parse_status = Http::scOkay; // HTTP/0.9
- completedState_ = HTTP_PARSE_FIRST;
- parseOffset_ = line_end;
return 1;
} else {
// otherwise last whitespace is somewhere after end of URI.
msgProtocol_ = Http::ProtocolVersion(0,9);
req.u_end = line_end;
request_parse_status = Http::scOkay; // treat as HTTP/0.9
- completedState_ = HTTP_PARSE_FIRST;
- parseOffset_ = req.end;
return 1;
#else
// protocol not supported / implemented.
* Rightio - we have all the schtuff. Return true; we've got enough.
*/
request_parse_status = Http::scOkay;
- parseOffset_ = req.end+1; // req.end is the \n byte. Next parse step needs to start *after* that byte.
- completedState_ = HTTP_PARSE_FIRST;
return 1;
}
-
+#include <cstdio>
bool
Http::One::RequestParser::parse()
{
- if (completedState_ == HTTP_PARSE_NEW) {
-
- // stage 1: locate the request-line
- if (skipGarbageLines() && (size_t)bufsiz < parseOffset_)
+ // stage 1: locate the request-line
+ if (parsingStage_ == HTTP_PARSE_NEW) {
+fprintf(stderr, "parse GARBAGE: '%s'\n", buf);
+ skipGarbageLines();
+fprintf(stderr, "parse GBG A(%d) < B(%u)\n", bufsiz, parseOffset_);
+
+ // if we hit something before EOS treat it as a message
+ if ((size_t)bufsiz > parseOffset_)
+ parsingStage_ = HTTP_PARSE_FIRST;
+ else
return false;
+ }
- // stage 2: parse the request-line
+ // stage 2: parse the request-line
+ if (parsingStage_ == HTTP_PARSE_FIRST) {
+fprintf(stderr, "parse FIRST: '%s'\n", buf);
PROF_start(HttpParserParseReqLine);
const int retcode = parseRequestFirstLine();
debugs(74, 5, "request-line: retval " << retcode << ": from " << req.start << "->" << req.end << " " << Raw("line", &buf[req.start], req.end-req.start));
debugs(74, 5, "request-line: proto " << req.v_start << "->" << req.v_end << " (" << msgProtocol_ << ")");
debugs(74, 5, "Parser: parse-offset=" << parseOffset_);
PROF_stop(HttpParserParseReqLine);
+
+ // syntax errors already
if (retcode < 0) {
- completedState_ = HTTP_PARSE_DONE;
+ parsingStage_ = HTTP_PARSE_DONE;
+fprintf(stderr, "parse FIRST DONE (error)\n");
return false;
}
+
+ // first-line (or a look-alike) found successfully.
+ if (retcode > 0) {
+ parseOffset_ += firstLineSize(); // first line bytes including CRLF terminator are now done.
+ parsingStage_ = HTTP_PARSE_MIME;
+fprintf(stderr, "parse FIRST (next: MIME)\n");
+ }
+else fprintf(stderr, "parse FIRST: ret=%d\n",retcode);
}
// stage 3: locate the mime header block
- if (completedState_ == HTTP_PARSE_FIRST) {
+ if (parsingStage_ == HTTP_PARSE_MIME) {
+fprintf(stderr, "parse MIME: '%s'\n", buf);
// HTTP/1.x request-line is valid and parsing completed.
if (msgProtocol_.major == 1) {
/* NOTE: HTTP/0.9 requests do not have a mime header block.
if (bufsiz-parseOffset_ >= Config.maxRequestHeaderSize) {
debugs(33, 5, "Too large request");
request_parse_status = Http::scHeaderTooLarge;
- completedState_ = HTTP_PARSE_DONE;
- } else
+ parsingStage_ = HTTP_PARSE_DONE;
+fprintf(stderr, "parse DONE: HTTP/1.x\n");
+ } else {
debugs(33, 5, "Incomplete request, waiting for end of headers");
- return false;
+fprintf(stderr, "parse MIME incomplete\n");
+} return false;
}
mimeHeaderBlock_.assign(&buf[req.end+1], mimeHeaderBytes);
+ parseOffset_ += mimeHeaderBytes; // done with these bytes now.
- } else
+ } else {
debugs(33, 3, "Missing HTTP/1.x identifier");
-
- // NP: planned name for this stage is HTTP_PARSE_MIME
- // but we do not do any further stages here yet so go straight to DONE
- completedState_ = HTTP_PARSE_DONE;
+fprintf(stderr, "parse MIME: HTTP/0.9\n");
+}
+ // NP: we do not do any further stages here yet so go straight to DONE
+ parsingStage_ = HTTP_PARSE_DONE;
// Squid could handle these headers, but admin does not want to
if (messageHeaderSize() >= Config.maxRequestHeaderSize) {
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-5, output.req.end);
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
// Being tolerant we can ignore and elide these apparently benign CR
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scNone, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL(-1, output.req.end);
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scNone, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL(-1, output.req.end);
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scNone, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL(-1, output.req.end);
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scNone, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL(-1, output.req.end);
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
Config.onoff.relaxed_header_parser = 1;
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(1, output.req.start);
CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
}
// tab padded method (NP: tab is not SP so treated as any other binary)
+ // XXX: binary codes are non-compliant
{
input.append("\tGET / HTTP/1.1\n", 16);
//printf("TEST: '%s'\n",input.content());
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
Config.onoff.relaxed_header_parser = 1;
CPPUNIT_ASSERT_EQUAL(true, output.parse());
CPPUNIT_ASSERT_EQUAL(true, output.isDone());
-// CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NEW, output.completedState_);
+// CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_DONE, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(1, output.req.start);
CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
output.reset(input.content(), input.contentSize());
CPPUNIT_ASSERT_EQUAL(false, output.parse());
CPPUNIT_ASSERT_EQUAL(false, output.isDone());
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, output.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, output.parsingStage_);
CPPUNIT_ASSERT_EQUAL(Http::scOkay, output.request_parse_status);
CPPUNIT_ASSERT_EQUAL(0, output.req.start);
CPPUNIT_ASSERT_EQUAL((int)input.contentSize()-1, output.req.end);
int garbageEnd = mb.contentSize();
mb.append("GET http://example.com/ HTTP/1.1\r\n", 34);
int reqLineEnd = mb.contentSize();
- mb.append("Host: example.com\r\n\r\n.", 22);
+ mb.append("Host: example.com\r\n\r\n", 21);
+ int mimeEnd = mb.contentSize();
+ mb.append("...", 3); // trailer to catch mime EOS errors.
Http1::RequestParser hp(mb.content(), 0);
#endif
// before end of garbage found its a moving offset.
- if (hp.bufsiz < garbageEnd) {
+ if (hp.bufsiz <= garbageEnd) {
CPPUNIT_ASSERT_EQUAL(hp.bufsiz, (int)hp.parseOffset_);
CPPUNIT_ASSERT_EQUAL(false, hp.isDone());
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NEW, hp.parsingStage_);
continue;
}
CPPUNIT_ASSERT_EQUAL(garbageEnd, (int)hp.parseOffset_);
CPPUNIT_ASSERT_EQUAL(false, parseResult);
CPPUNIT_ASSERT_EQUAL(false, hp.isDone());
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_NEW, hp.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, hp.parsingStage_);
continue;
}
// before request headers entirely found, parse announces incomplete
- if (hp.bufsiz < mb.contentSize()-1) {
+ if (hp.bufsiz < mimeEnd) {
CPPUNIT_ASSERT_EQUAL(reqLineEnd, (int)hp.parseOffset_);
CPPUNIT_ASSERT_EQUAL(false, parseResult);
CPPUNIT_ASSERT_EQUAL(false, hp.isDone());
// TODO: add all the other usual tests for request-line details
- CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_FIRST, hp.completedState_);
+ CPPUNIT_ASSERT_EQUAL(Http1::HTTP_PARSE_MIME, hp.parsingStage_);
continue;
}
// once request line is found (AND the following \n) current parser announces success
- CPPUNIT_ASSERT_EQUAL(reqLineEnd, (int)hp.parseOffset_);
+ CPPUNIT_ASSERT_EQUAL(mimeEnd, (int)hp.parseOffset_);
CPPUNIT_ASSERT_EQUAL(true, parseResult);
CPPUNIT_ASSERT_EQUAL(true, hp.isDone());
}