- Main Page
- Classes
- Files

- File List

# src/http_parser.cpp

    00001 // ---------------------------------------------------------------------
    00002 // pion:  a Boost C++ framework for building lightweight HTTP interfaces
    00003 // ---------------------------------------------------------------------
    00004 // Copyright (C) 2007-2012 Cloudmeter, Inc.  (http://www.cloudmeter.com)
    00005 //
    00006 // Distributed under the Boost Software License, Version 1.0.
    00007 // See http://www.boost.org/LICENSE_1_0.txt
    00008 //
    00009
    00010 #include <cstdlib>
    00011 #include <cstring>
    00012 #include <boost/regex.hpp>
    00013 #include <boost/assert.hpp>
    00014 #include <boost/logic/tribool.hpp>
    00015 #include <boost/algorithm/string.hpp>
    00016 #include <pion/algorithm.hpp>
    00017 #include <pion/http/parser.hpp>
    00018 #include <pion/http/request.hpp>
    00019 #include <pion/http/response.hpp>
    00020 #include <pion/http/message.hpp>
    00021
    00022
    00023 namespace pion {    // begin namespace pion
    00024 namespace http {    // begin namespace http
    00025
    00026
    00027 // static members of parser
    00028
    00029 const boost::uint32_t   parser::STATUS_MESSAGE_MAX = 1024;  // 1 KB
    00030 const boost::uint32_t   parser::METHOD_MAX = 1024;  // 1 KB
    00031 const boost::uint32_t   parser::RESOURCE_MAX = 256 * 1024;  // 256 KB
    00032 const boost::uint32_t   parser::QUERY_STRING_MAX = 1024 * 1024; // 1 MB
    00033 const boost::uint32_t   parser::HEADER_NAME_MAX = 1024; // 1 KB
    00034 const boost::uint32_t   parser::HEADER_VALUE_MAX = 1024 * 1024; // 1 MB
    00035 const boost::uint32_t   parser::QUERY_NAME_MAX = 1024;  // 1 KB
    00036 const boost::uint32_t   parser::QUERY_VALUE_MAX = 1024 * 1024;  // 1 MB
    00037 const boost::uint32_t   parser::COOKIE_NAME_MAX = 1024; // 1 KB
    00038 const boost::uint32_t   parser::COOKIE_VALUE_MAX = 1024 * 1024; // 1 MB
    00039 const std::size_t       parser::DEFAULT_CONTENT_MAX = 1024 * 1024;  // 1 MB
    00040 parser::error_category_t * parser::m_error_category_ptr = NULL;
    00041 boost::once_flag            parser::m_instance_flag = BOOST_ONCE_INIT;
    00042
    00043
    00044 // parser member functions
    00045
    00046 boost::tribool parser::parse(http::message& http_msg,
    00047     boost::system::error_code& ec)
    00048 {
    00049     BOOST_ASSERT(! eof() );
    00050
    00051     boost::tribool rc = boost::indeterminate;
    00052     std::size_t total_bytes_parsed = 0;
    00053
    00054     if(http_msg.has_missing_packets()) {
    00055         http_msg.set_data_after_missing_packet(true);
    00056     }
    00057
    00058     do {
    00059         switch (m_message_parse_state) {
    00060             // just started parsing the HTTP message
    00061             case PARSE_START:
    00062                 m_message_parse_state = PARSE_HEADERS;
    00063                 // step through to PARSE_HEADERS
    00064
    00065             // parsing the HTTP headers
    00066             case PARSE_HEADERS:
    00067             case PARSE_FOOTERS:
    00068                 rc = parse_headers(http_msg, ec);
    00069                 total_bytes_parsed += m_bytes_last_read;
    00070                 // check if we have finished parsing HTTP headers
    00071                 if (rc == true && m_message_parse_state == PARSE_HEADERS) {
    00072                     // finish_header_parsing() updates m_message_parse_state
    00073                     // We only call this for Headers and not Footers
    00074                     rc = finish_header_parsing(http_msg, ec);
    00075                 }
    00076                 break;
    00077
    00078             // parsing chunked payload content
    00079             case PARSE_CHUNKS:
    00080                 rc = parse_chunks(http_msg.get_chunk_cache(), ec);
    00081                 total_bytes_parsed += m_bytes_last_read;
    00082                 // check if we have finished parsing all chunks
    00083                 if (rc == true && !m_payload_handler) {
    00084                     http_msg.concatenate_chunks();
    00085
    00086                     // Handle footers if present
    00087                     rc = ((m_message_parse_state == PARSE_FOOTERS) ?
    00088                           boost::indeterminate : (boost::tribool)true);
    00089                 }
    00090                 break;
    00091
    00092             // parsing regular payload content with a known length
    00093             case PARSE_CONTENT:
    00094                 rc = consume_content(http_msg, ec);
    00095                 total_bytes_parsed += m_bytes_last_read;
    00096                 break;
    00097
    00098             // parsing payload content with no length (until EOF)
    00099             case PARSE_CONTENT_NO_LENGTH:
    00100                 consume_content_as_next_chunk(http_msg.get_chunk_cache());
    00101                 total_bytes_parsed += m_bytes_last_read;
    00102                 break;
    00103
    00104             // finished parsing the HTTP message
    00105             case PARSE_END:
    00106                 rc = true;
    00107                 break;
    00108         }
    00109     } while ( boost::indeterminate(rc) && ! eof() );
    00110
    00111     // check if we've finished parsing the HTTP message
    00112     if (rc == true) {
    00113         m_message_parse_state = PARSE_END;
    00114         finish(http_msg);
    00115     } else if(rc == false) {
    00116         compute_msg_status(http_msg, false);
    00117     }
    00118
    00119     // update bytes last read (aggregate individual operations for caller)
    00120     m_bytes_last_read = total_bytes_parsed;
    00121
    00122     return rc;
    00123 }
    00124
    00125 boost::tribool parser::parse_missing_data(http::message& http_msg,
    00126     std::size_t len, boost::system::error_code& ec)
    00127 {
    00128     static const char MISSING_DATA_CHAR = 'X';
    00129     boost::tribool rc = boost::indeterminate;
    00130
    00131     http_msg.set_missing_packets(true);
    00132
    00133     switch (m_message_parse_state) {
    00134
    00135         // cannot recover from missing data while parsing HTTP headers
    00136         case PARSE_START:
    00137         case PARSE_HEADERS:
    00138         case PARSE_FOOTERS:
    00139             set_error(ec, ERROR_MISSING_HEADER_DATA);
    00140             rc = false;
    00141             break;
    00142
    00143         // parsing chunked payload content
    00144         case PARSE_CHUNKS:
    00145             // parsing chunk data -> we can only recover if data fits into current chunk
    00146             if (m_chunked_content_parse_state == PARSE_CHUNK
    00147                 && m_bytes_read_in_current_chunk < m_size_of_current_chunk
    00148                 && (m_size_of_current_chunk - m_bytes_read_in_current_chunk) >= len)
    00149             {
    00150                 // use dummy content for missing data
    00151                 if (m_payload_handler) {
    00152                     for (std::size_t n = 0; n < len; ++n)
    00153                         m_payload_handler(&MISSING_DATA_CHAR, 1);
    00154                 } else {
    00155                     for (std::size_t n = 0; n < len && http_msg.get_chunk_cache().size() < m_max_content_length; ++n)
    00156                         http_msg.get_chunk_cache().push_back(MISSING_DATA_CHAR);
    00157                 }
    00158
    00159                 m_bytes_read_in_current_chunk += len;
    00160                 m_bytes_last_read = len;
    00161                 m_bytes_total_read += len;
    00162                 m_bytes_content_read += len;
    00163
    00164                 if (m_bytes_read_in_current_chunk == m_size_of_current_chunk) {
    00165                     m_chunked_content_parse_state = PARSE_EXPECTING_CR_AFTER_CHUNK;
    00166                 }
    00167             } else {
    00168                 // cannot recover from missing data
    00169                 set_error(ec, ERROR_MISSING_CHUNK_DATA);
    00170                 rc = false;
    00171             }
    00172             break;
    00173
    00174         // parsing regular payload content with a known length
    00175         case PARSE_CONTENT:
    00176             // parsing content (with length) -> we can only recover if data fits into content
    00177             if (m_bytes_content_remaining == 0) {
    00178                 // we have all of the remaining payload content
    00179                 rc = true;
    00180             } else if (m_bytes_content_remaining < len) {
    00181                 // cannot recover from missing data
    00182                 set_error(ec, ERROR_MISSING_TOO_MUCH_CONTENT);
    00183                 rc = false;
    00184             } else {
    00185
    00186                 // make sure content buffer is not already full
    00187                 if (m_payload_handler) {
    00188                     for (std::size_t n = 0; n < len; ++n)
    00189                         m_payload_handler(&MISSING_DATA_CHAR, 1);
    00190                 } else if ( (m_bytes_content_read+len) <= m_max_content_length) {
    00191                     // use dummy content for missing data
    00192                     for (std::size_t n = 0; n < len; ++n)
    00193                         http_msg.get_content()[m_bytes_content_read++] = MISSING_DATA_CHAR;
    00194                 } else {
    00195                     m_bytes_content_read += len;
    00196                 }
    00197
    00198                 m_bytes_content_remaining -= len;
    00199                 m_bytes_total_read += len;
    00200                 m_bytes_last_read = len;
    00201
    00202                 if (m_bytes_content_remaining == 0)
    00203                     rc = true;
    00204             }
    00205             break;
    00206
    00207         // parsing payload content with no length (until EOF)
    00208         case PARSE_CONTENT_NO_LENGTH:
    00209             // use dummy content for missing data
    00210             if (m_payload_handler) {
    00211                 for (std::size_t n = 0; n < len; ++n)
    00212                     m_payload_handler(&MISSING_DATA_CHAR, 1);
    00213             } else {
    00214                 for (std::size_t n = 0; n < len && http_msg.get_chunk_cache().size() < m_max_content_length; ++n)
    00215                     http_msg.get_chunk_cache().push_back(MISSING_DATA_CHAR);
    00216             }
    00217             m_bytes_last_read = len;
    00218             m_bytes_total_read += len;
    00219             m_bytes_content_read += len;
    00220             break;
    00221
    00222         // finished parsing the HTTP message
    00223         case PARSE_END:
    00224             rc = true;
    00225             break;
    00226     }
    00227
    00228     // check if we've finished parsing the HTTP message
    00229     if (rc == true) {
    00230         m_message_parse_state = PARSE_END;
    00231         finish(http_msg);
    00232     } else if(rc == false) {
    00233         compute_msg_status(http_msg, false);
    00234     }
    00235
    00236     return rc;
    00237 }
    00238
    00239 boost::tribool parser::parse_headers(http::message& http_msg,
    00240     boost::system::error_code& ec)
    00241 {
    00242     //
    00243     // note that boost::tribool may have one of THREE states:
    00244     //
    00245     // false: encountered an error while parsing HTTP headers
    00246     // true: finished successfully parsing the HTTP headers
    00247     // indeterminate: parsed bytes, but the HTTP headers are not yet finished
    00248     //
    00249     const char *read_start_ptr = m_read_ptr;
    00250     m_bytes_last_read = 0;
    00251     while (m_read_ptr < m_read_end_ptr) {
    00252
    00253         if (m_save_raw_headers)
    00254             m_raw_headers += *m_read_ptr;
    00255
    00256         switch (m_headers_parse_state) {
    00257         case PARSE_METHOD_START:
    00258             // we have not yet started parsing the HTTP method string
    00259             if (*m_read_ptr != ' ' && *m_read_ptr!='\r' && *m_read_ptr!='\n') { // ignore leading whitespace
    00260                 if (!is_char(*m_read_ptr) || is_control(*m_read_ptr) || is_special(*m_read_ptr)) {
    00261                     set_error(ec, ERROR_METHOD_CHAR);
    00262                     return false;
    00263                 }
    00264                 m_headers_parse_state = PARSE_METHOD;
    00265                 m_method.erase();
    00266                 m_method.push_back(*m_read_ptr);
    00267             }
    00268             break;
    00269
    00270         case PARSE_METHOD:
    00271             // we have started parsing the HTTP method string
    00272             if (*m_read_ptr == ' ') {
    00273                 m_resource.erase();
    00274                 m_headers_parse_state = PARSE_URI_STEM;
    00275             } else if (!is_char(*m_read_ptr) || is_control(*m_read_ptr) || is_special(*m_read_ptr)) {
    00276                 set_error(ec, ERROR_METHOD_CHAR);
    00277                 return false;
    00278             } else if (m_method.size() >= METHOD_MAX) {
    00279                 set_error(ec, ERROR_METHOD_SIZE);
    00280                 return false;
    00281             } else {
    00282                 m_method.push_back(*m_read_ptr);
    00283             }
    00284             break;
    00285
    00286         case PARSE_URI_STEM:
    00287             // we have started parsing the URI stem (or resource name)
    00288             if (*m_read_ptr == ' ') {
    00289                 m_headers_parse_state = PARSE_HTTP_VERSION_H;
    00290             } else if (*m_read_ptr == '?') {
    00291                 m_query_string.erase();
    00292                 m_headers_parse_state = PARSE_URI_QUERY;
    00293             } else if (*m_read_ptr == '\r') {
    00294                 http_msg.set_version_major(0);
    00295                 http_msg.set_version_minor(0);
    00296                 m_headers_parse_state = PARSE_EXPECTING_NEWLINE;
    00297             } else if (*m_read_ptr == '\n') {
    00298                 http_msg.set_version_major(0);
    00299                 http_msg.set_version_minor(0);
    00300                 m_headers_parse_state = PARSE_EXPECTING_CR;
    00301             } else if (is_control(*m_read_ptr)) {
    00302                 set_error(ec, ERROR_URI_CHAR);
    00303                 return false;
    00304             } else if (m_resource.size() >= RESOURCE_MAX) {
    00305                 set_error(ec, ERROR_URI_SIZE);
    00306                 return false;
    00307             } else {
    00308                 m_resource.push_back(*m_read_ptr);
    00309             }
    00310             break;
    00311
    00312         case PARSE_URI_QUERY:
    00313             // we have started parsing the URI query string
    00314             if (*m_read_ptr == ' ') {
    00315                 m_headers_parse_state = PARSE_HTTP_VERSION_H;
    00316             } else if (*m_read_ptr == '\r') {
    00317                 http_msg.set_version_major(0);
    00318                 http_msg.set_version_minor(0);
    00319                 m_headers_parse_state = PARSE_EXPECTING_NEWLINE;
    00320             } else if (*m_read_ptr == '\n') {
    00321                 http_msg.set_version_major(0);
    00322                 http_msg.set_version_minor(0);
    00323                 m_headers_parse_state = PARSE_EXPECTING_CR;
    00324             } else if (is_control(*m_read_ptr)) {
    00325                 set_error(ec, ERROR_QUERY_CHAR);
    00326                 return false;
    00327             } else if (m_query_string.size() >= QUERY_STRING_MAX) {
    00328                 set_error(ec, ERROR_QUERY_SIZE);
    00329                 return false;
    00330             } else {
    00331                 m_query_string.push_back(*m_read_ptr);
    00332             }
    00333             break;
    00334
    00335         case PARSE_HTTP_VERSION_H:
    00336             // parsing "HTTP"
    00337             if (*m_read_ptr == '\r') {
    00338                 // should only happen for requests (no HTTP/VERSION specified)
    00339                 if (! m_is_request) {
    00340                     set_error(ec, ERROR_VERSION_EMPTY);
    00341                     return false;
    00342                 }
    00343                 http_msg.set_version_major(0);
    00344                 http_msg.set_version_minor(0);
    00345                 m_headers_parse_state = PARSE_EXPECTING_NEWLINE;
    00346             } else if (*m_read_ptr == '\n') {
    00347                 // should only happen for requests (no HTTP/VERSION specified)
    00348                 if (! m_is_request) {
    00349                     set_error(ec, ERROR_VERSION_EMPTY);
    00350                     return false;
    00351                 }
    00352                 http_msg.set_version_major(0);
    00353                 http_msg.set_version_minor(0);
    00354                 m_headers_parse_state = PARSE_EXPECTING_CR;
    00355             } else if (*m_read_ptr != 'H') {
    00356                 set_error(ec, ERROR_VERSION_CHAR);
    00357                 return false;
    00358             }
    00359             m_headers_parse_state = PARSE_HTTP_VERSION_T_1;
    00360             break;
    00361
    00362         case PARSE_HTTP_VERSION_T_1:
    00363             // parsing "HTTP"
    00364             if (*m_read_ptr != 'T') {
    00365                 set_error(ec, ERROR_VERSION_CHAR);
    00366                 return false;
    00367             }
    00368             m_headers_parse_state = PARSE_HTTP_VERSION_T_2;
    00369             break;
    00370
    00371         case PARSE_HTTP_VERSION_T_2:
    00372             // parsing "HTTP"
    00373             if (*m_read_ptr != 'T') {
    00374                 set_error(ec, ERROR_VERSION_CHAR);
    00375                 return false;
    00376             }
    00377             m_headers_parse_state = PARSE_HTTP_VERSION_P;
    00378             break;
    00379
    00380         case PARSE_HTTP_VERSION_P:
    00381             // parsing "HTTP"
    00382             if (*m_read_ptr != 'P') {
    00383                 set_error(ec, ERROR_VERSION_CHAR);
    00384                 return false;
    00385             }
    00386             m_headers_parse_state = PARSE_HTTP_VERSION_SLASH;
    00387             break;
    00388
    00389         case PARSE_HTTP_VERSION_SLASH:
    00390             // parsing slash after "HTTP"
    00391             if (*m_read_ptr != '/') {
    00392                 set_error(ec, ERROR_VERSION_CHAR);
    00393                 return false;
    00394             }
    00395             m_headers_parse_state = PARSE_HTTP_VERSION_MAJOR_START;
    00396             break;
    00397
    00398         case PARSE_HTTP_VERSION_MAJOR_START:
    00399             // parsing the first digit of the major version number
    00400             if (!is_digit(*m_read_ptr)) {
    00401                 set_error(ec, ERROR_VERSION_CHAR);
    00402                 return false;
    00403             }
    00404             http_msg.set_version_major(*m_read_ptr - '0');
    00405             m_headers_parse_state = PARSE_HTTP_VERSION_MAJOR;
    00406             break;
    00407
    00408         case PARSE_HTTP_VERSION_MAJOR:
    00409             // parsing the major version number (not first digit)
    00410             if (*m_read_ptr == '.') {
    00411                 m_headers_parse_state = PARSE_HTTP_VERSION_MINOR_START;
    00412             } else if (is_digit(*m_read_ptr)) {
    00413                 http_msg.set_version_major( (http_msg.get_version_major() * 10)
    00414                                           + (*m_read_ptr - '0') );
    00415             } else {
    00416                 set_error(ec, ERROR_VERSION_CHAR);
    00417                 return false;
    00418             }
    00419             break;
    00420
    00421         case PARSE_HTTP_VERSION_MINOR_START:
    00422             // parsing the first digit of the minor version number
    00423             if (!is_digit(*m_read_ptr)) {
    00424                 set_error(ec, ERROR_VERSION_CHAR);
    00425                 return false;
    00426             }
    00427             http_msg.set_version_minor(*m_read_ptr - '0');
    00428             m_headers_parse_state = PARSE_HTTP_VERSION_MINOR;
    00429             break;
    00430
    00431         case PARSE_HTTP_VERSION_MINOR:
    00432             // parsing the major version number (not first digit)
    00433             if (*m_read_ptr == ' ') {
    00434                 // ignore trailing spaces after version in request
    00435                 if (! m_is_request) {
    00436                     m_headers_parse_state = PARSE_STATUS_CODE_START;
    00437                 }
    00438             } else if (*m_read_ptr == '\r') {
    00439                 // should only happen for requests
    00440                 if (! m_is_request) {
    00441                     set_error(ec, ERROR_STATUS_EMPTY);
    00442                     return false;
    00443                 }
    00444                 m_headers_parse_state = PARSE_EXPECTING_NEWLINE;
    00445             } else if (*m_read_ptr == '\n') {
    00446                 // should only happen for requests
    00447                 if (! m_is_request) {
    00448                     set_error(ec, ERROR_STATUS_EMPTY);
    00449                     return false;
    00450                 }
    00451                 m_headers_parse_state = PARSE_EXPECTING_CR;
    00452             } else if (is_digit(*m_read_ptr)) {
    00453                 http_msg.set_version_minor( (http_msg.get_version_minor() * 10)
    00454                                           + (*m_read_ptr - '0') );
    00455             } else {
    00456                 set_error(ec, ERROR_VERSION_CHAR);
    00457                 return false;
    00458             }
    00459             break;
    00460
    00461         case PARSE_STATUS_CODE_START:
    00462             // parsing the first digit of the response status code
    00463             if (!is_digit(*m_read_ptr)) {
    00464                 set_error(ec, ERROR_STATUS_CHAR);
    00465                 return false;
    00466             }
    00467             m_status_code = (*m_read_ptr - '0');
    00468             m_headers_parse_state = PARSE_STATUS_CODE;
    00469             break;
    00470
    00471         case PARSE_STATUS_CODE:
    00472             // parsing the response status code (not first digit)
    00473             if (*m_read_ptr == ' ') {
    00474                 m_status_message.erase();
    00475                 m_headers_parse_state = PARSE_STATUS_MESSAGE;
    00476             } else if (is_digit(*m_read_ptr)) {
    00477                 m_status_code = ( (m_status_code * 10) + (*m_read_ptr - '0') );
    00478             } else if (*m_read_ptr == '\r') {
    00479                 // recover from status message not sent
    00480                 m_status_message.erase();
    00481                 m_headers_parse_state = PARSE_EXPECTING_NEWLINE;
    00482             } else if (*m_read_ptr == '\n') {
    00483                 // recover from status message not sent
    00484                 m_status_message.erase();
    00485                 m_headers_parse_state = PARSE_EXPECTING_CR;
    00486             } else {
    00487                 set_error(ec, ERROR_STATUS_CHAR);
    00488                 return false;
    00489             }
    00490             break;
    00491
    00492         case PARSE_STATUS_MESSAGE:
    00493             // parsing the response status message
    00494             if (*m_read_ptr == '\r') {
    00495                 m_headers_parse_state = PARSE_EXPECTING_NEWLINE;
    00496             } else if (*m_read_ptr == '\n') {
    00497                 m_headers_parse_state = PARSE_EXPECTING_CR;
    00498             } else if (is_control(*m_read_ptr)) {
    00499                 set_error(ec, ERROR_STATUS_CHAR);
    00500                 return false;
    00501             } else if (m_status_message.size() >= STATUS_MESSAGE_MAX) {
    00502                 set_error(ec, ERROR_STATUS_CHAR);
    00503                 return false;
    00504             } else {
    00505                 m_status_message.push_back(*m_read_ptr);
    00506             }
    00507             break;
    00508
    00509         case PARSE_EXPECTING_NEWLINE:
    00510             // we received a CR; expecting a newline to follow
    00511             if (*m_read_ptr == '\n') {
    00512                 m_headers_parse_state = PARSE_HEADER_START;
    00513             } else if (*m_read_ptr == '\r') {
    00514                 // we received two CR's in a row
    00515                 // assume CR only is (incorrectly) being used for line termination
    00516                 // therefore, the message is finished
    00517                 ++m_read_ptr;
    00518                 m_bytes_last_read = (m_read_ptr - read_start_ptr);
    00519                 m_bytes_total_read += m_bytes_last_read;
    00520                 return true;
    00521             } else if (*m_read_ptr == '\t' || *m_read_ptr == ' ') {
    00522                 m_headers_parse_state = PARSE_HEADER_WHITESPACE;
    00523             } else if (!is_char(*m_read_ptr) || is_control(*m_read_ptr) || is_special(*m_read_ptr)) {
    00524                 set_error(ec, ERROR_HEADER_CHAR);
    00525                 return false;
    00526             } else {
    00527                 // assume it is the first character for the name of a header
    00528                 m_header_name.erase();
    00529                 m_header_name.push_back(*m_read_ptr);
    00530                 m_headers_parse_state = PARSE_HEADER_NAME;
    00531             }
    00532             break;
    00533
    00534         case PARSE_EXPECTING_CR:
    00535             // we received a newline without a CR
    00536             if (*m_read_ptr == '\r') {
    00537                 m_headers_parse_state = PARSE_HEADER_START;
    00538             } else if (*m_read_ptr == '\n') {
    00539                 // we received two newlines in a row
    00540                 // assume newline only is (incorrectly) being used for line termination
    00541                 // therefore, the message is finished
    00542                 ++m_read_ptr;
    00543                 m_bytes_last_read = (m_read_ptr - read_start_ptr);
    00544                 m_bytes_total_read += m_bytes_last_read;
    00545                 return true;
    00546             } else if (*m_read_ptr == '\t' || *m_read_ptr == ' ') {
    00547                 m_headers_parse_state = PARSE_HEADER_WHITESPACE;
    00548             } else if (!is_char(*m_read_ptr) || is_control(*m_read_ptr) || is_special(*m_read_ptr)) {
    00549                 set_error(ec, ERROR_HEADER_CHAR);
    00550                 return false;
    00551             } else {
    00552                 // assume it is the first character for the name of a header
    00553                 m_header_name.erase();
    00554                 m_header_name.push_back(*m_read_ptr);
    00555                 m_headers_parse_state = PARSE_HEADER_NAME;
    00556             }
    00557             break;
    00558
    00559         case PARSE_HEADER_WHITESPACE:
    00560             // parsing whitespace before a header name
    00561             if (*m_read_ptr == '\r') {
    00562                 m_headers_parse_state = PARSE_EXPECTING_NEWLINE;
    00563             } else if (*m_read_ptr == '\n') {
    00564                 m_headers_parse_state = PARSE_EXPECTING_CR;
    00565             } else if (*m_read_ptr != '\t' && *m_read_ptr != ' ') {
    00566                 if (!is_char(*m_read_ptr) || is_control(*m_read_ptr) || is_special(*m_read_ptr))
    00567                     set_error(ec, ERROR_HEADER_CHAR);
    00568                     return false;
    00569                 // assume it is the first character for the name of a header
    00570                 m_header_name.erase();
    00571                 m_header_name.push_back(*m_read_ptr);
    00572                 m_headers_parse_state = PARSE_HEADER_NAME;
    00573             }
    00574             break;
    00575
    00576         case PARSE_HEADER_START:
    00577             // parsing the start of a new header
    00578             if (*m_read_ptr == '\r') {
    00579                 m_headers_parse_state = PARSE_EXPECTING_FINAL_NEWLINE;
    00580             } else if (*m_read_ptr == '\n') {
    00581                 m_headers_parse_state = PARSE_EXPECTING_FINAL_CR;
    00582             } else if (*m_read_ptr == '\t' || *m_read_ptr == ' ') {
    00583                 m_headers_parse_state = PARSE_HEADER_WHITESPACE;
    00584             } else if (!is_char(*m_read_ptr) || is_control(*m_read_ptr) || is_special(*m_read_ptr)) {
    00585                 set_error(ec, ERROR_HEADER_CHAR);
    00586                 return false;
    00587             } else {
    00588                 // first character for the name of a header
    00589                 m_header_name.erase();
    00590                 m_header_name.push_back(*m_read_ptr);
    00591                 m_headers_parse_state = PARSE_HEADER_NAME;
    00592             }
    00593             break;
    00594
    00595         case PARSE_HEADER_NAME:
    00596             // parsing the name of a header
    00597             if (*m_read_ptr == ':') {
    00598                 m_header_value.erase();
    00599                 m_headers_parse_state = PARSE_SPACE_BEFORE_HEADER_VALUE;
    00600             } else if (!is_char(*m_read_ptr) || is_control(*m_read_ptr) || is_special(*m_read_ptr)) {
    00601                 set_error(ec, ERROR_HEADER_CHAR);
    00602                 return false;
    00603             } else if (m_header_name.size() >= HEADER_NAME_MAX) {
    00604                 set_error(ec, ERROR_HEADER_NAME_SIZE);
    00605                 return false;
    00606             } else {
    00607                 // character (not first) for the name of a header
    00608                 m_header_name.push_back(*m_read_ptr);
    00609             }
    00610             break;
    00611
    00612         case PARSE_SPACE_BEFORE_HEADER_VALUE:
    00613             // parsing space character before a header's value
    00614             if (*m_read_ptr == ' ') {
    00615                 m_headers_parse_state = PARSE_HEADER_VALUE;
    00616             } else if (*m_read_ptr == '\r') {
    00617                 http_msg.add_header(m_header_name, m_header_value);
    00618                 m_headers_parse_state = PARSE_EXPECTING_NEWLINE;
    00619             } else if (*m_read_ptr == '\n') {
    00620                 http_msg.add_header(m_header_name, m_header_value);
    00621                 m_headers_parse_state = PARSE_EXPECTING_CR;
    00622             } else if (!is_char(*m_read_ptr) || is_control(*m_read_ptr) || is_special(*m_read_ptr)) {
    00623                 set_error(ec, ERROR_HEADER_CHAR);
    00624                 return false;
    00625             } else {
    00626                 // assume it is the first character for the value of a header
    00627                 m_header_value.push_back(*m_read_ptr);
    00628                 m_headers_parse_state = PARSE_HEADER_VALUE;
    00629             }
    00630             break;
    00631
    00632         case PARSE_HEADER_VALUE:
    00633             // parsing the value of a header
    00634             if (*m_read_ptr == '\r') {
    00635                 http_msg.add_header(m_header_name, m_header_value);
    00636                 m_headers_parse_state = PARSE_EXPECTING_NEWLINE;
    00637             } else if (*m_read_ptr == '\n') {
    00638                 http_msg.add_header(m_header_name, m_header_value);
    00639                 m_headers_parse_state = PARSE_EXPECTING_CR;
    00640             } else if (*m_read_ptr != '\t' && is_control(*m_read_ptr)) {
    00641                 // RFC 2616, 2.2 basic Rules.
    00642                 // TEXT = <any OCTET except CTLs, but including LWS>
    00643                 // LWS  = [CRLF] 1*( SP | HT )
    00644                 //
    00645                 // TODO: parsing of folding LWS in multiple lines headers
    00646                 //       doesn't work properly still
    00647                 set_error(ec, ERROR_HEADER_CHAR);
    00648                 return false;
    00649             } else if (m_header_value.size() >= HEADER_VALUE_MAX) {
    00650                 set_error(ec, ERROR_HEADER_VALUE_SIZE);
    00651                 return false;
    00652             } else {
    00653                 // character (not first) for the value of a header
    00654                 m_header_value.push_back(*m_read_ptr);
    00655             }
    00656             break;
    00657
    00658         case PARSE_EXPECTING_FINAL_NEWLINE:
    00659             if (*m_read_ptr == '\n') ++m_read_ptr;
    00660             m_bytes_last_read = (m_read_ptr - read_start_ptr);
    00661             m_bytes_total_read += m_bytes_last_read;
    00662             return true;
    00663
    00664         case PARSE_EXPECTING_FINAL_CR:
    00665             if (*m_read_ptr == '\r') ++m_read_ptr;
    00666             m_bytes_last_read = (m_read_ptr - read_start_ptr);
    00667             m_bytes_total_read += m_bytes_last_read;
    00668             return true;
    00669         }
    00670
    00671         ++m_read_ptr;
    00672     }
    00673
    00674     m_bytes_last_read = (m_read_ptr - read_start_ptr);
    00675     m_bytes_total_read += m_bytes_last_read;
    00676     return boost::indeterminate;
    00677 }
    00678
    00679 void parser::update_message_with_header_data(http::message& http_msg) const
    00680 {
    00681     if (is_parsing_request()) {
    00682
    00683         // finish an HTTP request message
    00684
    00685         http::request& http_request(dynamic_cast<http::request&>(http_msg));
    00686         http_request.set_method(m_method);
    00687         http_request.set_resource(m_resource);
    00688         http_request.set_query_string(m_query_string);
    00689
    00690         // parse query pairs from the URI query string
    00691         if (! m_query_string.empty()) {
    00692             if (! parse_url_encoded(http_request.get_queries(),
    00693                                   m_query_string.c_str(),
    00694                                   m_query_string.size()))
    00695                 PION_LOG_WARN(m_logger, "Request query string parsing failed (URI)");
    00696         }
    00697
    00698         // parse "Cookie" headers in request
    00699         std::pair<ihash_multimap::const_iterator, ihash_multimap::const_iterator>
    00700         cookie_pair = http_request.get_headers().equal_range(http::types::HEADER_COOKIE);
    00701         for (ihash_multimap::const_iterator cookie_iterator = cookie_pair.first;
    00702              cookie_iterator != http_request.get_headers().end()
    00703              && cookie_iterator != cookie_pair.second; ++cookie_iterator)
    00704         {
    00705             if (! parse_cookie_header(http_request.get_cookies(),
    00706                                     cookie_iterator->second, false) )
    00707                 PION_LOG_WARN(m_logger, "Cookie header parsing failed");
    00708         }
    00709
    00710     } else {
    00711
    00712         // finish an HTTP response message
    00713
    00714         http::response& http_response(dynamic_cast<http::response&>(http_msg));
    00715         http_response.set_status_code(m_status_code);
    00716         http_response.set_status_message(m_status_message);
    00717
    00718         // parse "Set-Cookie" headers in response
    00719         std::pair<ihash_multimap::const_iterator, ihash_multimap::const_iterator>
    00720         cookie_pair = http_response.get_headers().equal_range(http::types::HEADER_SET_COOKIE);
    00721         for (ihash_multimap::const_iterator cookie_iterator = cookie_pair.first;
    00722              cookie_iterator != http_response.get_headers().end()
    00723              && cookie_iterator != cookie_pair.second; ++cookie_iterator)
    00724         {
    00725             if (! parse_cookie_header(http_response.get_cookies(),
    00726                                     cookie_iterator->second, true) )
    00727                 PION_LOG_WARN(m_logger, "Set-Cookie header parsing failed");
    00728         }
    00729
    00730     }
    00731 }
    00732
    00733 boost::tribool parser::finish_header_parsing(http::message& http_msg,
    00734     boost::system::error_code& ec)
    00735 {
    00736     boost::tribool rc = boost::indeterminate;
    00737
    00738     m_bytes_content_remaining = m_bytes_content_read = 0;
    00739     http_msg.set_content_length(0);
    00740     http_msg.update_transfer_encoding_using_header();
    00741     update_message_with_header_data(http_msg);
    00742
    00743     if (http_msg.is_chunked()) {
    00744
    00745         // content is encoded using chunks
    00746         m_message_parse_state = PARSE_CHUNKS;
    00747
    00748         // return true if parsing headers only
    00749         if (m_parse_headers_only)
    00750             rc = true;
    00751
    00752     } else if (http_msg.is_content_length_implied()) {
    00753
    00754         // content length is implied to be zero
    00755         m_message_parse_state = PARSE_END;
    00756         rc = true;
    00757
    00758     } else {
    00759         // content length should be specified in the headers
    00760
    00761         if (http_msg.has_header(http::types::HEADER_CONTENT_LENGTH)) {
    00762
    00763             // message has a content-length header
    00764             try {
    00765                 http_msg.update_content_length_using_header();
    00766             } catch (...) {
    00767                 PION_LOG_ERROR(m_logger, "Unable to update content length");
    00768                 set_error(ec, ERROR_INVALID_CONTENT_LENGTH);
    00769                 return false;
    00770             }
    00771
    00772             // check if content-length header == 0
    00773             if (http_msg.get_content_length() == 0) {
    00774                 m_message_parse_state = PARSE_END;
    00775                 rc = true;
    00776             } else {
    00777                 m_message_parse_state = PARSE_CONTENT;
    00778                 m_bytes_content_remaining = http_msg.get_content_length();
    00779
    00780                 // check if content-length exceeds maximum allowed
    00781                 if (m_bytes_content_remaining > m_max_content_length)
    00782                     http_msg.set_content_length(m_max_content_length);
    00783
    00784                 if (m_parse_headers_only) {
    00785                     // return true if parsing headers only
    00786                     rc = true;
    00787                 } else {
    00788                     // allocate a buffer for payload content (may be zero-size)
    00789                     http_msg.create_content_buffer();
    00790                 }
    00791             }
    00792
    00793         } else {
    00794             // no content-length specified, and the content length cannot
    00795             // otherwise be determined
    00796
    00797             // only if not a request, read through the close of the connection
    00798             if (! m_is_request) {
    00799                 // clear the chunk buffers before we start
    00800                 http_msg.get_chunk_cache().clear();
    00801
    00802                 // continue reading content until there is no more data
    00803                 m_message_parse_state = PARSE_CONTENT_NO_LENGTH;
    00804
    00805                 // return true if parsing headers only
    00806                 if (m_parse_headers_only)
    00807                     rc = true;
    00808             } else {
    00809                 m_message_parse_state = PARSE_END;
    00810                 rc = true;
    00811             }
    00812         }
    00813     }
    00814
    00815     finished_parsing_headers(ec);
    00816
    00817     return rc;
    00818 }
    00819
    00820 bool parser::parse_uri(const std::string& uri, std::string& proto,
    00821                       std::string& host, boost::uint16_t& port,
    00822                       std::string& path, std::string& query)
    00823 {
    00824     size_t proto_end = uri.find("://");
    00825     size_t proto_len = 0;
    00826
    00827     if(proto_end != std::string::npos) {
    00828         proto = uri.substr(0, proto_end);
    00829         proto_len = proto_end + 3; // add ://
    00830     } else {
    00831         proto.clear();
    00832     }
    00833
    00834     // find a first slash charact
    00835     // that indicates the end of the <server>:<port> part
    00836     size_t server_port_end = uri.find('/', proto_len);
    00837     if(server_port_end == std::string::npos) {
    00838         return false;
    00839     }
    00840
    00841     // copy <server>:<port> into temp string
    00842     std::string t;
    00843     t = uri.substr(proto_len, server_port_end - proto_len);
    00844     size_t port_pos = t.find(':', 0);
    00845
    00846     // assign output host and port parameters
    00847
    00848     host = t.substr(0, port_pos); // if port_pos == npos, copy whole string
    00849     if(host.length() == 0) {
    00850         return false;
    00851     }
    00852
    00853     // parse the port, if it's not empty
    00854     if(port_pos != std::string::npos) {
    00855         try {
    00856             port = boost::lexical_cast<int>(t.substr(port_pos+1));
    00857         } catch (boost::bad_lexical_cast &) {
    00858             return false;
    00859         }
    00860     } else if (proto == "http" || proto == "HTTP") {
    00861         port = 80;
    00862     } else if (proto == "https" || proto == "HTTPS") {
    00863         port = 443;
    00864     } else {
    00865         port = 0;
    00866     }
    00867
    00868     // copy the rest of the URI into path part
    00869     path = uri.substr(server_port_end);
    00870
    00871     // split the path and the query string parts
    00872     size_t query_pos = path.find('?', 0);
    00873
    00874     if(query_pos != std::string::npos) {
    00875         query = path.substr(query_pos + 1, path.length() - query_pos - 1);
    00876         path = path.substr(0, query_pos);
    00877     } else {
    00878         query.clear();
    00879     }
    00880
    00881     return true;
    00882 }
    00883
    00884 bool parser::parse_url_encoded(ihash_multimap& dict,
    00885                                const char *ptr, const size_t len)
    00886 {
    00887     // sanity check
    00888     if (ptr == NULL || len == 0)
    00889         return true;
    00890
    00891     // used to track whether we are parsing the name or value
    00892     enum QueryParseState {
    00893         QUERY_PARSE_NAME, QUERY_PARSE_VALUE
    00894     } parse_state = QUERY_PARSE_NAME;
    00895
    00896     // misc other variables used for parsing
    00897     const char * const end = ptr + len;
    00898     std::string query_name;
    00899     std::string query_value;
    00900
    00901     // iterate through each encoded character
    00902     while (ptr < end) {
    00903         switch (parse_state) {
    00904
    00905         case QUERY_PARSE_NAME:
    00906             // parsing query name
    00907             if (*ptr == '=') {
    00908                 // end of name found (OK if empty)
    00909                 parse_state = QUERY_PARSE_VALUE;
    00910             } else if (*ptr == '&') {
    00911                 // if query name is empty, just skip it (i.e. "&&")
    00912                 if (! query_name.empty()) {
    00913                     // assume that "=" is missing -- it's OK if the value is empty
    00914                     dict.insert( std::make_pair(algorithm::url_decode(query_name), algorithm::url_decode(query_value)) );
    00915                     query_name.erase();
    00916                 }
    00917             } else if (*ptr == '\r' || *ptr == '\n' || *ptr == '\t') {
    00918                 // ignore linefeeds, carriage return and tabs (normally within POST content)
    00919             } else if (is_control(*ptr) || query_name.size() >= QUERY_NAME_MAX) {
    00920                 // control character detected, or max sized exceeded
    00921                 return false;
    00922             } else {
    00923                 // character is part of the name
    00924                 query_name.push_back(*ptr);
    00925             }
    00926             break;
    00927
    00928         case QUERY_PARSE_VALUE:
    00929             // parsing query value
    00930             if (*ptr == '&') {
    00931                 // end of value found (OK if empty)
    00932                 if (! query_name.empty()) {
    00933                     dict.insert( std::make_pair(algorithm::url_decode(query_name), algorithm::url_decode(query_value)) );
    00934                     query_name.erase();
    00935                 }
    00936                 query_value.erase();
    00937                 parse_state = QUERY_PARSE_NAME;
    00938             } else if (*ptr == ',') {
    00939                 // end of value found in multi-value list (OK if empty)
    00940                 if (! query_name.empty())
    00941                     dict.insert( std::make_pair(algorithm::url_decode(query_name), algorithm::url_decode(query_value)) );
    00942                 query_value.erase();
    00943             } else if (*ptr == '\r' || *ptr == '\n' || *ptr == '\t') {
    00944                 // ignore linefeeds, carriage return and tabs (normally within POST content)
    00945             } else if (is_control(*ptr) || query_value.size() >= QUERY_VALUE_MAX) {
    00946                 // control character detected, or max sized exceeded
    00947                 return false;
    00948             } else {
    00949                 // character is part of the value
    00950                 query_value.push_back(*ptr);
    00951             }
    00952             break;
    00953         }
    00954
    00955         ++ptr;
    00956     }
    00957
    00958     // handle last pair in string
    00959     if (! query_name.empty())
    00960         dict.insert( std::make_pair(algorithm::url_decode(query_name), algorithm::url_decode(query_value)) );
    00961
    00962     return true;
    00963 }
    00964
    00965 bool parser::parse_multipart_form_data(ihash_multimap& dict,
    00966                                        const std::string& content_type,
    00967                                        const char *ptr, const size_t len)
    00968 {
    00969     // sanity check
    00970     if (ptr == NULL || len == 0)
    00971         return true;
    00972
    00973     // parse field boundary
    00974     std::size_t pos = content_type.find("boundary=");
    00975     if (pos == std::string::npos)
    00976         return false;
    00977     const std::string boundary = std::string("--") + content_type.substr(pos+9);
    00978
    00979     // used to track what we are parsing
    00980     enum MultiPartParseState {
    00981         MP_PARSE_START,
    00982         MP_PARSE_HEADER_CR, MP_PARSE_HEADER_LF,
    00983         MP_PARSE_HEADER_NAME, MP_PARSE_HEADER_SPACE, MP_PARSE_HEADER_VALUE,
    00984         MP_PARSE_HEADER_LAST_LF, MP_PARSE_FIELD_DATA
    00985     } parse_state = MP_PARSE_START;
    00986
    00987     // a few variables used for parsing
    00988     std::string header_name;
    00989     std::string header_value;
    00990     std::string field_name;
    00991     std::string field_value;
    00992     bool found_parameter = false;
    00993     bool save_current_field = true;
    00994     const char * const end_ptr = ptr + len;
    00995
    00996     ptr = std::search(ptr, end_ptr, boundary.begin(), boundary.end());
    00997
    00998     while (ptr != NULL && ptr < end_ptr) {
    00999         switch (parse_state) {
    01000             case MP_PARSE_START:
    01001                 // start parsing a new field
    01002                 header_name.clear();
    01003                 header_value.clear();
    01004                 field_name.clear();
    01005                 field_value.clear();
    01006                 save_current_field = true;
    01007                 ptr += boundary.size() - 1;
    01008                 parse_state = MP_PARSE_HEADER_CR;
    01009                 break;
    01010             case MP_PARSE_HEADER_CR:
    01011                 // expecting CR while parsing headers
    01012                 if (*ptr == '\r') {
    01013                     // got it -> look for linefeed
    01014                     parse_state = MP_PARSE_HEADER_LF;
    01015                 } else if (*ptr == '\n') {
    01016                     // got a linefeed? try to ignore and start parsing header
    01017                     parse_state = MP_PARSE_HEADER_NAME;
    01018                 } else if (*ptr == '-' && ptr+1 < end_ptr && ptr[1] == '-') {
    01019                     // end of multipart content
    01020                     return true;
    01021                 } else return false;
    01022                 break;
    01023             case MP_PARSE_HEADER_LF:
    01024                 // expecting LF while parsing headers
    01025                 if (*ptr == '\n') {
    01026                     // got it -> start parsing header name
    01027                     parse_state = MP_PARSE_HEADER_NAME;
    01028                 } else return false;
    01029                 break;
    01030             case MP_PARSE_HEADER_NAME:
    01031                 // parsing the name of a header
    01032                 if (*ptr == '\r' || *ptr == '\n') {
    01033                     if (header_name.empty()) {
    01034                         // got CR or LF at beginning; skip to data
    01035                         parse_state = (*ptr == '\r' ? MP_PARSE_HEADER_LAST_LF : MP_PARSE_FIELD_DATA);
    01036                     } else {
    01037                         // premature CR or LF -> just ignore and start parsing next header
    01038                         parse_state = (*ptr == '\r' ? MP_PARSE_HEADER_LF : MP_PARSE_HEADER_NAME);
    01039                     }
    01040                 } else if (*ptr == ':') {
    01041                     // done parsing header name -> consume space next
    01042                     parse_state = MP_PARSE_HEADER_SPACE;
    01043                 } else {
    01044                     // one more byte for header name
    01045                     header_name += *ptr;
    01046                 }
    01047                 break;
    01048             case MP_PARSE_HEADER_SPACE:
    01049                 // expecting a space before header value
    01050                 if (*ptr == '\r') {
    01051                     // premature CR -> just ignore and start parsing next header
    01052                     parse_state = MP_PARSE_HEADER_LF;
    01053                 } else if (*ptr == '\n') {
    01054                     // premature LF -> just ignore and start parsing next header
    01055                     parse_state = MP_PARSE_HEADER_NAME;
    01056                 } else if (*ptr != ' ') {
    01057                     // not a space -> assume it's a value char
    01058                     header_value += *ptr;
    01059                     parse_state = MP_PARSE_HEADER_VALUE;
    01060                 }
    01061                 // otherwise just ignore the space(s)
    01062                 break;
    01063             case MP_PARSE_HEADER_VALUE:
    01064                 // parsing the value of a header
    01065                 if (*ptr == '\r' || *ptr == '\n') {
    01066                     // reached the end of the value -> check if it's important
    01067                     if (boost::algorithm::iequals(header_name, types::HEADER_CONTENT_TYPE)) {
    01068                         // only keep fields that have a text type or no type
    01069                         save_current_field = boost::algorithm::iequals(header_value.substr(0, 5), "text/");
    01070                     } else if (boost::algorithm::iequals(header_name, types::HEADER_CONTENT_DISPOSITION)) {
    01071                         // get current field from content-disposition header
    01072                         std::size_t name_pos = header_value.find("name=\"");
    01073                         if (name_pos != std::string::npos) {
    01074                             for (name_pos += 6; name_pos < header_value.size() && header_value[name_pos] != '\"'; ++name_pos) {
    01075                                 field_name += header_value[name_pos];
    01076                             }
    01077                         }
    01078                     }
    01079                     // clear values and start parsing next header
    01080                     header_name.clear();
    01081                     header_value.clear();
    01082                     parse_state = (*ptr == '\r' ? MP_PARSE_HEADER_LF : MP_PARSE_HEADER_NAME);
    01083                 } else {
    01084                     // one more byte for header value
    01085                     header_value += *ptr;
    01086                 }
    01087                 break;
    01088             case MP_PARSE_HEADER_LAST_LF:
    01089                 // expecting final linefeed to terminate headers and begin field data
    01090                 if (*ptr == '\n') {
    01091                     // got it
    01092                     if (save_current_field && !field_name.empty()) {
    01093                         // parse the field if we care & know enough about it
    01094                         parse_state = MP_PARSE_FIELD_DATA;
    01095                     } else {
    01096                         // otherwise skip ahead to next field
    01097                         parse_state = MP_PARSE_START;
    01098                         ptr = std::search(ptr, end_ptr, boundary.begin(), boundary.end());
    01099                     }
    01100                 } else return false;
    01101                 break;
    01102             case MP_PARSE_FIELD_DATA:
    01103                 // parsing the value of a field -> find the end of it
    01104                 const char *field_end_ptr = end_ptr;
    01105                 const char *next_ptr = std::search(ptr, end_ptr, boundary.begin(), boundary.end());
    01106                 if (next_ptr) {
    01107                     // don't include CRLF before next boundary
    01108                     const char *temp_ptr = next_ptr - 2;
    01109                     if (temp_ptr[0] == '\r' && temp_ptr[1] == '\n')
    01110                         field_end_ptr = temp_ptr;
    01111                     else field_end_ptr = next_ptr;
    01112                 }
    01113                 field_value.assign(ptr, field_end_ptr - ptr);
    01114                 // add the field to the query dictionary
    01115                 dict.insert( std::make_pair(field_name, field_value) );
    01116                 found_parameter = true;
    01117                 // skip ahead to next field
    01118                 parse_state = MP_PARSE_START;
    01119                 ptr = next_ptr;
    01120                 break;
    01121         }
    01122         // we've already bumped position if MP_PARSE_START
    01123         if (parse_state != MP_PARSE_START)
    01124             ++ptr;
    01125     }
    01126
    01127     return found_parameter;
    01128 }
    01129
    01130 bool parser::parse_cookie_header(ihash_multimap& dict,
    01131                                    const char *ptr, const size_t len,
    01132                                    bool set_cookie_header)
    01133 {
    01134     // BASED ON RFC 2109
    01135     // http://www.ietf.org/rfc/rfc2109.txt
    01136     //
    01137     // The current implementation ignores cookie attributes which begin with '$'
    01138     // (i.e. $Path=/, $Domain=, etc.)
    01139
    01140     // used to track what we are parsing
    01141     enum CookieParseState {
    01142         COOKIE_PARSE_NAME, COOKIE_PARSE_VALUE, COOKIE_PARSE_IGNORE
    01143     } parse_state = COOKIE_PARSE_NAME;
    01144
    01145     // misc other variables used for parsing
    01146     const char * const end = ptr + len;
    01147     std::string cookie_name;
    01148     std::string cookie_value;
    01149     char value_quote_character = '\0';
    01150
    01151     // iterate through each character
    01152     while (ptr < end) {
    01153         switch (parse_state) {
    01154
    01155         case COOKIE_PARSE_NAME:
    01156             // parsing cookie name
    01157             if (*ptr == '=') {
    01158                 // end of name found (OK if empty)
    01159                 value_quote_character = '\0';
    01160                 parse_state = COOKIE_PARSE_VALUE;
    01161             } else if (*ptr == ';' || *ptr == ',') {
    01162                 // ignore empty cookie names since this may occur naturally
    01163                 // when quoted values are encountered
    01164                 if (! cookie_name.empty()) {
    01165                     // value is empty (OK)
    01166                     if (! is_cookie_attribute(cookie_name, set_cookie_header))
    01167                         dict.insert( std::make_pair(cookie_name, cookie_value) );
    01168                     cookie_name.erase();
    01169                 }
    01170             } else if (*ptr != ' ') {   // ignore whitespace
    01171                 // check if control character detected, or max sized exceeded
    01172                 if (is_control(*ptr) || cookie_name.size() >= COOKIE_NAME_MAX)
    01173                     return false;
    01174                 // character is part of the name
    01175                 cookie_name.push_back(*ptr);
    01176             }
    01177             break;
    01178
    01179         case COOKIE_PARSE_VALUE:
    01180             // parsing cookie value
    01181             if (value_quote_character == '\0') {
    01182                 // value is not (yet) quoted
    01183                 if (*ptr == ';' || *ptr == ',') {
    01184                     // end of value found (OK if empty)
    01185                     if (! is_cookie_attribute(cookie_name, set_cookie_header))
    01186                         dict.insert( std::make_pair(cookie_name, cookie_value) );
    01187                     cookie_name.erase();
    01188                     cookie_value.erase();
    01189                     parse_state = COOKIE_PARSE_NAME;
    01190                 } else if (*ptr == '\'' || *ptr == '"') {
    01191                     if (cookie_value.empty()) {
    01192                         // begin quoted value
    01193                         value_quote_character = *ptr;
    01194                     } else if (cookie_value.size() >= COOKIE_VALUE_MAX) {
    01195                         // max size exceeded
    01196                         return false;
    01197                     } else {
    01198                         // assume character is part of the (unquoted) value
    01199                         cookie_value.push_back(*ptr);
    01200                     }
    01201                 } else if (*ptr != ' ' || !cookie_value.empty()) {  // ignore leading unquoted whitespace
    01202                     // check if control character detected, or max sized exceeded
    01203                     if (is_control(*ptr) || cookie_value.size() >= COOKIE_VALUE_MAX)
    01204                         return false;
    01205                     // character is part of the (unquoted) value
    01206                     cookie_value.push_back(*ptr);
    01207                 }
    01208             } else {
    01209                 // value is quoted
    01210                 if (*ptr == value_quote_character) {
    01211                     // end of value found (OK if empty)
    01212                     if (! is_cookie_attribute(cookie_name, set_cookie_header))
    01213                         dict.insert( std::make_pair(cookie_name, cookie_value) );
    01214                     cookie_name.erase();
    01215                     cookie_value.erase();
    01216                     parse_state = COOKIE_PARSE_IGNORE;
    01217                 } else if (cookie_value.size() >= COOKIE_VALUE_MAX) {
    01218                     // max size exceeded
    01219                     return false;
    01220                 } else {
    01221                     // character is part of the (quoted) value
    01222                     cookie_value.push_back(*ptr);
    01223                 }
    01224             }
    01225             break;
    01226
    01227         case COOKIE_PARSE_IGNORE:
    01228             // ignore everything until we reach a comma "," or semicolon ";"
    01229             if (*ptr == ';' || *ptr == ',')
    01230                 parse_state = COOKIE_PARSE_NAME;
    01231             break;
    01232         }
    01233
    01234         ++ptr;
    01235     }
    01236
    01237     // handle last cookie in string
    01238     if (! is_cookie_attribute(cookie_name, set_cookie_header))
    01239         dict.insert( std::make_pair(cookie_name, cookie_value) );
    01240
    01241     return true;
    01242 }
    01243
    01244 boost::tribool parser::parse_chunks(http::message::chunk_cache_t& chunks,
    01245     boost::system::error_code& ec)
    01246 {
    01247     //
    01248     // note that boost::tribool may have one of THREE states:
    01249     //
    01250     // false: encountered an error while parsing message
    01251     // true: finished successfully parsing the message
    01252     // indeterminate: parsed bytes, but the message is not yet finished
    01253     //
    01254     const char *read_start_ptr = m_read_ptr;
    01255     m_bytes_last_read = 0;
    01256     while (m_read_ptr < m_read_end_ptr) {
    01257
    01258         switch (m_chunked_content_parse_state) {
    01259         case PARSE_CHUNK_SIZE_START:
    01260             // we have not yet started parsing the next chunk size
    01261             if (is_hex_digit(*m_read_ptr)) {
    01262                 m_chunk_size_str.erase();
    01263                 m_chunk_size_str.push_back(*m_read_ptr);
    01264                 m_chunked_content_parse_state = PARSE_CHUNK_SIZE;
    01265             } else if (*m_read_ptr == ' ' || *m_read_ptr == '\x09' || *m_read_ptr == '\x0D' || *m_read_ptr == '\x0A') {
    01266                 // Ignore leading whitespace.  Technically, the standard probably doesn't allow white space here,
    01267                 // but we'll be flexible, since there's no ambiguity.
    01268                 break;
    01269             } else {
    01270                 set_error(ec, ERROR_CHUNK_CHAR);
    01271                 return false;
    01272             }
    01273             break;
    01274
    01275         case PARSE_CHUNK_SIZE:
    01276             if (is_hex_digit(*m_read_ptr)) {
    01277                 m_chunk_size_str.push_back(*m_read_ptr);
    01278             } else if (*m_read_ptr == '\x0D') {
    01279                 m_chunked_content_parse_state = PARSE_EXPECTING_LF_AFTER_CHUNK_SIZE;
    01280             } else if (*m_read_ptr == ' ' || *m_read_ptr == '\x09') {
    01281                 // Ignore trailing tabs or spaces.  Technically, the standard probably doesn't allow this,
    01282                 // but we'll be flexible, since there's no ambiguity.
    01283                 m_chunked_content_parse_state = PARSE_EXPECTING_CR_AFTER_CHUNK_SIZE;
    01284             } else if (*m_read_ptr == ';') {
    01285                 // Following the semicolon we have text which will be ignored till we encounter
    01286                 //  a CRLF
    01287                 m_chunked_content_parse_state = PARSE_EXPECTING_IGNORED_TEXT_AFTER_CHUNK_SIZE;
    01288             } else {
    01289                 set_error(ec, ERROR_CHUNK_CHAR);
    01290                 return false;
    01291             }
    01292             break;
    01293
    01294         case PARSE_EXPECTING_IGNORED_TEXT_AFTER_CHUNK_SIZE:
    01295             if (*m_read_ptr == '\x0D') {
    01296                 m_chunked_content_parse_state = PARSE_EXPECTING_LF_AFTER_CHUNK_SIZE;
    01297             }
    01298             break;
    01299
    01300         case PARSE_EXPECTING_CR_AFTER_CHUNK_SIZE:
    01301             if (*m_read_ptr == '\x0D') {
    01302                 m_chunked_content_parse_state = PARSE_EXPECTING_LF_AFTER_CHUNK_SIZE;
    01303             } else if (*m_read_ptr == ' ' || *m_read_ptr == '\x09') {
    01304                 // Ignore trailing tabs or spaces.  Technically, the standard probably doesn't allow this,
    01305                 // but we'll be flexible, since there's no ambiguity.
    01306                 break;
    01307             } else {
    01308                 set_error(ec, ERROR_CHUNK_CHAR);
    01309                 return false;
    01310             }
    01311             break;
    01312
    01313         case PARSE_EXPECTING_LF_AFTER_CHUNK_SIZE:
    01314             // We received a CR; expecting LF to follow.  We can't be flexible here because
    01315             // if we see anything other than LF, we can't be certain where the chunk starts.
    01316             if (*m_read_ptr == '\x0A') {
    01317                 m_bytes_read_in_current_chunk = 0;
    01318                 m_size_of_current_chunk = strtol(m_chunk_size_str.c_str(), 0, 16);
    01319                 if (m_size_of_current_chunk == 0) {
    01320                     m_chunked_content_parse_state = PARSE_EXPECTING_FINAL_CR_OR_FOOTERS_AFTER_LAST_CHUNK;
    01321                 } else {
    01322                     m_chunked_content_parse_state = PARSE_CHUNK;
    01323                 }
    01324             } else {
    01325                 set_error(ec, ERROR_CHUNK_CHAR);
    01326                 return false;
    01327             }
    01328             break;
    01329
    01330         case PARSE_CHUNK:
    01331             if (m_bytes_read_in_current_chunk < m_size_of_current_chunk) {
    01332                 if (m_payload_handler) {
    01333                     const std::size_t bytes_avail = bytes_available();
    01334                     const std::size_t bytes_in_chunk = m_size_of_current_chunk - m_bytes_read_in_current_chunk;
    01335                     const std::size_t len = (bytes_in_chunk > bytes_avail) ? bytes_avail : bytes_in_chunk;
    01336                     m_payload_handler(m_read_ptr, len);
    01337                     m_bytes_read_in_current_chunk += len;
    01338                     if (len > 1) m_read_ptr += (len - 1);
    01339                 } else if (chunks.size() < m_max_content_length) {
    01340                     chunks.push_back(*m_read_ptr);
    01341                     m_bytes_read_in_current_chunk++;
    01342                 }
    01343             }
    01344             if (m_bytes_read_in_current_chunk == m_size_of_current_chunk) {
    01345                 m_chunked_content_parse_state = PARSE_EXPECTING_CR_AFTER_CHUNK;
    01346             }
    01347             break;
    01348
    01349         case PARSE_EXPECTING_CR_AFTER_CHUNK:
    01350             // we've read exactly m_size_of_current_chunk bytes since starting the current chunk
    01351             if (*m_read_ptr == '\x0D') {
    01352                 m_chunked_content_parse_state = PARSE_EXPECTING_LF_AFTER_CHUNK;
    01353             } else {
    01354                 set_error(ec, ERROR_CHUNK_CHAR);
    01355                 return false;
    01356             }
    01357             break;
    01358
    01359         case PARSE_EXPECTING_LF_AFTER_CHUNK:
    01360             // we received a CR; expecting LF to follow
    01361             if (*m_read_ptr == '\x0A') {
    01362                 m_chunked_content_parse_state = PARSE_CHUNK_SIZE_START;
    01363             } else {
    01364                 set_error(ec, ERROR_CHUNK_CHAR);
    01365                 return false;
    01366             }
    01367             break;
    01368
    01369         case PARSE_EXPECTING_FINAL_CR_OR_FOOTERS_AFTER_LAST_CHUNK:
    01370             // we've read the final chunk; expecting final CRLF
    01371             if (*m_read_ptr == '\x0D') {
    01372                 m_chunked_content_parse_state = PARSE_EXPECTING_FINAL_LF_AFTER_LAST_CHUNK;
    01373             } else {
    01374                 // Packet contains footers; Chunk parsing is commplete
    01375                 // Footer data contains name value pairs to be added to HTTP Message
    01376                 m_message_parse_state = PARSE_FOOTERS;
    01377                 m_headers_parse_state = PARSE_HEADER_START;
    01378                 m_bytes_last_read = (m_read_ptr - read_start_ptr);
    01379                 m_bytes_total_read += m_bytes_last_read;
    01380                 m_bytes_content_read += m_bytes_last_read;
    01381                 PION_LOG_DEBUG(m_logger, "Parsed " << m_bytes_last_read << " chunked payload content bytes; chunked content complete.");
    01382                 return true;
    01383             }
    01384             break;
    01385
    01386         case PARSE_EXPECTING_FINAL_LF_AFTER_LAST_CHUNK:
    01387             // we received the final CR; expecting LF to follow
    01388             if (*m_read_ptr == '\x0A') {
    01389                 ++m_read_ptr;
    01390                 m_bytes_last_read = (m_read_ptr - read_start_ptr);
    01391                 m_bytes_total_read += m_bytes_last_read;
    01392                 m_bytes_content_read += m_bytes_last_read;
    01393                 PION_LOG_DEBUG(m_logger, "Parsed " << m_bytes_last_read << " chunked payload content bytes; chunked content complete.");
    01394                 return true;
    01395             } else {
    01396                 set_error(ec, ERROR_CHUNK_CHAR);
    01397                 return false;
    01398             }
    01399         }
    01400
    01401         ++m_read_ptr;
    01402     }
    01403
    01404     m_bytes_last_read = (m_read_ptr - read_start_ptr);
    01405     m_bytes_total_read += m_bytes_last_read;
    01406     m_bytes_content_read += m_bytes_last_read;
    01407     return boost::indeterminate;
    01408 }
    01409
    01410 boost::tribool parser::consume_content(http::message& http_msg,
    01411     boost::system::error_code& ec)
    01412 {
    01413     size_t content_bytes_to_read;
    01414     size_t content_bytes_available = bytes_available();
    01415     boost::tribool rc = boost::indeterminate;
    01416
    01417     if (m_bytes_content_remaining == 0) {
    01418         // we have all of the remaining payload content
    01419         return true;
    01420     } else {
    01421         if (content_bytes_available >= m_bytes_content_remaining) {
    01422             // we have all of the remaining payload content
    01423             rc = true;
    01424             content_bytes_to_read = m_bytes_content_remaining;
    01425         } else {
    01426             // only some of the payload content is available
    01427             content_bytes_to_read = content_bytes_available;
    01428         }
    01429         m_bytes_content_remaining -= content_bytes_to_read;
    01430     }
    01431
    01432     // make sure content buffer is not already full
    01433     if (m_payload_handler) {
    01434         m_payload_handler(m_read_ptr, content_bytes_to_read);
    01435     } else if (m_bytes_content_read < m_max_content_length) {
    01436         if (m_bytes_content_read + content_bytes_to_read > m_max_content_length) {
    01437             // read would exceed maximum size for content buffer
    01438             // copy only enough bytes to fill up the content buffer
    01439             memcpy(http_msg.get_content() + m_bytes_content_read, m_read_ptr,
    01440                 m_max_content_length - m_bytes_content_read);
    01441         } else {
    01442             // copy all bytes available
    01443             memcpy(http_msg.get_content() + m_bytes_content_read, m_read_ptr, content_bytes_to_read);
    01444         }
    01445     }
    01446
    01447     m_read_ptr += content_bytes_to_read;
    01448     m_bytes_content_read += content_bytes_to_read;
    01449     m_bytes_total_read += content_bytes_to_read;
    01450     m_bytes_last_read = content_bytes_to_read;
    01451
    01452     return rc;
    01453 }
    01454
    01455 std::size_t parser::consume_content_as_next_chunk(http::message::chunk_cache_t& chunks)
    01456 {
    01457     if (bytes_available() == 0) {
    01458         m_bytes_last_read = 0;
    01459     } else {
    01460         // note: m_bytes_last_read must be > 0 because of bytes_available() check
    01461         m_bytes_last_read = (m_read_end_ptr - m_read_ptr);
    01462         if (m_payload_handler) {
    01463             m_payload_handler(m_read_ptr, m_bytes_last_read);
    01464             m_read_ptr += m_bytes_last_read;
    01465         } else {
    01466             while (m_read_ptr < m_read_end_ptr) {
    01467                 if (chunks.size() < m_max_content_length)
    01468                     chunks.push_back(*m_read_ptr);
    01469                 ++m_read_ptr;
    01470             }
    01471         }
    01472         m_bytes_total_read += m_bytes_last_read;
    01473         m_bytes_content_read += m_bytes_last_read;
    01474     }
    01475     return m_bytes_last_read;
    01476 }
    01477
    01478 void parser::finish(http::message& http_msg) const
    01479 {
    01480     switch (m_message_parse_state) {
    01481     case PARSE_START:
    01482         http_msg.set_is_valid(false);
    01483         http_msg.set_content_length(0);
    01484         http_msg.create_content_buffer();
    01485         return;
    01486     case PARSE_END:
    01487         http_msg.set_is_valid(true);
    01488         break;
    01489     case PARSE_HEADERS:
    01490     case PARSE_FOOTERS:
    01491         http_msg.set_is_valid(false);
    01492         update_message_with_header_data(http_msg);
    01493         http_msg.set_content_length(0);
    01494         http_msg.create_content_buffer();
    01495         break;
    01496     case PARSE_CONTENT:
    01497         http_msg.set_is_valid(false);
    01498         if (get_content_bytes_read() < m_max_content_length)   // NOTE: we can read more than we have allocated/stored
    01499             http_msg.set_content_length(get_content_bytes_read());
    01500         break;
    01501     case PARSE_CHUNKS:
    01502         http_msg.set_is_valid(m_chunked_content_parse_state==PARSE_CHUNK_SIZE_START);
    01503         if (!m_payload_handler)
    01504             http_msg.concatenate_chunks();
    01505         break;
    01506     case PARSE_CONTENT_NO_LENGTH:
    01507         http_msg.set_is_valid(true);
    01508         if (!m_payload_handler)
    01509             http_msg.concatenate_chunks();
    01510         break;
    01511     }
    01512
    01513     compute_msg_status(http_msg, http_msg.is_valid());
    01514
    01515     if (is_parsing_request() && !m_payload_handler && !m_parse_headers_only) {
    01516         // Parse query pairs from post content if content type is x-www-form-urlencoded.
    01517         // Type could be followed by parameters (as defined in section 3.6 of RFC 2616)
    01518         // e.g. Content-Type: application/x-www-form-urlencoded; charset=UTF-8
    01519         http::request& http_request(dynamic_cast<http::request&>(http_msg));
    01520         const std::string& content_type_header = http_request.get_header(http::types::HEADER_CONTENT_TYPE);
    01521         if (content_type_header.compare(0, http::types::CONTENT_TYPE_URLENCODED.length(),
    01522                                         http::types::CONTENT_TYPE_URLENCODED) == 0)
    01523         {
    01524             if (! parse_url_encoded(http_request.get_queries(),
    01525                                   http_request.get_content(),
    01526                                   http_request.get_content_length()))
    01527                 PION_LOG_WARN(m_logger, "Request form data parsing failed (POST urlencoded)");
    01528         } else if (content_type_header.compare(0, http::types::CONTENT_TYPE_MULTIPART_FORM_DATA.length(),
    01529                                                http::types::CONTENT_TYPE_MULTIPART_FORM_DATA) == 0)
    01530         {
    01531             if (! parse_multipart_form_data(http_request.get_queries(),
    01532                                             content_type_header,
    01533                                             http_request.get_content(),
    01534                                             http_request.get_content_length()))
    01535                 PION_LOG_WARN(m_logger, "Request form data parsing failed (POST multipart)");
    01536         }
    01537     }
    01538 }
    01539
    01540 void parser::compute_msg_status(http::message& http_msg, bool msg_parsed_ok )
    01541 {
    01542     http::message::data_status_t st = http::message::STATUS_NONE;
    01543
    01544     if(http_msg.has_missing_packets()) {
    01545         st = http_msg.has_data_after_missing_packets() ?
    01546                         http::message::STATUS_PARTIAL : http::message::STATUS_TRUNCATED;
    01547     } else {
    01548         st = msg_parsed_ok ? http::message::STATUS_OK : http::message::STATUS_TRUNCATED;
    01549     }
    01550
    01551     http_msg.set_status(st);
    01552 }
    01553
    01554 void parser::create_error_category(void)
    01555 {
    01556     static error_category_t UNIQUE_ERROR_CATEGORY;
    01557     m_error_category_ptr = &UNIQUE_ERROR_CATEGORY;
    01558 }
    01559
    01560 bool parser::parse_forwarded_for(const std::string& header, std::string& public_ip)
    01561 {
    01562     // static regex's used to check for ipv4 address
    01563     static const boost::regex IPV4_ADDR_RX("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}");
    01564
    01570     static const boost::regex PRIVATE_NET_RX("(10\\.[0-9]{1,3}|127\\.[0-9]{1,3}|192\\.168|172\\.1[6-9]|172\\.2[0-9]|172\\.3[0-1])\\.[0-9]{1,3}\\.[0-9]{1,3}");
    01571
    01572     // sanity check
    01573     if (header.empty())
    01574         return false;
    01575
    01576     // local variables re-used by while loop
    01577     boost::match_results<std::string::const_iterator> m;
    01578     std::string::const_iterator start_it = header.begin();
    01579
    01580     // search for next ip address within the header
    01581     while (boost::regex_search(start_it, header.end(), m, IPV4_ADDR_RX)) {
    01582         // get ip that matched
    01583         std::string ip_str(m[0].first, m[0].second);
    01584         // check if public network ip address
    01585         if (! boost::regex_match(ip_str, PRIVATE_NET_RX) ) {
    01586             // match found!
    01587             public_ip = ip_str;
    01588             return true;
    01589         }
    01590         // update search starting position
    01591         start_it = m[0].second;
    01592     }
    01593
    01594     // no matches found
    01595     return false;
    01596 }
    01597
    01598 }   // end namespace http
    01599 }   // end namespace pion