From 605fed7839820245d02a211a515a5a7cc1635aab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Sat, 15 Apr 2017 06:00:40 +0300 Subject: [PATCH] MXS-1220: Add HTTP parser The HTTP parser parses HTTP/1.1 messages into easily manageable data structures. This should make it easier to map the HTTP requests into actual commands in MaxScale. --- server/core/CMakeLists.txt | 1 + server/core/httpparser.cc | 112 +++++++++++++++++++++++ server/core/maxscale/httpparser.hh | 140 +++++++++++++++++++++++++++++ 3 files changed, 253 insertions(+) create mode 100644 server/core/httpparser.cc create mode 100644 server/core/maxscale/httpparser.hh diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 6f63cca4b..b718f1dfd 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -14,6 +14,7 @@ add_library(maxscale-common SHARED hashtable.cc hint.cc housekeeper.cc + httpparser.cc listener.cc load_utils.cc log_manager.cc diff --git a/server/core/httpparser.cc b/server/core/httpparser.cc new file mode 100644 index 000000000..30de6379e --- /dev/null +++ b/server/core/httpparser.cc @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2019-07-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include "maxscale/httpparser.hh" + +#include + +static enum http_verb string_to_http_verb(string& verb) +{ + if (verb == "GET") + { + return HTTP_GET; + } + else if (verb == "POST") + { + return HTTP_POST; + } + else if (verb == "PUT") + { + return HTTP_PUT; + } + else if (verb == "PATCH") + { + return HTTP_PATCH; + } + else if (verb == "OPTIONS") + { + return HTTP_OPTIONS; + } + + return HTTP_UNKNOWN; +} + +HttpParser* HttpParser::parse(string request) +{ + size_t pos = request.find("\r\n"); + string request_line = request.substr(0, pos); + request.erase(0, pos + 2); + + pos = request_line.find(" "); + string verb = request_line.substr(0, pos); + request_line.erase(0, pos + 1); + + pos = request_line.find(" "); + string uri = request_line.substr(0, pos); + request_line.erase(0, pos + 1); + + pos = request_line.find("\r\n"); + string http_version = request_line.substr(0, pos); + request_line.erase(0, pos + 2); + + map headers; + + while ((pos = request.find("\r\n")) != string::npos) + { + string header_line = request.substr(0, pos); + request.erase(0, pos + 2); + + if (header_line.length() == 0) + { + /** End of headers */ + break; + } + + if ((pos = header_line.find(":")) != string::npos) + { + string key = header_line.substr(0, pos); + header_line.erase(0, pos + 1); + + while (isspace(header_line[0])) + { + header_line.erase(0, 1); + } + + headers[key] = header_line; + } + } + + HttpParser* parser = NULL; + enum http_verb verb_value = string_to_http_verb(verb); + + if (http_version == "HTTP/1.1" && verb_value != HTTP_UNKNOWN) + { + parser = new HttpParser(); + parser->m_verb = verb_value; + parser->m_resource = uri; + parser->m_headers = headers; + parser->m_body = request; + } + + return parser; +} + +HttpParser::HttpParser() +{ + +} + +HttpParser::~HttpParser() +{ + +} diff --git a/server/core/maxscale/httpparser.hh b/server/core/maxscale/httpparser.hh new file mode 100644 index 000000000..ed6955ad2 --- /dev/null +++ b/server/core/maxscale/httpparser.hh @@ -0,0 +1,140 @@ +#pragma once +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2019-07-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include + +#include +#include +#include + +using std::shared_ptr; +using std::string; +using std::map; + +class HttpParser; + +/** Typedef for managed pointer */ +typedef std::shared_ptr SHttpParser; + +enum http_verb +{ + HTTP_UNKNOWN, + HTTP_GET, + HTTP_PUT, + HTTP_POST, + HTTP_OPTIONS, + HTTP_PATCH +}; + +class HttpParser +{ +public: + /** + * @brief Parse a request + * + * @param request Request to parse + * + * @return Parsed statement or NULL if request is not valid + */ + static HttpParser* parse(string request); + + ~HttpParser(); + + /** + * @brief Return request verb type + * + * @return One of the HTTP verb values + */ + enum http_verb get_verb() const + { + return m_verb; + } + + /** + * @brief Check if a request contains the specified header + * + * @param header Header to check + * + * @return True if header is in the request + */ + bool have_header(const string& header) const + { + return m_headers.find(header) != m_headers.end(); + } + + /** + * @brief Get header value + * + * @param header Header to get + * + * @return String value or empty string if no header found + */ + const string get_header(const string header) + { + string rval; + map::iterator it = m_headers.find(header); + + if (it != m_headers.end()) + { + rval = it->second; + } + + return rval; + } + + /** + * @brief Check if body is defined + * + * @return True if body is defined + */ + bool have_body() const + { + return m_body.length() != 0; + } + + /** + * @brief Return request body + * + * @return Request body or empty string if no body is defined + */ + const string& get_body() const + { + return m_body; + } + + void set_body(string body) + { + m_body = body; + } + + /** + * @brief Get request resource + * + * @return The request resoure + */ + const string& get_resource() const + { + return m_resource; + } + +private: + HttpParser(); + HttpParser(const HttpParser&); + HttpParser& operator = (const HttpParser&); + + map m_headers; + string m_body; + string m_resource; + enum http_verb m_verb; +};