Menu

[r78]: / trunk / boost / network / protocol / http / impl / parser.ipp  Maximize  Restore  History

Download this file

783 lines (705 with data), 35.9 kB

// This file is part of the Boost Network library
// Based on the Pion Network Library (r421)
// Copyright Atomic Labs, Inc. 2007-2008
// See http://cpp-netlib.sourceforge.net for library home page.
//
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_NETWORK_PROTOCOL_HTTP_PARSER_IPP
#define BOOST_NETWORK_PROTOCOL_HTTP_PARSER_IPP

#include <boost/network/protocol/http/parser.hpp>

namespace boost { namespace network { namespace http {
    
    // member functions for class basic_parser<Tag,Traits>

    template <typename Tag, typename ParserTraits>
    boost::tribool basic_parser<Tag,Traits>::parse_http_headers(basic_message<Tag>& http_msg)
    {
        //
        // note that boost::tribool may have one of THREE states:
        //
        // false: encountered an error while parsing HTTP headers
        // true: finished successfully parsing the HTTP headers
        // indeterminate: parsed bytes, but the HTTP headers are not yet finished
        //
        const char *read_start_ptr = m_read_ptr;
        m_bytes_last_read = 0;
        while (m_read_ptr < m_read_end_ptr) {
    
            switch (m_headers_parse_state) {
            case PARSE_METHOD_START:
                // we have not yet started parsing the HTTP method string
                if (*m_read_ptr != ' ' && *m_read_ptr!='\r' && *m_read_ptr!='\n') {	// ignore leading whitespace
                    if (!is_char(*m_read_ptr) || is_control(*m_read_ptr) || is_special(*m_read_ptr))
                        return false;
                    m_headers_parse_state = PARSE_METHOD;
                    m_method.erase();
                    m_method.push_back(*m_read_ptr);
                }
                break;
    
            case PARSE_METHOD:
                // we have started parsing the HTTP method string
                if (*m_read_ptr == ' ') {
                    m_resource.erase();
                    m_headers_parse_state = PARSE_URI_STEM;
                } else if (!is_char(*m_read_ptr) || is_control(*m_read_ptr) || is_special(*m_read_ptr)) {
                    return false;
                } else if (m_method.size() >= ParserTraits::METHOD_MAX) {
                    return false;
                } else {
                    m_method.push_back(*m_read_ptr);
                }
                break;
    
            case PARSE_URI_STEM:
                // we have started parsing the URI stem (or resource name)
                if (*m_read_ptr == ' ') {
                    m_headers_parse_state = PARSE_HTTP_VERSION_H;
                } else if (*m_read_ptr == '?') {
                    m_query_string.erase();
                    m_headers_parse_state = PARSE_URI_QUERY;
                } else if (is_control(*m_read_ptr)) {
                    return false;
                } else if (m_resource.size() >= ParserTraits::RESOURCE_MAX) {
                    return false;
                } else {
                    m_resource.push_back(*m_read_ptr);
                }
                break;
    
            case PARSE_URI_QUERY:
                // we have started parsing the URI query string
                if (*m_read_ptr == ' ') {
                    m_headers_parse_state = PARSE_HTTP_VERSION_H;
                } else if (is_control(*m_read_ptr)) {
                    return false;
                } else if (m_query_string.size() >= ParserTraits::QUERY_STRING_MAX) {
                    return false;
                } else {
                    m_query_string.push_back(*m_read_ptr);
                }
                break;
                
            case PARSE_HTTP_VERSION_H:
                // parsing "HTTP"
                if (*m_read_ptr != 'H') return false;
                m_headers_parse_state = PARSE_HTTP_VERSION_T_1;
                break;
    
            case PARSE_HTTP_VERSION_T_1:
                // parsing "HTTP"
                if (*m_read_ptr != 'T') return false;
                m_headers_parse_state = PARSE_HTTP_VERSION_T_2;
                break;
    
            case PARSE_HTTP_VERSION_T_2:
                // parsing "HTTP"
                if (*m_read_ptr != 'T') return false;
                m_headers_parse_state = PARSE_HTTP_VERSION_P;
                break;
    
            case PARSE_HTTP_VERSION_P:
                // parsing "HTTP"
                if (*m_read_ptr != 'P') return false;
                m_headers_parse_state = PARSE_HTTP_VERSION_SLASH;
                break;
    
            case PARSE_HTTP_VERSION_SLASH:
                // parsing slash after "HTTP"
                if (*m_read_ptr != '/') return false;
                m_headers_parse_state = PARSE_HTTP_VERSION_MAJOR_START;
                break;
    
            case PARSE_HTTP_VERSION_MAJOR_START:
                // parsing the first digit of the major version number
                if (!is_digit(*m_read_ptr)) return false;
                http_msg.setVersionMajor(*m_read_ptr - '0');
                m_headers_parse_state = PARSE_HTTP_VERSION_MAJOR;
                break;
    
            case PARSE_HTTP_VERSION_MAJOR:
                // parsing the major version number (not first digit)
                if (*m_read_ptr == '.') {
                    m_headers_parse_state = PARSE_HTTP_VERSION_MINOR_START;
                } else if (is_digit(*m_read_ptr)) {
                    http_msg.setVersionMajor( (http_msg.getVersionMajor() * 10)
                                              + (*m_read_ptr - '0') );
                } else {
                    return false;
                }
                break;
    
            case PARSE_HTTP_VERSION_MINOR_START:
                // parsing the first digit of the minor version number
                if (!is_digit(*m_read_ptr)) return false;
                http_msg.setVersionMinor(*m_read_ptr - '0');
                m_headers_parse_state = PARSE_HTTP_VERSION_MINOR;
                break;
    
            case PARSE_HTTP_VERSION_MINOR:
                // parsing the major version number (not first digit)
                if (*m_read_ptr == ' ') {
                    // should only happen for responses
                    if (m_is_request) return false;
                    m_headers_parse_state = PARSE_STATUS_CODE_START;
                } else if (*m_read_ptr == '\r') {
                    // should only happen for requests
                    if (! m_is_request) return false;
                    m_headers_parse_state = PARSE_EXPECTING_NEWLINE;
                } else if (*m_read_ptr == '\n') {
                    // should only happen for requests
                    if (! m_is_request) return false;
                    m_headers_parse_state = PARSE_EXPECTING_CR;
                } else if (is_digit(*m_read_ptr)) {
                    http_msg.setVersionMinor( (http_msg.getVersionMinor() * 10)
                                              + (*m_read_ptr - '0') );
                } else {
                    return false;
                }
                break;
    
            case PARSE_STATUS_CODE_START:
                // parsing the first digit of the response status code
                if (!is_digit(*m_read_ptr)) return false;
                m_status_code = (*m_read_ptr - '0');
                m_headers_parse_state = PARSE_STATUS_CODE;
                break;
                
            case PARSE_STATUS_CODE:
                // parsing the response status code (not first digit)
                if (*m_read_ptr == ' ') {
                    m_status_message.erase();
                    m_headers_parse_state = PARSE_STATUS_MESSAGE;
                } else if (is_digit(*m_read_ptr)) {
                    m_status_code = ( (m_status_code * 10) + (*m_read_ptr - '0') );
                } else {
                    return false;
                }
                break;
                
            case PARSE_STATUS_MESSAGE:
                // parsing the response status message
                if (*m_read_ptr == '\r') {
                    m_headers_parse_state = PARSE_EXPECTING_NEWLINE;
                } else if (*m_read_ptr == '\n') {
                    m_headers_parse_state = PARSE_EXPECTING_CR;
                } else if (is_control(*m_read_ptr)) {
                    return false;
                } else if (m_status_message.size() >= ParserTraits::STATUS_MESSAGE_MAX) {
                    return false;
                } else {
                    m_status_message.push_back(*m_read_ptr);
                }
                break;
    
            case PARSE_EXPECTING_NEWLINE:
                // we received a CR; expecting a newline to follow
                if (*m_read_ptr == '\n') {
                    m_headers_parse_state = PARSE_HEADER_START;
                } else if (*m_read_ptr == '\r') {
                    // we received two CR's in a row
                    // assume CR only is (incorrectly) being used for line termination
                    // therefore, the message is finished
                    ++m_read_ptr;
                    m_bytes_last_read = (m_read_ptr - read_start_ptr);
                    m_bytes_total_read += m_bytes_last_read;
                    return true;
                } else if (*m_read_ptr == '\t' || *m_read_ptr == ' ') {
                    m_headers_parse_state = PARSE_HEADER_WHITESPACE;
                } else if (!is_char(*m_read_ptr) || is_control(*m_read_ptr) || is_special(*m_read_ptr)) {
                    return false;
                } else {
                    // assume it is the first character for the name of a header
                    m_header_name.erase();
                    m_header_name.push_back(*m_read_ptr);
                    m_headers_parse_state = PARSE_HEADER_NAME;
                }
                break;
    
            case PARSE_EXPECTING_CR:
                // we received a newline without a CR
                if (*m_read_ptr == '\r') {
                    m_headers_parse_state = PARSE_HEADER_START;
                } else if (*m_read_ptr == '\n') {
                    // we received two newlines in a row
                    // assume newline only is (incorrectly) being used for line termination
                    // therefore, the message is finished
                    ++m_read_ptr;
                    m_bytes_last_read = (m_read_ptr - read_start_ptr);
                    m_bytes_total_read += m_bytes_last_read;
                    return true;
                } else if (*m_read_ptr == '\t' || *m_read_ptr == ' ') {
                    m_headers_parse_state = PARSE_HEADER_WHITESPACE;
                } else if (!is_char(*m_read_ptr) || is_control(*m_read_ptr) || is_special(*m_read_ptr)) {
                    return false;
                } else {
                    // assume it is the first character for the name of a header
                    m_header_name.erase();
                    m_header_name.push_back(*m_read_ptr);
                    m_headers_parse_state = PARSE_HEADER_NAME;
                }
                break;
    
            case PARSE_HEADER_WHITESPACE:
                // parsing whitespace before a header name
                if (*m_read_ptr == '\r') {
                    m_headers_parse_state = PARSE_EXPECTING_NEWLINE;
                } else if (*m_read_ptr == '\n') {
                    m_headers_parse_state = PARSE_EXPECTING_CR;
                } else if (*m_read_ptr != '\t' && *m_read_ptr != ' ') {
                    if (!is_char(*m_read_ptr) || is_control(*m_read_ptr) || is_special(*m_read_ptr))
                        return false;
                    // assume it is the first character for the name of a header
                    m_header_name.erase();
                    m_header_name.push_back(*m_read_ptr);
                    m_headers_parse_state = PARSE_HEADER_NAME;
                }
                break;
    
            case PARSE_HEADER_START:
                // parsing the start of a new header
                if (*m_read_ptr == '\r') {
                    m_headers_parse_state = PARSE_EXPECTING_FINAL_NEWLINE;
                } else if (*m_read_ptr == '\n') {
                    m_headers_parse_state = PARSE_EXPECTING_FINAL_CR;
                } else if (*m_read_ptr == '\t' || *m_read_ptr == ' ') {
                    m_headers_parse_state = PARSE_HEADER_WHITESPACE;
                } else if (!is_char(*m_read_ptr) || is_control(*m_read_ptr) || is_special(*m_read_ptr)) {
                    return false;
                } else {
                    // first character for the name of a header
                    m_header_name.erase();
                    m_header_name.push_back(*m_read_ptr);
                    m_headers_parse_state = PARSE_HEADER_NAME;
                }
                break;
    
            case PARSE_HEADER_NAME:
                // parsing the name of a header
                if (*m_read_ptr == ':') {
                    m_header_value.erase();
                    m_headers_parse_state = PARSE_SPACE_BEFORE_HEADER_VALUE;
                } else if (!is_char(*m_read_ptr) || is_control(*m_read_ptr) || is_special(*m_read_ptr)) {
                    return false;
                } else if (m_header_name.size() >= ParserTraits::HEADER_NAME_MAX) {
                    return false;
                } else {
                    // character (not first) for the name of a header
                    m_header_name.push_back(*m_read_ptr);
                }
                break;
    
            case PARSE_SPACE_BEFORE_HEADER_VALUE:
                // parsing space character before a header's value
                if (*m_read_ptr == ' ') {
                    m_headers_parse_state = PARSE_HEADER_VALUE;
                } else if (*m_read_ptr == '\r') {
                    http_msg.addHeader(m_header_name, m_header_value);
                    m_headers_parse_state = PARSE_EXPECTING_NEWLINE;
                } else if (*m_read_ptr == '\n') {
                    http_msg.addHeader(m_header_name, m_header_value);
                    m_headers_parse_state = PARSE_EXPECTING_CR;
                } else if (!is_char(*m_read_ptr) || is_control(*m_read_ptr) || is_special(*m_read_ptr)) {
                    return false;
                } else {
                    // assume it is the first character for the value of a header
                    m_header_value.push_back(*m_read_ptr);
                    m_headers_parse_state = PARSE_HEADER_VALUE;
                }
                break;
    
            case PARSE_HEADER_VALUE:
                // parsing the value of a header
                if (*m_read_ptr == '\r') {
                    http_msg.addHeader(m_header_name, m_header_value);
                    m_headers_parse_state = PARSE_EXPECTING_NEWLINE;
                } else if (*m_read_ptr == '\n') {
                    http_msg.addHeader(m_header_name, m_header_value);
                    m_headers_parse_state = PARSE_EXPECTING_CR;
                } else if (is_control(*m_read_ptr)) {
                    return false;
                } else if (m_header_value.size() >= ParserTraits::HEADER_VALUE_MAX) {
                    return false;
                } else {
                    // character (not first) for the value of a header
                    m_header_value.push_back(*m_read_ptr);
                }
                break;
    
            case PARSE_EXPECTING_FINAL_NEWLINE:
                if (*m_read_ptr == '\n') ++m_read_ptr;
                m_bytes_last_read = (m_read_ptr - read_start_ptr);
                m_bytes_total_read += m_bytes_last_read;
                return true;
    
            case PARSE_EXPECTING_FINAL_CR:
                if (*m_read_ptr == '\r') ++m_read_ptr;
                m_bytes_last_read = (m_read_ptr - read_start_ptr);
                m_bytes_total_read += m_bytes_last_read;
                return true;
            }
    
            ++m_read_ptr;
        }
    
        m_bytes_last_read = (m_read_ptr - read_start_ptr);
        m_bytes_total_read += m_bytes_last_read;
        return boost::indeterminate;
    }
    
