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.
This commit is contained in:
Markus Mäkelä 2017-04-15 06:00:40 +03:00 committed by Markus Mäkelä
parent 23b6fb3e6d
commit 605fed7839
3 changed files with 253 additions and 0 deletions

View File

@ -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

112
server/core/httpparser.cc Normal file
View File

@ -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 <ctype.h>
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<string, string> 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()
{
}

View File

@ -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 <maxscale/cppdefs.hh>
#include <string>
#include <map>
#include <tr1/memory>
using std::shared_ptr;
using std::string;
using std::map;
class HttpParser;
/** Typedef for managed pointer */
typedef std::shared_ptr<HttpParser> 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<string, string>::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<string, string> m_headers;
string m_body;
string m_resource;
enum http_verb m_verb;
};