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:
committed by
Markus Mäkelä
parent
23b6fb3e6d
commit
605fed7839
@ -14,6 +14,7 @@ add_library(maxscale-common SHARED
|
|||||||
hashtable.cc
|
hashtable.cc
|
||||||
hint.cc
|
hint.cc
|
||||||
housekeeper.cc
|
housekeeper.cc
|
||||||
|
httpparser.cc
|
||||||
listener.cc
|
listener.cc
|
||||||
load_utils.cc
|
load_utils.cc
|
||||||
log_manager.cc
|
log_manager.cc
|
||||||
|
|||||||
112
server/core/httpparser.cc
Normal file
112
server/core/httpparser.cc
Normal 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()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
140
server/core/maxscale/httpparser.hh
Normal file
140
server/core/maxscale/httpparser.hh
Normal 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;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user