315 lines
12 KiB
C++
315 lines
12 KiB
C++
// This file is part of the Boost Network library
|
|
// Based on the Pion Network Library (r421)
|
|
// Copyright Atomic Labs, Inc. 2007-2008
|
|
// See http://cpp-netlib.sourceforge.net for library home page.
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
|
// http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
#ifndef BOOST_NETWORK_PROTOCOL_HTTP_PARSER_HPP
|
|
#define BOOST_NETWORK_PROTOCOL_HTTP_PARSER_HPP
|
|
|
|
#include <boost/network/protocol/http/traits.hpp>
|
|
#include <boost/network/traits/string.hpp>
|
|
#include <boost/network/message.hpp>
|
|
#include <boost/logic/tribool.hpp>
|
|
#include <boost/cstdint.hpp>
|
|
#include <boost/noncopyable.hpp>
|
|
#include <string>
|
|
|
|
namespace boost { namespace network { namespace http {
|
|
|
|
// forward declarations used to finish HTTP requests
|
|
template <typename Tag>
|
|
class basic_request;
|
|
|
|
// forward declarations used to finish HTTP requests
|
|
template <typename Tag>
|
|
class basic_response;
|
|
|
|
/// an incremental HTTP 1.0/1.1 protocol parser
|
|
template <typename Tag, typename ParserTraits = parser_traits<Tag> >
|
|
class basic_parser :
|
|
private boost::noncopyable
|
|
{
|
|
public:
|
|
|
|
// import types from ParserTraits template
|
|
typedef ParserTraits traits_type;
|
|
typedef typename string<Tag>::type string_type;
|
|
|
|
// default destructor
|
|
virtual ~basic_parser() {}
|
|
|
|
/**
|
|
* creates a new HTTP protocol parser
|
|
*
|
|
* @param is_request if true, the message is parsed as an HTTP request;
|
|
* if false, the message is parsed as an HTTP response
|
|
*/
|
|
basic_parser(const bool is_request) :
|
|
m_is_request(is_request), m_read_ptr(NULL), m_read_end_ptr(NULL),
|
|
m_headers_parse_state(is_request ? PARSE_METHOD_START : PARSE_HTTP_VERSION_H),
|
|
m_chunked_content_parse_state(PARSE_CHUNK_SIZE_START),
|
|
m_status_code(0), m_bytes_last_read(0), m_bytes_total_read(0)
|
|
{}
|
|
|
|
/**
|
|
* parses an HTTP message up to the end of the headers using bytes
|
|
* available in the read buffer
|
|
*
|
|
* @param http_msg the HTTP message object to populate from parsing
|
|
*
|
|
* @return boost::tribool result of parsing:
|
|
* false = message has an error,
|
|
* true = finished parsing HTTP headers,
|
|
* indeterminate = not yet finished parsing HTTP headers
|
|
*/
|
|
boost::tribool parse_http_headers(basic_message<Tag>& http_msg);
|
|
|
|
/**
|
|
* parses a chunked HTTP message-body using bytes available in the read buffer
|
|
*
|
|
* @param chunk_buffers buffers to be populated from parsing chunked content
|
|
*
|
|
* @return boost::tribool result of parsing:
|
|
* false = message has an error,
|
|
* true = finished parsing message,
|
|
* indeterminate = message is not yet finished
|
|
*/
|
|
boost::tribool parse_chunks(types::chunk_cache_t& chunk_buffers);
|
|
|
|
/**
|
|
* prepares the payload content buffer and consumes any content remaining
|
|
* in the parser's read buffer
|
|
*
|
|
* @param http_msg the HTTP message object to consume content for
|
|
* @return unsigned long number of content bytes consumed, if any
|
|
*/
|
|
std::size_t consume_content(basic_message<Tag>& http_msg);
|
|
|
|
/**
|
|
* consume the bytes available in the read buffer, converting them into
|
|
* the next chunk for the HTTP message
|
|
*
|
|
* @param chunk_buffers buffers to be populated from parsing chunked content
|
|
* @return unsigned long number of content bytes consumed, if any
|
|
*/
|
|
std::size_t consume_content_as_next_chunk(types::chunk_cache_t& chunk_buffers);
|
|
|
|
/**
|
|
* finishes parsing an HTTP request message (copies over request-only data)
|
|
*
|
|
* @param http_request the HTTP request object to finish
|
|
*/
|
|
void finish(basic_request<Tag>& http_request);
|
|
|
|
/**
|
|
* finishes an HTTP response message (copies over response-only data)
|
|
*
|
|
* @param http_request the HTTP response object to finish
|
|
*/
|
|
void finish(basic_response<Tag>& http_response);
|
|
|
|
/**
|
|
* resets the location and size of the read buffer
|
|
*
|
|
* @param ptr pointer to the first bytes available to be read
|
|
* @param len number of bytes available to be read
|
|
*/
|
|
inline void set_read_buffer(const char *ptr, std::size_t len) {
|
|
m_read_ptr = ptr;
|
|
m_read_end_ptr = ptr + len;
|
|
}
|
|
|
|
/**
|
|
* saves the current read position bookmark
|
|
*
|
|
* @param read_ptr points to the next character to be consumed in the read_buffer
|
|
* @param read_end_ptr points to the end of the read_buffer (last byte + 1)
|
|
*/
|
|
inline void save_read_position(const char *&read_ptr, const char *&read_end_ptr) const {
|
|
read_ptr = m_read_ptr;
|
|
read_end_ptr = m_read_end_ptr;
|
|
}
|
|
|
|
/// resets the parser to its initial state
|
|
inline void reset(void);
|
|
|
|
/// returns true if there are no more bytes available in the read buffer
|
|
inline bool eof(void) const {
|
|
return m_read_ptr == NULL || m_read_ptr >= m_read_end_ptr;
|
|
}
|
|
|
|
/// returns the number of bytes read during the last parse operation
|
|
inline std::size_t gcount(void) const { return m_bytes_last_read; }
|
|
|
|
/// returns the total number of bytes read while parsing the HTTP message
|
|
inline std::size_t bytes_read(void) const { return m_bytes_total_read; }
|
|
|
|
/// returns the number of bytes available in the read buffer
|
|
inline std::size_t bytes_available(void) const {
|
|
return (eof() ? 0 : (m_read_end_ptr - m_read_ptr));
|
|
}
|
|
|
|
|
|
protected:
|
|
|
|
/**
|
|
* parse key-value pairs out of a url-encoded string
|
|
* (i.e. this=that&a=value)
|
|
*
|
|
* @param params container for key-values string pairs
|
|
* @param ptr points to the start of the encoded string
|
|
* @param len length of the encoded string, in bytes
|
|
*
|
|
* @return bool true if successful
|
|
*/
|
|
static bool parse_url_encoded(types::query_params& params,
|
|
const char *ptr, const std::size_t len);
|
|
|
|
/**
|
|
* parse key-value pairs out of a "Cookie" request header
|
|
* (i.e. this=that; a=value)
|
|
*
|
|
* @param params container for key-values string pairs
|
|
* @param cookie_header header string to be parsed
|
|
*
|
|
* @return bool true if successful
|
|
*/
|
|
static bool parse_cookie_header(types::cookie_params& params,
|
|
const string_type& cookie_header);
|
|
|
|
|
|
/// true if the message is an HTTP request; false if it is an HTTP response
|
|
const bool m_is_request;
|
|
|
|
/// points to the next character to be consumed in the read_buffer
|
|
const char * m_read_ptr;
|
|
|
|
/// points to the end of the read_buffer (last byte + 1)
|
|
const char * m_read_end_ptr;
|
|
|
|
|
|
private:
|
|
|
|
// returns true if the argument is a special character
|
|
inline static bool is_special(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;
|
|
}
|
|
}
|
|
|
|
// returns true if the argument is a character
|
|
inline static bool is_char(int c) {
|
|
return(c >= 0 && c <= 127);
|
|
}
|
|
|
|
// returns true if the argument is a control character
|
|
inline static bool is_control(int c) {
|
|
return( (c >= 0 && c <= 31) || c == 127);
|
|
}
|
|
|
|
// returns true if the argument is a digit
|
|
inline static bool is_digit(int c) {
|
|
return(c >= '0' && c <= '9');
|
|
}
|
|
|
|
// returns true if the argument is a hexadecimal digit
|
|
inline static bool is_hex_digit(int c) {
|
|
return((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
|
|
}
|
|
|
|
|
|
/// state used to keep track of where we are in parsing the HTTP headers
|
|
enum headers_parse_state_t {
|
|
PARSE_METHOD_START, PARSE_METHOD, PARSE_URI_STEM, PARSE_URI_QUERY,
|
|
PARSE_HTTP_VERSION_H, PARSE_HTTP_VERSION_T_1, PARSE_HTTP_VERSION_T_2,
|
|
PARSE_HTTP_VERSION_P, PARSE_HTTP_VERSION_SLASH,
|
|
PARSE_HTTP_VERSION_MAJOR_START, PARSE_HTTP_VERSION_MAJOR,
|
|
PARSE_HTTP_VERSION_MINOR_START, PARSE_HTTP_VERSION_MINOR,
|
|
PARSE_STATUS_CODE_START, PARSE_STATUS_CODE, PARSE_STATUS_MESSAGE,
|
|
PARSE_EXPECTING_NEWLINE, PARSE_EXPECTING_CR,
|
|
PARSE_HEADER_WHITESPACE, PARSE_HEADER_START, PARSE_HEADER_NAME,
|
|
PARSE_SPACE_BEFORE_HEADER_VALUE, PARSE_HEADER_VALUE,
|
|
PARSE_EXPECTING_FINAL_NEWLINE, PARSE_EXPECTING_FINAL_CR
|
|
};
|
|
|
|
/// state used to keep track of where we are in parsing chunked content
|
|
enum chunked_content_parse_state_t {
|
|
PARSE_CHUNK_SIZE_START, PARSE_CHUNK_SIZE,
|
|
PARSE_EXPECTING_CR_AFTER_CHUNK_SIZE,
|
|
PARSE_EXPECTING_LF_AFTER_CHUNK_SIZE, PARSE_CHUNK,
|
|
PARSE_EXPECTING_CR_AFTER_CHUNK, PARSE_EXPECTING_LF_AFTER_CHUNK,
|
|
PARSE_EXPECTING_FINAL_CR_AFTER_LAST_CHUNK,
|
|
PARSE_EXPECTING_FINAL_LF_AFTER_LAST_CHUNK
|
|
};
|
|
|
|
|
|
/// the current state of parsing HTTP headers
|
|
headers_parse_state_t m_headers_parse_state;
|
|
|
|
/// the current state of parsing chunked content
|
|
chunked_content_parse_state_t m_chunked_content_parse_state;
|
|
|
|
/// Used for parsing the HTTP response status code
|
|
boost::uint16_t m_status_code;
|
|
|
|
/// Used for parsing the HTTP response status message
|
|
string_type m_status_message;
|
|
|
|
/// Used for parsing the request method
|
|
string_type m_method;
|
|
|
|
/// Used for parsing the name of resource requested
|
|
string_type m_resource;
|
|
|
|
/// Used for parsing the query string portion of a URI
|
|
string_type m_query_string;
|
|
|
|
/// Used for parsing the name of HTTP headers
|
|
string_type m_header_name;
|
|
|
|
/// Used for parsing the value of HTTP headers
|
|
string_type m_header_value;
|
|
|
|
/// Used for parsing the chunk size
|
|
string_type m_chunk_size_str;
|
|
|
|
/// Used for parsing the current chunk
|
|
std::vector<char> m_current_chunk;
|
|
|
|
/// number of bytes in the chunk currently being parsed
|
|
std::size_t m_size_of_current_chunk;
|
|
|
|
/// number of bytes read so far in the chunk currently being parsed
|
|
std::size_t m_bytes_read_in_current_chunk;
|
|
|
|
/// number of bytes read during last parse operation
|
|
std::size_t m_bytes_last_read;
|
|
|
|
/// total number of bytes read while parsing the HTTP message
|
|
std::size_t m_bytes_total_read;
|
|
};
|
|
|
|
/// typedef for the default HTTP protocol parser implementation
|
|
typedef basic_parser<tags::default_> parser;
|
|
|
|
}; // namespace http
|
|
|
|
}; // namespace network
|
|
|
|
}; // namespace boost
|
|
|
|
// import implementation file
|
|
#include <boost/network/protocol/http/impl/parser.ipp>
|
|
|
|
#endif // BOOST_NETWORK_PROTOCOL_HTTP_PARSER_HPP
|