10 #ifndef __PION_HTTPPARSER_HEADER__
11 #define __PION_HTTPPARSER_HEADER__
14 #include <boost/noncopyable.hpp>
15 #include <boost/logic/tribool.hpp>
16 #include <boost/system/error_code.hpp>
17 #include <boost/thread/once.hpp>
18 #include <pion/PionConfig.hpp>
19 #include <pion/PionLogger.hpp>
20 #include <pion/net/HTTPMessage.hpp>
34 private boost::noncopyable
44 ERROR_METHOD_CHAR = 1,
55 ERROR_HEADER_NAME_SIZE,
56 ERROR_HEADER_VALUE_SIZE,
57 ERROR_INVALID_CONTENT_LENGTH,
59 ERROR_MISSING_CHUNK_DATA,
60 ERROR_MISSING_HEADER_DATA,
61 ERROR_MISSING_TOO_MUCH_CONTENT,
66 :
public boost::system::error_category
69 const char *name()
const {
return "HTTPParser"; }
70 std::string message(
int ev)
const {
72 case ERROR_METHOD_CHAR:
73 return "invalid method character";
74 case ERROR_METHOD_SIZE:
75 return "method exceeds maximum size";
77 return "invalid URI character";
79 return "method exceeds maximum size";
80 case ERROR_QUERY_CHAR:
81 return "invalid query string character";
82 case ERROR_QUERY_SIZE:
83 return "query string exceeds maximum size";
84 case ERROR_VERSION_EMPTY:
85 return "HTTP version undefined";
86 case ERROR_VERSION_CHAR:
87 return "invalid version character";
88 case ERROR_STATUS_EMPTY:
89 return "HTTP status undefined";
90 case ERROR_STATUS_CHAR:
91 return "invalid status character";
92 case ERROR_HEADER_CHAR:
93 return "invalid header character";
94 case ERROR_HEADER_NAME_SIZE:
95 return "header name exceeds maximum size";
96 case ERROR_HEADER_VALUE_SIZE:
97 return "header value exceeds maximum size";
98 case ERROR_INVALID_CONTENT_LENGTH:
99 return "invalid Content-Length header";
100 case ERROR_CHUNK_CHAR:
101 return "invalid chunk character";
102 case ERROR_MISSING_HEADER_DATA:
103 return "missing header data";
104 case ERROR_MISSING_CHUNK_DATA:
105 return "missing chunk data";
106 case ERROR_MISSING_TOO_MUCH_CONTENT:
107 return "missing too much content";
109 return "HTTPParser error";
120 HTTPParser(
const bool is_request, std::size_t max_content_length = DEFAULT_CONTENT_MAX)
121 : m_logger(PION_GET_LOGGER(
"pion.net.HTTPParser")), m_is_request(is_request),
122 m_read_ptr(NULL), m_read_end_ptr(NULL), m_message_parse_state(PARSE_START),
123 m_headers_parse_state(is_request ? PARSE_METHOD_START : PARSE_HTTP_VERSION_H),
124 m_chunked_content_parse_state(PARSE_CHUNK_SIZE_START), m_status_code(0),
125 m_bytes_content_remaining(0), m_bytes_content_read(0),
126 m_bytes_last_read(0), m_bytes_total_read(0),
127 m_max_content_length(max_content_length),
128 m_parse_headers_only(false), m_save_raw_headers(false)
145 boost::tribool parse(
HTTPMessage& http_msg, boost::system::error_code& ec);
159 boost::tribool parseMissingData(
HTTPMessage& http_msg, std::size_t len,
160 boost::system::error_code& ec);
177 m_read_end_ptr = ptr + len;
187 read_ptr = m_read_ptr;
188 read_end_ptr = m_read_end_ptr;
200 if (m_message_parse_state != PARSE_CONTENT_NO_LENGTH)
202 m_message_parse_state = PARSE_END;
221 boost::system::error_code ec;
222 finishHeaderParsing(http_msg, ec);
227 m_message_parse_state = PARSE_START;
228 m_headers_parse_state = (m_is_request ? PARSE_METHOD_START : PARSE_HTTP_VERSION_H);
229 m_chunked_content_parse_state = PARSE_CHUNK_SIZE_START;
231 m_status_message.erase();
234 m_query_string.erase();
235 m_raw_headers.erase();
236 m_bytes_content_read = m_bytes_last_read = m_bytes_total_read = 0;
240 inline bool eof(
void)
const {
return m_read_ptr == NULL || m_read_ptr >= m_read_end_ptr; }
243 inline std::size_t
bytes_available(
void)
const {
return (eof() ? 0 : (std::size_t)(m_read_end_ptr - m_read_ptr)); }
246 inline std::size_t
gcount(
void)
const {
return m_bytes_last_read; }
258 inline const std::string&
getRawHeaders(
void)
const {
return m_raw_headers; }
296 const char *ptr,
const std::size_t len);
310 const char *ptr,
const std::size_t len,
311 bool set_cookie_header);
324 const std::string& cookie_header,
bool set_cookie_header)
326 return parseCookieHeader(dict, cookie_header.c_str(), cookie_header.size(), set_cookie_header);
339 const std::string& query)
341 return parseURLEncoded(dict, query.c_str(), query.size());
353 static bool parseForwardedFor(
const std::string& header, std::string& public_ip);
358 return *m_error_category_ptr;
376 boost::tribool parseHeaders(
HTTPMessage& http_msg, boost::system::error_code& ec);
383 void updateMessageWithHeaderData(
HTTPMessage& http_msg)
const;
397 boost::tribool finishHeaderParsing(
HTTPMessage& http_msg,
398 boost::system::error_code& ec);
412 boost::system::error_code& ec);
425 boost::tribool consumeContent(
HTTPMessage& http_msg,
426 boost::system::error_code& ec);
442 static void computeMsgStatus(
HTTPMessage& http_msg,
bool msg_parsed_ok);
451 ec = boost::system::error_code(static_cast<int>(ev), getErrorCategory());
455 static void createErrorCategory(
void);
459 inline static bool isChar(
int c);
460 inline static bool isControl(
int c);
461 inline static bool isSpecial(
int c);
462 inline static bool isDigit(
int c);
463 inline static bool isHexDigit(
int c);
464 inline static bool isCookieAttribute(
const std::string& name,
bool set_cookie_header);
514 enum MessageParseState {
515 PARSE_START, PARSE_HEADERS, PARSE_CONTENT,
516 PARSE_CONTENT_NO_LENGTH, PARSE_CHUNKS, PARSE_END
521 enum HeadersParseState {
522 PARSE_METHOD_START, PARSE_METHOD, PARSE_URI_STEM, PARSE_URI_QUERY,
523 PARSE_HTTP_VERSION_H, PARSE_HTTP_VERSION_T_1, PARSE_HTTP_VERSION_T_2,
524 PARSE_HTTP_VERSION_P, PARSE_HTTP_VERSION_SLASH,
525 PARSE_HTTP_VERSION_MAJOR_START, PARSE_HTTP_VERSION_MAJOR,
526 PARSE_HTTP_VERSION_MINOR_START, PARSE_HTTP_VERSION_MINOR,
527 PARSE_STATUS_CODE_START, PARSE_STATUS_CODE, PARSE_STATUS_MESSAGE,
528 PARSE_EXPECTING_NEWLINE, PARSE_EXPECTING_CR,
529 PARSE_HEADER_WHITESPACE, PARSE_HEADER_START, PARSE_HEADER_NAME,
530 PARSE_SPACE_BEFORE_HEADER_VALUE, PARSE_HEADER_VALUE,
531 PARSE_EXPECTING_FINAL_NEWLINE, PARSE_EXPECTING_FINAL_CR
536 enum ChunkedContentParseState {
537 PARSE_CHUNK_SIZE_START, PARSE_CHUNK_SIZE,
538 PARSE_EXPECTING_CR_AFTER_CHUNK_SIZE,
539 PARSE_EXPECTING_LF_AFTER_CHUNK_SIZE, PARSE_CHUNK,
540 PARSE_EXPECTING_CR_AFTER_CHUNK, PARSE_EXPECTING_LF_AFTER_CHUNK,
541 PARSE_EXPECTING_FINAL_CR_AFTER_LAST_CHUNK,
542 PARSE_EXPECTING_FINAL_LF_AFTER_LAST_CHUNK
547 MessageParseState m_message_parse_state;
550 HeadersParseState m_headers_parse_state;
553 ChunkedContentParseState m_chunked_content_parse_state;
556 boost::uint16_t m_status_code;
559 std::string m_status_message;
562 std::string m_method;
565 std::string m_resource;
568 std::string m_query_string;
571 std::string m_raw_headers;
574 std::string m_header_name;
577 std::string m_header_value;
580 std::string m_chunk_size_str;
583 std::size_t m_size_of_current_chunk;
586 std::size_t m_bytes_read_in_current_chunk;
589 std::size_t m_bytes_content_remaining;
592 std::size_t m_bytes_content_read;
595 std::size_t m_bytes_last_read;
598 std::size_t m_bytes_total_read;
601 std::size_t m_max_content_length;
604 bool m_parse_headers_only;
607 bool m_save_raw_headers;
610 static ErrorCategory * m_error_category_ptr;
613 static boost::once_flag m_instance_flag;
619 inline bool HTTPParser::isChar(
int c)
621 return(c >= 0 && c <= 127);
624 inline bool HTTPParser::isControl(
int c)
626 return( (c >= 0 && c <= 31) || c == 127);
629 inline bool HTTPParser::isSpecial(
int c)
632 case '(':
case ')':
case '<':
case '>':
case '@':
633 case ',':
case ';':
case ':':
case '\\':
case '"':
634 case '/':
case '[':
case ']':
case '?':
case '=':
635 case '{':
case '}':
case ' ':
case '\t':
642 inline bool HTTPParser::isDigit(
int c)
644 return(c >=
'0' && c <=
'9');
647 inline bool HTTPParser::isHexDigit(
int c)
649 return((c >=
'0' && c <=
'9') || (c >=
'a' && c <=
'f') || (c >=
'A' && c <=
'F'));
652 inline bool HTTPParser::isCookieAttribute(
const std::string& name,
bool set_cookie_header)
654 return (name.empty() || name[0] ==
'$' || (set_cookie_header &&
655 (name==
"Comment" || name==
"Domain" || name==
"Max-Age" || name==
"Path" || name==
"Secure" || name==
"Version" || name==
"Expires")
std::size_t getTotalBytesRead(void) const
returns the total number of bytes read while parsing the HTTP message
static void setError(boost::system::error_code &ec, ErrorValue ev)
static const boost::uint32_t METHOD_MAX
maximum length for the request method
static const boost::uint32_t QUERY_NAME_MAX
maximum length for the name of a query string variable
void loadReadPosition(const char *&read_ptr, const char *&read_end_ptr) const
static void createErrorCategory(void)
creates the unique HTTPParser ErrorCategory
void reset(void)
resets the parser to its initial state
const bool m_is_request
true if the message is an HTTP request; false if it is an HTTP response
class-specific error category
void concatenateChunks(void)
std::size_t gcount(void) const
returns the number of bytes read during the last parse operation
static const boost::uint32_t COOKIE_VALUE_MAX
maximum length for the value of a cookie; also used for path and domain
void resetMaxContentLength(void)
resets the maximum length for HTTP payload content to the default value
static const boost::uint32_t RESOURCE_MAX
maximum length for the resource requested
bool isParsingResponse(void) const
returns true if the parser is being used to parse an HTTP response
static const boost::uint32_t HEADER_VALUE_MAX
maximum length for an HTTP header value
ErrorValue
class-specific error code values
static const std::size_t DEFAULT_CONTENT_MAX
maximum length for HTTP payload content
StringDictionary QueryParams
data type for HTTP query parameters
static const boost::uint32_t HEADER_NAME_MAX
maximum length for an HTTP header name
void setMaxContentLength(std::size_t n)
sets the maximum length for HTTP payload content
void skipHeaderParsing(HTTPMessage &http_msg)
HTTPParser(const bool is_request, std::size_t max_content_length=DEFAULT_CONTENT_MAX)
static const boost::uint32_t COOKIE_NAME_MAX
maximum length for the name of a cookie name
StringDictionary CookieParams
data type for HTTP cookie parameters
void setLogger(PionLogger log_ptr)
sets the logger to be used
static bool parseCookieHeader(HTTPTypes::CookieParams &dict, const std::string &cookie_header, bool set_cookie_header)
std::size_t getContentBytesRead(void) const
returns the total number of bytes read while parsing the payload content
static const boost::uint32_t QUERY_VALUE_MAX
maximum length for the value of a query string variable
static const boost::uint32_t STATUS_MESSAGE_MAX
maximum length for response status message
PionLogger getLogger(void)
returns the logger currently in use
static const boost::uint32_t QUERY_STRING_MAX
maximum length for the query string
std::size_t bytes_available(void) const
returns the number of bytes available in the read buffer
static ErrorCategory & getErrorCategory(void)
returns an instance of HTTPParser::ErrorCategory
virtual ~HTTPParser()
default destructor
PionLogger m_logger
primary logging interface used by this class
const char * m_read_ptr
points to the next character to be consumed in the read_buffer
the following enables use of the lock-free cache
static bool parseURLEncoded(HTTPTypes::QueryParams &dict, const std::string &query)
bool checkPrematureEOF(HTTPMessage &http_msg)
std::vector< char > ChunkCache
used to cache chunked data
void setSaveRawHeaders(bool b)
sets parameter for saving raw HTTP header content
bool getSaveRawHeaders(void) const
returns true if the parser is saving raw HTTP header contents
void setReadBuffer(const char *ptr, size_t len)
bool eof(void) const
returns true if there are no more bytes available in the read buffer
void parseHeadersOnly(bool b=true)
const std::string & getRawHeaders(void) const
returns the raw HTTP headers saved by the parser
std::size_t getMaxContentLength(void) const
returns the maximum length for HTTP payload content
const char * m_read_end_ptr
points to the end of the read_buffer (last byte + 1)
bool isParsingRequest(void) const
returns true if the parser is being used to parse an HTTP request