    template <typename Tag, typename ParserTraits>
    boost::tribool basic_parser<Tag,Traits>::parse_chunks(types::chunk_cache_t& chunk_buffers)
    {
        //
        // note that boost::tribool may have one of THREE states:
        //
        // false: encountered an error while parsing message
        // true: finished successfully parsing the message
        // indeterminate: parsed bytes, but the message is not yet finished
        //
        const char *read_start_ptr = m_read_ptr;
        m_bytes_last_read = 0;
        while (m_read_ptr < m_read_end_ptr) {
    
            switch (m_chunked_content_parse_state) {
            case PARSE_CHUNK_SIZE_START:
                // we have not yet started parsing the next chunk size
                if (is_hex_digit(*m_read_ptr)) {
                    m_chunk_size_str.erase();
                    m_chunk_size_str.push_back(*m_read_ptr);
                    m_chunked_content_parse_state = PARSE_CHUNK_SIZE;
                } else if (*m_read_ptr == ' ' || *m_read_ptr == '\x09' || *m_read_ptr == '\x0D' || *m_read_ptr == '\x0A') {
                    // Ignore leading whitespace.  Technically, the standard probably doesn't allow white space here, 
                    // but we'll be flexible, since there's no ambiguity.
                    break;
                } else {
                    return false;
                }
                break;
    
            case PARSE_CHUNK_SIZE:
                if (is_hex_digit(*m_read_ptr)) {
                    m_chunk_size_str.push_back(*m_read_ptr);
                } else if (*m_read_ptr == '\x0D') {
                    m_chunked_content_parse_state = PARSE_EXPECTING_LF_AFTER_CHUNK_SIZE;
                } else if (*m_read_ptr == ' ' || *m_read_ptr == '\x09') {
                    // Ignore trailing tabs or spaces.  Technically, the standard probably doesn't allow this, 
                    // but we'll be flexible, since there's no ambiguity.
                    m_chunked_content_parse_state = PARSE_EXPECTING_CR_AFTER_CHUNK_SIZE;
                } else {
                    return false;
                }
                break;
    
            case PARSE_EXPECTING_CR_AFTER_CHUNK_SIZE:
                if (*m_read_ptr == '\x0D') {
                    m_chunked_content_parse_state = PARSE_EXPECTING_LF_AFTER_CHUNK_SIZE;
                } else if (*m_read_ptr == ' ' || *m_read_ptr == '\x09') {
                    // Ignore trailing tabs or spaces.  Technically, the standard probably doesn't allow this, 
                    // but we'll be flexible, since there's no ambiguity.
                    break;
                } else {
                    return false;
                }
                break;
    
            case PARSE_EXPECTING_LF_AFTER_CHUNK_SIZE:
                // We received a CR; expecting LF to follow.  We can't be flexible here because 
                // if we see anything other than LF, we can't be certain where the chunk starts.
                if (*m_read_ptr == '\x0A') {
                    m_bytes_read_in_current_chunk = 0;
                    m_size_of_current_chunk = strtol(m_chunk_size_str.c_str(), 0, 16);
                    if (m_size_of_current_chunk == 0) {
                        m_chunked_content_parse_state = PARSE_EXPECTING_FINAL_CR_AFTER_LAST_CHUNK;
                    } else {
                        m_current_chunk.clear();
                        m_chunked_content_parse_state = PARSE_CHUNK;
                    }
                } else {
                    return false;
                }
                break;
    
            case PARSE_CHUNK:
                if (m_bytes_read_in_current_chunk < m_size_of_current_chunk) {
                    m_current_chunk.push_back(*m_read_ptr);
                    m_bytes_read_in_current_chunk++;
                }
                if (m_bytes_read_in_current_chunk == m_size_of_current_chunk) {
                    chunk_buffers.push_back(m_current_chunk);
                    m_current_chunk.clear();
                    m_chunked_content_parse_state = PARSE_EXPECTING_CR_AFTER_CHUNK;
                }
                break;
    
            case PARSE_EXPECTING_CR_AFTER_CHUNK:
                // we've read exactly m_size_of_current_chunk bytes since starting the current chunk
                if (*m_read_ptr == '\x0D') {
                    m_chunked_content_parse_state = PARSE_EXPECTING_LF_AFTER_CHUNK;
                } else {
                    return false;
                }
                break;
    
            case PARSE_EXPECTING_LF_AFTER_CHUNK:
                // we received a CR; expecting LF to follow
                if (*m_read_ptr == '\x0A') {
                    m_chunked_content_parse_state = PARSE_CHUNK_SIZE_START;
                } else {
                    return false;
                }
                break;
    
            case PARSE_EXPECTING_FINAL_CR_AFTER_LAST_CHUNK:
                // we've read the final chunk; expecting final CRLF
                if (*m_read_ptr == '\x0D') {
                    m_chunked_content_parse_state = PARSE_EXPECTING_FINAL_LF_AFTER_LAST_CHUNK;
                } else {
                    return false;
                }
                break;
    
            case PARSE_EXPECTING_FINAL_LF_AFTER_LAST_CHUNK:
                // we received the final CR; expecting LF to follow
                if (*m_read_ptr == '\x0A') {
                    ++m_read_ptr;
                    m_bytes_last_read = (m_read_ptr - read_start_ptr);
                    m_bytes_total_read += m_bytes_last_read;
                    return true;
                } else {
                    return false;
                }
            }
    
            ++m_read_ptr;
        }
    
        m_bytes_last_read = (m_read_ptr - read_start_ptr);
        m_bytes_total_read += m_bytes_last_read;
        return boost::indeterminate;
    }

