📄 httprequestparser.cpp
字号:
// -----------------------------------------------------------------// libpion: a C++ framework for building lightweight HTTP interfaces// -----------------------------------------------------------------// Copyright (C) 2007 Atomic Labs, Inc. (http://www.atomiclabs.com)//// Distributed under the Boost Software License, Version 1.0.// See accompanying file COPYING or copy at http://www.boost.org/LICENSE_1_0.txt//#include <libpion/HTTPRequestParser.hpp>#include <boost/bind.hpp>#include <boost/asio.hpp>namespace pion { // begin namespace pion // static members of HTTPRequestParserconst unsigned int HTTPRequestParser::METHOD_MAX = 1024; // 1 KBconst unsigned int HTTPRequestParser::RESOURCE_MAX = 256 * 1024; // 256 KBconst unsigned int HTTPRequestParser::QUERY_STRING_MAX = 1024 * 1024; // 1 MBconst unsigned int HTTPRequestParser::HEADER_NAME_MAX = 1024; // 1 KBconst unsigned int HTTPRequestParser::HEADER_VALUE_MAX = 1024 * 1024; // 1 MBconst unsigned int HTTPRequestParser::QUERY_NAME_MAX = 1024; // 1 KBconst unsigned int HTTPRequestParser::QUERY_VALUE_MAX = 1024 * 1024; // 1 MBconst unsigned int HTTPRequestParser::COOKIE_NAME_MAX = 1024; // 1 KBconst unsigned int HTTPRequestParser::COOKIE_VALUE_MAX = 1024 * 1024; // 1 MBconst unsigned int HTTPRequestParser::POST_CONTENT_MAX = 1024 * 1024; // 1 MB // HTTPRequestParser member functionsvoid HTTPRequestParser::readRequest(void){ if (m_tcp_conn->getSSLFlag()) {#ifdef PION_HAVE_SSL m_tcp_conn->getSSLSocket().async_read_some(boost::asio::buffer(m_read_buffer), boost::bind(&HTTPRequestParser::readHeaderBytes, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));#else PION_LOG_ERROR(m_logger, "SSL flag set for server, but support is not enabled"); m_tcp_conn->finish();#endif } else { m_tcp_conn->getSocket().async_read_some(boost::asio::buffer(m_read_buffer), boost::bind(&HTTPRequestParser::readHeaderBytes, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); }}void HTTPRequestParser::readHeaderBytes(const boost::system::error_code& read_error, std::size_t bytes_read){ if (read_error) { // a read error occured handleReadError(read_error); return; } PION_LOG_DEBUG(m_logger, "Read " << bytes_read << " bytes from HTTP request"); // parse the bytes read from the last operation const char *read_ptr = m_read_buffer.data(); const char * const read_end = read_ptr + bytes_read; boost::tribool result = parseRequestHeaders(read_ptr, bytes_read); PION_LOG_DEBUG(m_logger, "Parsed " << (read_ptr - m_read_buffer.data()) << " HTTP header bytes"); if (result) { // finished reading request headers and they are valid // check if we have post content to read const unsigned long content_length = m_http_request->hasHeader(HTTPTypes::HEADER_CONTENT_LENGTH) ? strtoul(m_http_request->getHeader(HTTPTypes::HEADER_CONTENT_LENGTH).c_str(), 0, 10) : 0; if (content_length == 0) { // there is no post content to read readContentBytes(read_error, 0); } else { // read the post content unsigned long content_bytes_to_read = content_length; m_http_request->setContentLength(content_length); char *post_buffer = m_http_request->createPostContentBuffer(); if (read_ptr < read_end) { // there are extra bytes left from the last read operation // copy them into the beginning of the content buffer const unsigned int bytes_left_in_read_buffer = read_end - read_ptr; if (bytes_left_in_read_buffer >= content_length) { // the last read operation included all of the post content memcpy(post_buffer, read_ptr, content_length); content_bytes_to_read = 0; PION_LOG_DEBUG(m_logger, "Parsed " << content_length << " request content bytes from last read operation (finished)"); } else { // only some of the post content has been read so far memcpy(post_buffer, read_ptr, bytes_left_in_read_buffer); content_bytes_to_read -= bytes_left_in_read_buffer; post_buffer += bytes_left_in_read_buffer; PION_LOG_DEBUG(m_logger, "Parsed " << bytes_left_in_read_buffer << " request content bytes from last read operation (partial)"); } } if (content_bytes_to_read == 0) { // read all of the content from the last read operation readContentBytes(read_error, 0); } else { // read the rest of the post content into the buffer // and only return after we've finished or an error occurs if (m_tcp_conn->getSSLFlag()) {#ifdef PION_HAVE_SSL boost::asio::async_read(m_tcp_conn->getSSLSocket(), boost::asio::buffer(post_buffer, content_bytes_to_read), boost::asio::transfer_at_least(content_bytes_to_read), boost::bind(&HTTPRequestParser::readContentBytes, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));#else PION_LOG_ERROR(m_logger, "SSL flag set for server, but support is not enabled"); m_tcp_conn->finish();#endif } else { boost::asio::async_read(m_tcp_conn->getSocket(), boost::asio::buffer(post_buffer, content_bytes_to_read), boost::asio::transfer_at_least(content_bytes_to_read), boost::bind(&HTTPRequestParser::readContentBytes, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } } } } else if (!result) { // the request is invalid or an error occured #ifndef NDEBUG // display extra error information if debug mode is enabled std::string bad_request; read_ptr = m_read_buffer.data(); while (read_ptr < read_end && bad_request.size() < 50) { if (!isprint(*read_ptr) || *read_ptr == '\n' || *read_ptr=='\r') bad_request += '.'; else bad_request += *read_ptr; ++read_ptr; } PION_LOG_ERROR(m_logger, "Bad request debug: " << bad_request); #endif m_http_request->setIsValid(false); m_request_handler(m_http_request, m_tcp_conn); } else { // not yet finished parsing the request -> read more data readRequest(); }}void HTTPRequestParser::readContentBytes(const boost::system::error_code& read_error, std::size_t bytes_read){ if (read_error) { // a read error occured handleReadError(read_error); return; } if (bytes_read != 0) { PION_LOG_DEBUG(m_logger, "Read " << bytes_read << " request content bytes (finished)"); } // the request is valid m_http_request->setIsValid(true); // parse query pairs from the URI query string if (! m_http_request->getQueryString().empty()) { if (! parseURLEncoded(m_http_request->getQueryParams(), m_http_request->getQueryString().c_str(), m_http_request->getQueryString().size())) PION_LOG_WARN(m_logger, "Request query string parsing failed (URI)"); } // parse query pairs from post content (x-www-form-urlencoded) if (m_http_request->getHeader(HTTPTypes::HEADER_CONTENT_TYPE) == HTTPTypes::CONTENT_TYPE_URLENCODED) { if (! parseURLEncoded(m_http_request->getQueryParams(), m_http_request->getPostContent(), m_http_request->getContentLength())) PION_LOG_WARN(m_logger, "Request query string parsing failed (POST content)"); } // parse "Cookie" headers std::pair<HTTPTypes::Headers::const_iterator, HTTPTypes::Headers::const_iterator> cookie_pair = m_http_request->getHeaders().equal_range(HTTPTypes::HEADER_COOKIE); for (HTTPTypes::Headers::const_iterator cookie_iterator = cookie_pair.first; cookie_iterator != m_http_request->getCookieParams().end() && cookie_iterator != cookie_pair.second; ++cookie_iterator) { if (! parseCookieHeader(m_http_request->getCookieParams(), cookie_iterator->second) ) PION_LOG_WARN(m_logger, "Cookie header parsing failed"); } // call the request handler with the finished request m_request_handler(m_http_request, m_tcp_conn);}void HTTPRequestParser::handleReadError(const boost::system::error_code& read_error){ // only log errors if the parsing has already begun if (m_parse_state != PARSE_METHOD_START) { if (read_error == boost::asio::error::operation_aborted) { // if the operation was aborted, the acceptor was stopped, // which means another thread is shutting-down the server PION_LOG_INFO(m_logger, "HTTP request parsing aborted (shutting down)"); } else { PION_LOG_INFO(m_logger, "HTTP request parsing aborted (" << read_error.message() << ')'); } } // close the connection, forcing the client to establish a new one m_tcp_conn->finish();}boost::tribool HTTPRequestParser::parseRequestHeaders(const char *& ptr, const size_t len){ // parse characters available in the read buffer const char * const end = ptr + len; while (ptr < end) { switch (m_parse_state) { case PARSE_METHOD_START: // we have not yet started parsing the HTTP method string if (*ptr != ' ' && *ptr!='\r' && *ptr!='\n') { // ignore leading whitespace if (!isChar(*ptr) || isControl(*ptr) || isSpecial(*ptr)) return false; m_parse_state = PARSE_METHOD; m_method.erase(); m_method.push_back(*ptr); } break; case PARSE_METHOD: // we have started parsing the HTTP method string if (*ptr == ' ') { m_http_request->setMethod(m_method); m_resource.erase(); m_parse_state = PARSE_URI_STEM; } else if (!isChar(*ptr) || isControl(*ptr) || isSpecial(*ptr)) { return false; } else if (m_method.size() >= METHOD_MAX) { return false; } else { m_method.push_back(*ptr); } break; case PARSE_URI_STEM: // we have started parsing the URI stem (or resource name) if (*ptr == ' ') { m_http_request->setResource(m_resource); m_parse_state = PARSE_HTTP_VERSION_H; } else if (*ptr == '?') { m_http_request->setResource(m_resource); m_query_string.erase(); m_parse_state = PARSE_URI_QUERY; } else if (isControl(*ptr)) { return false; } else if (m_resource.size() >= RESOURCE_MAX) { return false; } else { m_resource.push_back(*ptr); } break; case PARSE_URI_QUERY: // we have started parsing the URI query string if (*ptr == ' ') { m_http_request->setQueryString(m_query_string); m_parse_state = PARSE_HTTP_VERSION_H; } else if (isControl(*ptr)) { return false; } else if (m_query_string.size() >= QUERY_STRING_MAX) { return false; } else { m_query_string.push_back(*ptr); } break; case PARSE_HTTP_VERSION_H: // parsing "HTTP" if (*ptr != 'H') return false; m_parse_state = PARSE_HTTP_VERSION_T_1; break; case PARSE_HTTP_VERSION_T_1: // parsing "HTTP" if (*ptr != 'T') return false; m_parse_state = PARSE_HTTP_VERSION_T_2; break; case PARSE_HTTP_VERSION_T_2: // parsing "HTTP" if (*ptr != 'T') return false; m_parse_state = PARSE_HTTP_VERSION_P; break; case PARSE_HTTP_VERSION_P: // parsing "HTTP" if (*ptr != 'P') return false; m_parse_state = PARSE_HTTP_VERSION_SLASH; break; case PARSE_HTTP_VERSION_SLASH: // parsing slash after "HTTP" if (*ptr != '/') return false; m_parse_state = PARSE_HTTP_VERSION_MAJOR_START; break; case PARSE_HTTP_VERSION_MAJOR_START: // parsing the first digit of the major version number if (!isDigit(*ptr)) return false; m_http_request->setVersionMajor(*ptr - '0'); m_parse_state = PARSE_HTTP_VERSION_MAJOR; break; case PARSE_HTTP_VERSION_MAJOR: // parsing the major version number (not first digit) if (*ptr == '.') { m_parse_state = PARSE_HTTP_VERSION_MINOR_START; } else if (isDigit(*ptr)) { m_http_request->setVersionMajor( (m_http_request->getVersionMajor() * 10)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -