MaxScale/server/core/httprequest.cc
Markus Mäkelä 19c4016e65 MXS-1220: Add to_string to HttpRequest
Allowing requests to be converted to raw HTTP requests allows them to be
propagated to other instances of MaxScale. This should allow multiple
MaxScales to perform the same action in a coherent manner.

A simple clustering mechanism needs to be added to make MaxScale aware of
other instances.
2017-06-02 12:52:33 +03:00

207 lines
4.4 KiB
C++

/*
* 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: 2020-01-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/httprequest.hh"
#include "maxscale/admin.hh"
#include <ctype.h>
#include <string.h>
#include <sstream>
using std::string;
using std::deque;
#define HTTP_HOST_HEADER "Host"
#define HTTP_METHOD_OVERRIDE "X-HTTP-Method-Override"
const std::string HttpRequest::HTTP_PREFIX = "http://";
const std::string HttpRequest::HTTPS_PREFIX = "https://";
/** TODO: Move this to a C++ string utility header */
namespace maxscale
{
static inline string& trim(string& str)
{
if (str.length())
{
if (isspace(*str.begin()))
{
string::iterator it = str.begin();
while (it != str.end() && isspace(*it))
{
it++;
}
str.erase(str.begin(), it);
}
if (isspace(*str.rbegin()))
{
string::reverse_iterator it = str.rbegin();
while (it != str.rend() && isspace(*it))
{
it++;
}
str.erase(it.base(), str.end());
}
}
return str;
}
}
static void process_uri(string& uri, std::deque<string>& uri_parts)
{
/** Clean up trailing slashes in requested resource */
while (uri.length() > 1 && *uri.rbegin() == '/')
{
uri.erase(uri.find_last_of("/"));
}
string my_uri = uri;
while (my_uri.length() && *my_uri.begin() == '/')
{
my_uri.erase(my_uri.begin());
}
while (my_uri.length() > 0)
{
size_t pos = my_uri.find("/");
string part = pos == string::npos ? my_uri : my_uri.substr(0, pos);
my_uri.erase(0, pos == string::npos ? pos : pos + 1);
uri_parts.push_back(part);
}
}
HttpRequest::HttpRequest(struct MHD_Connection *connection, string url, string method, json_t* data):
m_json(data),
m_json_string(data ? mxs::json_dump(data, 0) : ""),
m_resource(url),
m_verb(method),
m_connection(connection)
{
process_uri(url, m_resource_parts);
m_hostname = mxs_admin_https_enabled() ? HttpRequest::HTTPS_PREFIX : HttpRequest::HTTP_PREFIX;
m_hostname += get_header(HTTP_HOST_HEADER);
string method_override = get_header(HTTP_METHOD_OVERRIDE);
if (method_override.length())
{
m_verb = method_override;
}
if (m_hostname[m_hostname.size() - 1] != '/')
{
m_hostname += "/";
}
m_hostname += MXS_REST_API_VERSION;
}
HttpRequest::~HttpRequest()
{
}
bool HttpRequest::validate_api_version()
{
bool rval = false;
if (m_resource_parts.size() > 0 &&
m_resource_parts[0] == MXS_REST_API_VERSION)
{
m_resource_parts.pop_front();
rval = true;
}
return rval;
}
namespace
{
struct ValueFormatter
{
std::stringstream ss;
const char* separator;
const char* terminator;
ValueFormatter(const char* sep, const char* term):
separator(sep), terminator(term)
{
}
};
}
static int value_combine_cb(void *cls,
enum MHD_ValueKind kind,
const char *key,
const char *value)
{
ValueFormatter& cnf = *(ValueFormatter*)cls;
cnf.ss << key;
if (value)
{
cnf.ss << cnf.separator << value;
}
cnf.ss << cnf.terminator;
return MHD_YES;
}
std::string HttpRequest::to_string() const
{
std::stringstream req;
req << m_verb << " " << m_resource;
ValueFormatter opts("=", "&");
MHD_get_connection_values(m_connection, MHD_GET_ARGUMENT_KIND,
value_combine_cb, &opts);
std::string optstr = opts.ss.str();
size_t len = optstr.length();
if (len)
{
req << "?";
if (optstr[len - 1] == '&')
{
optstr.erase(len - 1);
}
}
req << optstr << " " << "HTTP/1.1" << "\r\n";
ValueFormatter hdr(": ", "\r\n");
MHD_get_connection_values(m_connection, MHD_HEADER_KIND,
value_combine_cb, &hdr);
std::string hdrstr = hdr.ss.str();
if (hdrstr.length())
{
req << hdrstr;
}
req << "\r\n";
req << m_json_string;
return req.str();
}