    template <typename Tag, typename ParserTraits>
    std::size_t basic_parser<Tag,Traits>::consume_content(basic_message<Tag>& http_msg)
    {
        // get the payload content length from the HTTP headers
        http_msg.updateContentLengthUsingHeader();
    
        // read the post content
        std::size_t content_bytes_to_read = http_msg.getContentLength();
        char *post_buffer = http_msg.createContentBuffer();
        
        if (m_read_ptr < m_read_end_ptr) {
            // there are extra bytes left from the last read operation
            // copy them into the beginning of the content buffer
            const std::size_t bytes_left_in_read_buffer = bytes_available();
            
            if (bytes_left_in_read_buffer >= http_msg.getContentLength()) {
                // the last read operation included all of the payload content
                memcpy(post_buffer, m_read_ptr, http_msg.getContentLength());
                content_bytes_to_read = 0;
                m_read_ptr += http_msg.getContentLength();
            } else {
                // only some of the post content has been read so far
                memcpy(post_buffer, m_read_ptr, bytes_left_in_read_buffer);
                content_bytes_to_read -= bytes_left_in_read_buffer;
                m_read_ptr = m_read_end_ptr;
            }
        }
        
        m_bytes_last_read = (http_msg.getContentLength() - content_bytes_to_read);
        m_bytes_total_read += m_bytes_last_read;
        return m_bytes_last_read;
    }
    
