#include "HttpRequestParser.h" #include "HttpRequest.h" namespace Http { HttpRequestParser::HttpRequestParser() : m_state(State::MethodStart) { } void HttpRequestParser::Reset() { m_state = State::MethodStart; } Utilities::Tribool HttpRequestParser::Consume(HttpRequest& request, char input) { switch (m_state) { case State::MethodStart: if (!IsChar(input) || IsCtl(input) || IsTSpecial(input)) { return false; } else { m_state = State::Method; request.method.push_back(input); return Utilities::Tribool::Indeterminate; } case State::Method: if (input == ' ') { m_state = State::URI; return Utilities::Tribool::Indeterminate; } else if (!IsChar(input) || IsCtl(input) || IsTSpecial(input)) { return false; } else { request.method.push_back(input); return Utilities::Tribool::Indeterminate; } case State::URI: if (input == ' ') { m_state = State::HttpVersionH; return Utilities::Tribool::Indeterminate; } else if (IsCtl(input)) { return false; } else { request.uri.push_back(input); return Utilities::Tribool::Indeterminate; } case State::HttpVersionH: if (input == 'H') { m_state = State::HttpVersionT1; return Utilities::Tribool::Indeterminate; } else { return false; } case State::HttpVersionT1: if (input == 'T') { m_state = State::HttpVersionT2; return Utilities::Tribool::Indeterminate; } else { return false; } case State::HttpVersionT2: if (input == 'T') { m_state = State::HttpVersionP; return Utilities::Tribool::Indeterminate; } else { return false; } case State::HttpVersionP: if (input == 'P') { m_state = State::HttpVersionSlash; return Utilities::Tribool::Indeterminate; } else { return false; } case State::HttpVersionSlash: if (input == '/') { request.httpVersionMajor = 0; request.httpVersionMinor = 0; m_state = State::HttpVersionMajorStart; return Utilities::Tribool::Indeterminate; } else { return false; } case State::HttpVersionMajorStart: if (IsDigit(input)) { request.httpVersionMajor = request.httpVersionMajor * 10 + input - '0'; m_state = State::HttpVersionMajor; return Utilities::Tribool::Indeterminate; } else { return false; } case State::HttpVersionMajor: if (input == '.') { m_state = State::HttpVersionMinorStart; return Utilities::Tribool::Indeterminate; } else if (IsDigit(input)) { request.httpVersionMajor = request.httpVersionMajor * 10 + input - '0'; return Utilities::Tribool::Indeterminate; } else { return false; } case State::HttpVersionMinorStart: if (IsDigit(input)) { request.httpVersionMinor = request.httpVersionMinor * 10 + input - '0'; m_state = State::HttpVersionMinor; return Utilities::Tribool::Indeterminate; } else { return false; } case State::HttpVersionMinor: if (input == '\r') { m_state = State::ExpectingNewline1; return Utilities::Tribool::Indeterminate; } else if (IsDigit(input)) { request.httpVersionMinor = request.httpVersionMinor * 10 + input - '0'; return Utilities::Tribool::Indeterminate; } else { return false; } case State::ExpectingNewline1: if (input == '\n') { m_state = State::HeaderLineStart; return Utilities::Tribool::Indeterminate; } else { return false; } case State::HeaderLineStart: if (input == '\r') { m_state = State::ExpectingNewline3; return Utilities::Tribool::Indeterminate; } else if (!request.headers.empty() && (input == ' ' || input == '\t')) { m_state = State::HeaderLws; return Utilities::Tribool::Indeterminate; } else if (!IsChar(input) || IsCtl(input) || IsTSpecial(input)) { return false; } else { request.headers.push_back(HttpHeader()); request.headers.back().name.push_back(input); m_state = State::HeaderName; return Utilities::Tribool::Indeterminate; } case State::HeaderLws: if (input == '\r') { m_state = State::ExpectingNewline2; return Utilities::Tribool::Indeterminate; } else if (input == ' ' || input == '\t') { return Utilities::Tribool::Indeterminate; } else if (IsCtl(input)) { return false; } else { m_state = State::HeaderValue; request.headers.back().value.push_back(input); return Utilities::Tribool::Indeterminate; } case State::HeaderName: if (input == ':') { m_state = State::SpaceBeforeHeaderValue; return Utilities::Tribool::Indeterminate; } else if (!IsChar(input) || IsCtl(input) || IsTSpecial(input)) { return false; } else { request.headers.back().name.push_back(input); return Utilities::Tribool::Indeterminate; } case State::SpaceBeforeHeaderValue: if (input == ' ') { m_state = State::HeaderValue; return Utilities::Tribool::Indeterminate; } else { return false; } case State::HeaderValue: if (input == '\r') { m_state = State::ExpectingNewline2; return Utilities::Tribool::Indeterminate; } else if (IsCtl(input)) { return false; } else { request.headers.back().value.push_back(input); return Utilities::Tribool::Indeterminate; } case State::ExpectingNewline2: if (input == '\n') { m_state = State::HeaderLineStart; return Utilities::Tribool::Indeterminate; } else { return false; } case State::ExpectingNewline3: return (input == '\n'); default: return false; } } bool HttpRequestParser::IsChar(int c) { return c >= 0 && c <= 127; } bool HttpRequestParser::IsCtl(int c) { return (c >= 0 && c <= 31) || (c == 127); } bool HttpRequestParser::IsTSpecial(int c) { switch (c) { case '(': case ')': case '<': case '>': case '@': case ',': case ';': case ':': case '\\': case '"': case '/': case '[': case ']': case '?': case '=': case '{': case '}': case ' ': case '\t': return true; default: return false; } } bool HttpRequestParser::IsDigit(int c) { return c >= '0' && c <= '9'; } } // namespace Http