boost::tribool& RequestParser::consume(Request& req, Buffer::iterator it) { static boost::tribool False(false); static boost::tribool True(true); static boost::tribool Indeterminate(boost::indeterminate); const char input = *it; if (++requestSize_ > MAX_REQUEST_HEADER_SIZE) return False; switch (httpState_) { case method_start: if (input == '\r') { /* * allow a new line before a request -- this seems to be * accepted practice when dealing with multiple requests * in one connection, separated by a CRLF. */ httpState_ = expecting_newline_0; return Indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return False; } else { httpState_ = method; consumeToString(req.method, MAX_METHOD_SIZE); consumeChar(it); return Indeterminate; } case expecting_newline_0: if (input == '\n') { httpState_ = method_start; return Indeterminate; } else { return False; } case method: if (input == ' ') { consumeComplete(it); httpState_ = uri_start; return Indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return False; } else { if (consumeChar(it)) return Indeterminate; else return False; } case uri_start: if (is_ctl(input)) { return False; } else { httpState_ = uri; consumeToString(req.uri, MAX_URI_SIZE); consumeChar(it); return Indeterminate; } case uri: if (input == ' ') { consumeComplete(it); httpState_ = http_version_h; return Indeterminate; } else if (is_ctl(input)) { return False; } else { if (consumeChar(it)) return Indeterminate; else return False; } case http_version_h: if (input == 'H') { httpState_ = http_version_t_1; return Indeterminate; } else { return False; } case http_version_t_1: if (input == 'T') { httpState_ = http_version_t_2; return Indeterminate; } else { return False; } case http_version_t_2: if (input == 'T') { httpState_ = http_version_p; return Indeterminate; } else { return False; } case http_version_p: if (input == 'P') { httpState_ = http_version_slash; return Indeterminate; } else { return False; } case http_version_slash: if (input == '/') { req.http_version_major = 0; req.http_version_minor = 0; httpState_ = http_version_major_start; return Indeterminate; } else { return False; } case http_version_major_start: if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; httpState_ = http_version_major; return Indeterminate; } else { return False; } case http_version_major: if (input == '.') { httpState_ = http_version_minor_start; return Indeterminate; } else if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; return Indeterminate; } else { return False; } case http_version_minor_start: if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; httpState_ = http_version_minor; return Indeterminate; } else { return False; } case http_version_minor: if (input == '\r') { httpState_ = expecting_newline_1; return Indeterminate; } else if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; return Indeterminate; } else { return False; } case expecting_newline_1: if (input == '\n') { httpState_ = header_line_start; return Indeterminate; } else { return False; } case header_line_start: if (input == '\r') { httpState_ = expecting_newline_3; return Indeterminate; } else if ((input == ' ' || input == '\t') && haveHeader_) { // continuation of previous header httpState_ = header_lws; return Indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return False; } else { req.headers.push_back(Request::Header()); haveHeader_ = true; consumeToString(req.headers.back().name, MAX_FIELD_NAME_SIZE); consumeChar(it); httpState_ = header_name; return Indeterminate; } case header_lws: if (input == '\r') { httpState_ = expecting_newline_2; return Indeterminate; } else if (input == ' ' || input == '\t') { return Indeterminate; } else if (is_ctl(input)) { return False; } else { httpState_ = header_value; consumeChar(it); return Indeterminate; } case header_name: if (input == ':') { consumeComplete(it); httpState_ = space_before_header_value; return Indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return False; } else { if (consumeChar(it)) return Indeterminate; else return False; } case space_before_header_value: if (input == ' ') { consumeToString(req.headers.back().value, MAX_FIELD_VALUE_SIZE); httpState_ = header_value; return Indeterminate; } else { consumeToString(req.headers.back().value, MAX_FIELD_VALUE_SIZE); httpState_ = header_value; /* fall through */ } case header_value: if (input == '\r') { consumeComplete(it); httpState_ = expecting_newline_2; return Indeterminate; } else if (is_ctl(input)) { return False; } else { if (consumeChar(it)) return Indeterminate; else return False; } case expecting_newline_2: if (input == '\n') { httpState_ = header_line_start; return Indeterminate; } else { return False; } case expecting_newline_3: if (input == '\n') return True; else return False; default: return False; } }
boost::tribool& RequestParser::consume(Request& req, char input) { static boost::tribool False(false); static boost::tribool True(true); static boost::tribool Indeterminate(boost::indeterminate); if (++requestSize_ > MAX_REQUEST_HEADER_SIZE) return False; switch (httpState_) { case method_start: if (input == '\r') { /* * allow a new line before a request -- this seems to be * accepted practice when dealing with multiple requests * in one connection, separated by a CRLF. */ httpState_ = expecting_newline_0; return Indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return False; } else { httpState_ = method; consumeToString(req.method, MAX_METHOD_SIZE); consumeChar(input); return Indeterminate; } case expecting_newline_0: if (input == '\n') { httpState_ = method_start; return Indeterminate; } else { return False; } case method: if (input == ' ') { consumeComplete(); httpState_ = uri_start; return Indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return False; } else { if (consumeChar(input)) return Indeterminate; else return False; } case uri_start: if (is_ctl(input)) { return False; } else { httpState_ = uri; consumeToString(req.uri, MAX_URI_SIZE); consumeChar(input); return Indeterminate; } case uri: if (input == ' ') { consumeComplete(); httpState_ = http_version_h; return Indeterminate; } else if (is_ctl(input)) { return False; } else { if (consumeChar(input)) return Indeterminate; else return False; } case http_version_h: if (input == 'H') { httpState_ = http_version_t_1; return Indeterminate; } else { return False; } case http_version_t_1: if (input == 'T') { httpState_ = http_version_t_2; return Indeterminate; } else { return False; } case http_version_t_2: if (input == 'T') { httpState_ = http_version_p; return Indeterminate; } else { return False; } case http_version_p: if (input == 'P') { httpState_ = http_version_slash; return Indeterminate; } else { return False; } case http_version_slash: if (input == '/') { req.http_version_major = 0; req.http_version_minor = 0; httpState_ = http_version_major_start; return Indeterminate; } else { return False; } case http_version_major_start: if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; httpState_ = http_version_major; return Indeterminate; } else { return False; } case http_version_major: if (input == '.') { httpState_ = http_version_minor_start; return Indeterminate; } else if (is_digit(input)) { req.http_version_major = req.http_version_major * 10 + input - '0'; return Indeterminate; } else { return False; } case http_version_minor_start: if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; httpState_ = http_version_minor; return Indeterminate; } else { return False; } case http_version_minor: if (input == '\r') { httpState_ = expecting_newline_1; return Indeterminate; } else if (is_digit(input)) { req.http_version_minor = req.http_version_minor * 10 + input - '0'; return Indeterminate; } else { return False; } case expecting_newline_1: if (input == '\n') { httpState_ = header_line_start; return Indeterminate; } else { return False; } case header_line_start: if (input == '\r') { httpState_ = expecting_newline_3; return Indeterminate; } else if (!req.headerMap.empty() && (input == ' ' || input == '\t')) { // continuation of previous header httpState_ = header_lws; return Indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return False; } else { consumeToString(headerName_, MAX_FIELD_NAME_SIZE); consumeChar(input); httpState_ = header_name; return Indeterminate; } case header_lws: if (input == '\r') { httpState_ = expecting_newline_2; return Indeterminate; } else if (input == ' ' || input == '\t') { return Indeterminate; } else if (is_ctl(input)) { return False; } else { httpState_ = header_value; headerValue_.push_back(input); return Indeterminate; } case header_name: if (input == ':') { consumeComplete(); httpState_ = space_before_header_value; return Indeterminate; } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return False; } else { if (consumeChar(input)) return Indeterminate; else return False; } case space_before_header_value: if (input == ' ') { consumeToString(headerValue_, MAX_FIELD_VALUE_SIZE); httpState_ = header_value; return Indeterminate; } else { consumeToString(headerValue_, MAX_FIELD_VALUE_SIZE); httpState_ = header_value; } case header_value: if (input == '\r') { consumeComplete(); if (req.headerMap.find(headerName_) != req.headerMap.end()) { req.headerMap[headerName_] += ',' + headerValue_; } else { Request::HeaderMap::iterator i = req.headerMap.insert(std::make_pair(headerName_, headerValue_)) .first; req.headerOrder.push_back(i); } httpState_ = expecting_newline_2; return Indeterminate; } else if (is_ctl(input)) { return False; } else { if (consumeChar(input)) return Indeterminate; else return False; } case expecting_newline_2: if (input == '\n') { httpState_ = header_line_start; return Indeterminate; } else { return False; } case expecting_newline_3: if (input == '\n') return True; else return False; default: return False; } }