    template <typename Tag, typename ParserTraits>
    std::size_t basic_parser<Tag,Traits>::consume_content_as_next_chunk(types::chunk_cache_t& chunk_buffers)
    {
        if (bytes_available() == 0) {
            m_bytes_last_read = 0;
        } else {
            std::vector<char>	next_chunk;
            while (m_read_ptr < m_read_end_ptr) {
                next_chunk.push_back(*m_read_ptr);
                ++m_read_ptr;
            }
            chunk_buffers.push_back(next_chunk);
            m_bytes_last_read = next_chunk.size();
            m_bytes_total_read += m_bytes_last_read;
        }
        return m_bytes_last_read;
    }
        
    template <typename Tag, typename ParserTraits>
    void basic_parser<Tag,Traits>::finish(basic_request<Tag>& http_request)
    {
        http_request.setIsValid(true);
        http_request.setMethod(m_method);
        http_request.setResource(m_resource);
        http_request.setQueryString(m_query_string);
        
        // parse query pairs from the URI query string
        if (! m_query_string.empty()) {
            if (! parseURLEncoded(http_request.getQueryParams(),
                                  m_query_string.c_str(),
                                  m_query_string.size())) 
        }
        
        // parse query pairs from post content (x-www-form-urlencoded)
        if (http_request.getHeader(types::HEADER_CONTENT_TYPE) ==
            types::CONTENT_TYPE_URLENCODED)
        {
            if (! parseURLEncoded(http_request.getQueryParams(),
                                  http_request.getContent(),
                                  http_request.getContentLength())) 
        }
        
        // parse "Cookie" headers
        std::pair<types::headers::const_iterator, types::headers::const_iterator>
            cookie_pair = http_request.getHeaders().equal_range(types::HEADER_COOKIE);
        for (types::headers::const_iterator cookie_iterator = cookie_pair.first;
             cookie_iterator != http_request.getHeaders().end()
             && cookie_iterator != cookie_pair.second; ++cookie_iterator)
        {
            if (! parseCookieHeader(http_request.getCookieParams(),
                                    cookie_iterator->second) )
        }
    }

