osrm-backend/HttpServer/request_parser.h
2010-07-26 12:27:58 +00:00

403 lines
7.3 KiB
C++

/*
open source routing machine
Copyright (C) Dennis Luxen, 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU AFFERO General Public License as published by
the Free Software Foundation; either version 3 of the License, or
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
or see http://www.gnu.org/licenses/agpl.txt.
*/
#ifndef HTTP_REQUEST_PARSER_HPP
#define HTTP_REQUEST_PARSER_HPP
#include <boost/logic/tribool.hpp>
#include <boost/tuple/tuple.hpp>
namespace http {
struct Request;
/// Parser for incoming requests.
class request_parser
{
public:
/// Construct ready to parse the request method.
request_parser();
/// Reset to initial parser state.
void reset();
/// Parse some data. The tribool return value is true when a complete request
/// has been parsed, false if the data is invalid, indeterminate when more
/// data is required. The InputIterator return value indicates how much of the
/// input has been consumed.
template <typename InputIterator>
boost::tuple<boost::tribool, InputIterator> parse(Request& req,
InputIterator begin, InputIterator end)
{
while (begin != end)
{
boost::tribool result = consume(req, *begin++);
if (result || !result)
return boost::make_tuple(result, begin);
}
boost::tribool result = boost::indeterminate;
return boost::make_tuple(result, begin);
}
private:
/// Handle the next character of input.
boost::tribool consume(Request& req, char input);
/// Check if a byte is an HTTP character.
static bool is_char(int c);
/// Check if a byte is an HTTP control character.
static bool is_ctl(int c);
/// Check if a byte is defined as an HTTP tspecial character.
static bool is_tspecial(int c);
/// Check if a byte is a digit.
static bool is_digit(int c);
/// The current state of the parser.
enum state
{
method_start,
method,
uri_start,
uri,
http_version_h,
http_version_t_1,
http_version_t_2,
http_version_p,
http_version_slash,
http_version_major_start,
http_version_major,
http_version_minor_start,
http_version_minor,
expecting_newline_1,
header_line_start,
header_lws,
header_name,
space_before_header_value,
header_value,
expecting_newline_2,
expecting_newline_3
} state_;
};
} // namespace http
#include "request.h"
namespace http {
request_parser::request_parser()
: state_(method_start)
{
}
void request_parser::reset()
{
state_ = method_start;
}
boost::tribool request_parser::consume(Request& req, char input)
{
switch (state_)
{
case method_start:
if (!is_char(input) || is_ctl(input) || is_tspecial(input))
{
return false;
}
else
{
state_ = method;
return boost::indeterminate;
}
case method:
if (input == ' ')
{
state_ = uri;
return boost::indeterminate;
}
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
{
return false;
}
else
{
return boost::indeterminate;
}
case uri_start:
if (is_ctl(input))
{
return false;
}
else
{
state_ = uri;
req.uri.push_back(input);
return boost::indeterminate;
}
case uri:
if (input == ' ')
{
state_ = http_version_h;
return boost::indeterminate;
}
else if (is_ctl(input))
{
return false;
}
else
{
req.uri.push_back(input);
return boost::indeterminate;
}
case http_version_h:
if (input == 'H')
{
state_ = http_version_t_1;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_t_1:
if (input == 'T')
{
state_ = http_version_t_2;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_t_2:
if (input == 'T')
{
state_ = http_version_p;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_p:
if (input == 'P')
{
state_ = http_version_slash;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_slash:
if (input == '/')
{
state_ = http_version_major_start;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_major_start:
if (is_digit(input))
{
state_ = http_version_major;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_major:
if (input == '.')
{
state_ = http_version_minor_start;
return boost::indeterminate;
}
else if (is_digit(input))
{
return boost::indeterminate;
}
else
{
return false;
}
case http_version_minor_start:
if (is_digit(input))
{
state_ = http_version_minor;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_minor:
if (input == '\r')
{
state_ = expecting_newline_1;
return boost::indeterminate;
}
else if (is_digit(input))
{
return boost::indeterminate;
}
else
{
return false;
}
case expecting_newline_1:
if (input == '\n')
{
state_ = header_line_start;
return boost::indeterminate;
}
else
{
return false;
}
case header_line_start:
if (input == '\r')
{
state_ = expecting_newline_3;
return boost::indeterminate;
}
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
{
return false;
}
else
{
state_ = header_name;
return boost::indeterminate;
}
case header_lws:
if (input == '\r')
{
state_ = expecting_newline_2;
return boost::indeterminate;
}
else if (input == ' ' || input == '\t')
{
return boost::indeterminate;
}
else if (is_ctl(input))
{
return false;
}
else
{
state_ = header_value;
return boost::indeterminate;
}
case header_name:
if (input == ':')
{
state_ = space_before_header_value;
return boost::indeterminate;
}
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
{
return false;
}
else
{
return boost::indeterminate;
}
case space_before_header_value:
if (input == ' ')
{
state_ = header_value;
return boost::indeterminate;
}
else
{
return false;
}
case header_value:
if (input == '\r')
{
state_ = expecting_newline_2;
return boost::indeterminate;
}
else if (is_ctl(input))
{
return false;
}
else
{
return boost::indeterminate;
}
case expecting_newline_2:
if (input == '\n')
{
state_ = header_line_start;
return boost::indeterminate;
}
else
{
return false;
}
case expecting_newline_3:
return (input == '\n');
default:
return false;
}
}
bool request_parser::is_char(int c)
{
return c >= 0 && c <= 127;
}
bool request_parser::is_ctl(int c)
{
return (c >= 0 && c <= 31) || (c == 127);
}
bool request_parser::is_tspecial(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 request_parser::is_digit(int c)
{
return c >= '0' && c <= '9';
}
} // namespace http
#endif // HTTP_SERVER3_REQUEST_PARSER_HPP