// // request_parser.cpp // ~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2014 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // 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) // #include "request_parser.hpp" #include #include #include #include "request.hpp" namespace http { namespace server4 { // Enable the pseudo-keywords reenter, yield and fork. #include std::string request_parser::content_length_name_ = "Content-Length"; boost::tribool request_parser::consume(request& req, char c) { reenter (this) { req.method.clear(); req.uri.clear(); req.http_version_major = 0; req.http_version_minor = 0; req.headers.clear(); req.content.clear(); content_length_ = 0; // Request method. while (is_char(c) && !is_ctl(c) && !is_tspecial(c) && c != ' ') { req.method.push_back(c); yield return boost::indeterminate; } if (req.method.empty()) return false; // Space. if (c != ' ') return false; yield return boost::indeterminate; // URI. while (!is_ctl(c) && c != ' ') { req.uri.push_back(c); yield return boost::indeterminate; } if (req.uri.empty()) return false; // Space. if (c != ' ') return false; yield return boost::indeterminate; // HTTP protocol identifier. if (c != 'H') return false; yield return boost::indeterminate; if (c != 'T') return false; yield return boost::indeterminate; if (c != 'T') return false; yield return boost::indeterminate; if (c != 'P') return false; yield return boost::indeterminate; // Slash. if (c != '/') return false; yield return boost::indeterminate; // Major version number. if (!is_digit(c)) return false; while (is_digit(c)) { req.http_version_major = req.http_version_major * 10 + c - '0'; yield return boost::indeterminate; } // Dot. if (c != '.') return false; yield return boost::indeterminate; // Minor version number. if (!is_digit(c)) return false; while (is_digit(c)) { req.http_version_minor = req.http_version_minor * 10 + c - '0'; yield return boost::indeterminate; } // CRLF. if (c != '\r') return false; yield return boost::indeterminate; if (c != '\n') return false; yield return boost::indeterminate; // Headers. while ((is_char(c) && !is_ctl(c) && !is_tspecial(c) && c != '\r') || (c == ' ' || c == '\t')) { if (c == ' ' || c == '\t') { // Leading whitespace. Must be continuation of previous header's value. if (req.headers.empty()) return false; while (c == ' ' || c == '\t') yield return boost::indeterminate; } else { // Start the next header. req.headers.push_back(header()); // Header name. while (is_char(c) && !is_ctl(c) && !is_tspecial(c) && c != ':') { req.headers.back().name.push_back(c); yield return boost::indeterminate; } // Colon and space separates the header name from the header value. if (c != ':') return false; yield return boost::indeterminate; if (c != ' ') return false; yield return boost::indeterminate; } // Header value. while (is_char(c) && !is_ctl(c) && c != '\r') { req.headers.back().value.push_back(c); yield return boost::indeterminate; } // CRLF. if (c != '\r') return false; yield return boost::indeterminate; if (c != '\n') return false; yield return boost::indeterminate; } // CRLF. if (c != '\r') return false; yield return boost::indeterminate; if (c != '\n') return false; // Check for optional Content-Length header. for (std::size_t i = 0; i < req.headers.size(); ++i) { if (headers_equal(req.headers[i].name, content_length_name_)) { try { content_length_ = boost::lexical_cast(req.headers[i].value); } catch (boost::bad_lexical_cast&) { return false; } } } // Content. while (req.content.size() < content_length_) { yield return boost::indeterminate; req.content.push_back(c); } } return true; } // Disable the pseudo-keywords reenter, yield and fork. #include bool request_parser::is_char(int c) { return c >= 0 && c <= 127; } bool request_parser::is_ctl(int c) { return (c >= 0 && c <= 31) || (c == 127); } bool request_parser::is_tspecial(int c) { switch (c) { case '(': case ')': case '<': case '>': case '@': case ',': case ';': case ':': case '\\': case '"': case '/': case '[': case ']': case '?': case '=': case '{': case '}': case ' ': case '\t': return true; default: return false; } } bool request_parser::is_digit(int c) { return c >= '0' && c <= '9'; } bool request_parser::tolower_compare(char a, char b) { return std::tolower(a) == std::tolower(b); } bool request_parser::headers_equal(const std::string& a, const std::string& b) { if (a.length() != b.length()) return false; return std::equal(a.begin(), a.end(), b.begin(), &request_parser::tolower_compare); } } // namespace server4 } // namespace http