    template <typename Tag, typename ParserTraits>
    void basic_parser<Tag,Traits>::finish(basic_response<Tag>& http_response)
    {
        http_response.setIsValid(true);
        http_response.setStatusCode(m_status_code);
        http_response.setStatusMessage(m_status_message);
    }
    
    template <typename Tag, typename ParserTraits>
    inline void basic_parser<Tag,Traits>::reset(void)
    {
        m_headers_parse_state = (m_is_request ? PARSE_METHOD_START : PARSE_HTTP_VERSION_H);
        m_chunked_content_parse_state = PARSE_CHUNK_SIZE_START;
        m_status_code = 0;
        m_status_message.erase();
        m_method.erase();
        m_resource.erase();
        m_query_string.erase();
        m_current_chunk.clear();
        m_bytes_last_read = m_bytes_total_read = 0;
    }
 
   template <typename Tag, typename ParserTraits>
   static bool basic_parser<Tag,Traits>::parse_url_encoded(types::query_params& params,
                                                           const char *ptr, const size_t len)
    {
        // used to track whether we are parsing the name or value
        enum query_parse_state_t {
            QUERY_PARSE_NAME, QUERY_PARSE_VALUE
        } parse_state = QUERY_PARSE_NAME;
    
        // misc other variables used for parsing
        const char * const end = ptr + len;
        std::string query_name;
        std::string query_value;
        
        // iterate through each encoded character
        while (ptr < end) {
            switch (parse_state) {
            
            case QUERY_PARSE_NAME:
                // parsing query name
                if (*ptr == '=') {
                    // end of name found
                    if (query_name.empty()) return false;
                    parse_state = QUERY_PARSE_VALUE;
                } else if (*ptr == '&') {
                    // value is empty (OK)
                    if (query_name.empty()) return false;
                    params.insert( std::make_pair(query_name, query_value) );
                    query_name.erase();
                } else if (is_control(*ptr) || query_name.size() >= ParserTraits::QUERY_NAME_MAX) {
                    // control character detected, or max sized exceeded
                    return false;
                } else {
                    // character is part of the name
                    query_name.push_back(*ptr);
                }
                break;
    
            case QUERY_PARSE_VALUE:
                // parsing query value
                if (*ptr == '&') {
                    // end of value found (OK if empty)
                    params.insert( std::make_pair(query_name, query_value) );
                    query_name.erase();
                    query_value.erase();
                    parse_state = QUERY_PARSE_NAME;
                } else if (is_control(*ptr) || query_value.size() >= ParserTraits::QUERY_VALUE_MAX) {
                    // control character detected, or max sized exceeded
                    return false;
                } else {
                    // character is part of the value
                    query_value.push_back(*ptr);
                }
                break;
            }
            
            ++ptr;
        }
        
        // handle last pair in string
        if (! query_name.empty())
            params.insert( std::make_pair(query_name, query_value) );
        
        return true;
    }
                                 
    template <typename Tag, typename ParserTraits>
    static bool basic_parser<Tag,Traits>::parse_cookie_header(types::cookie_params& params,
                                                              const std::string& cookie_header)
    {
        // BASED ON RFC 2109
        // 
        // The current implementation ignores cookie attributes which begin with '$'
        // (i.e. $Path=/, $Domain=, etc.)
    
        // used to track what we are parsing
        enum cookie_parse_state_t {
            COOKIE_PARSE_NAME, COOKIE_PARSE_VALUE, COOKIE_PARSE_IGNORE
        } parse_state = COOKIE_PARSE_NAME;
        
        // misc other variables used for parsing
        std::string cookie_name;
        std::string cookie_value;
        char value_quote_character = '\0';
        
        // iterate through each character
        for (std::string::const_iterator string_iterator = cookie_header.begin();
             string_iterator != cookie_header.end(); ++string_iterator)
        {
            switch (parse_state) {
                
            case COOKIE_PARSE_NAME:
                // parsing cookie name
                if (*string_iterator == '=') {
                    // end of name found
                    if (cookie_name.empty()) return false;
                    value_quote_character = '\0';
                    parse_state = COOKIE_PARSE_VALUE;
                } else if (*string_iterator == ';' || *string_iterator == ',') {
                    // ignore empty cookie names since this may occur naturally
                    // when quoted values are encountered
                    if (! cookie_name.empty()) {
                        // value is empty (OK)
                        if (cookie_name[0] != '$')
                            params.insert( std::make_pair(cookie_name, cookie_value) );
                        cookie_name.erase();
                    }
                } else if (*string_iterator != ' ') {	// ignore whitespace
                    // check if control character detected, or max sized exceeded
                    if (is_control(*string_iterator) || cookie_name.size() >= ParserTraits::COOKIE_NAME_MAX)
                        return false;
                    // character is part of the name
                    // cookie names are case insensitive -> convert to lowercase
                    cookie_name.push_back( tolower(*string_iterator) );
                }
                break;
                
            case COOKIE_PARSE_VALUE:
                // parsing cookie value
                if (value_quote_character == '\0') {
                    // value is not (yet) quoted
                    if (*string_iterator == ';' || *string_iterator == ',') {
                        // end of value found (OK if empty)
                        if (cookie_name[0] != '$') 
                            params.insert( std::make_pair(cookie_name, cookie_value) );
                        cookie_name.erase();
                        cookie_value.erase();
                        parse_state = COOKIE_PARSE_NAME;
                    } else if (*string_iterator == '\'' || *string_iterator == '"') {
                        if (cookie_value.empty()) {
                            // begin quoted value
                            value_quote_character = *string_iterator;
                        } else if (cookie_value.size() >= ParserTraits::COOKIE_VALUE_MAX) {
                            // max size exceeded
                            return false;
                        } else {
                            // assume character is part of the (unquoted) value
                            cookie_value.push_back(*string_iterator);
                        }
                    } else if (*string_iterator != ' ') {	// ignore unquoted whitespace
                        // check if control character detected, or max sized exceeded
                        if (is_control(*string_iterator) || cookie_value.size() >= ParserTraits::COOKIE_VALUE_MAX)
                            return false;
                        // character is part of the (unquoted) value
                        cookie_value.push_back(*string_iterator);
                    }
                } else {
                    // value is quoted
                    if (*string_iterator == value_quote_character) {
                        // end of value found (OK if empty)
                        if (cookie_name[0] != '$') 
                            params.insert( std::make_pair(cookie_name, cookie_value) );
                        cookie_name.erase();
                        cookie_value.erase();
                        parse_state = COOKIE_PARSE_IGNORE;
                    } else if (cookie_value.size() >= ParserTraits::COOKIE_VALUE_MAX) {
                        // max size exceeded
                        return false;
                    } else {
                        // character is part of the (quoted) value
                        cookie_value.push_back(*string_iterator);
                    }
                }
                break;
                
            case COOKIE_PARSE_IGNORE:
                // ignore everything until we reach a comma "," or semicolon ";"
                if (*string_iterator == ';' || *string_iterator == ',')
                    parse_state = COOKIE_PARSE_NAME;
                break;
            }
        }
        
        // handle last cookie in string
        if (! cookie_name.empty() && cookie_name[0] != '$')
            params.insert( std::make_pair(cookie_name, cookie_value) );
        
        return true;
    }

}; // namespace http

}; // namespace network

}; // namespace boost

#endif // BOOST_NETWORK_PROTOCOL_HTTP_PARSER_IPP