MaxScale/server/core/maxscale/httprequest.hh
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

297 lines
7.3 KiB
C++

#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: 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/cppdefs.hh>
#include <deque>
#include <map>
#include <string>
#include <tr1/memory>
#include <cstdint>
#include <microhttpd.h>
#include <maxscale/jansson.hh>
#include <maxscale/utils.hh>
#include <maxscale/http.hh>
// The API version part of the URL
#define MXS_REST_API_VERSION "v1"
static int value_iterator(void *cls,
enum MHD_ValueKind kind,
const char *key,
const char *value)
{
std::pair<std::string, std::string>* cmp = (std::pair<std::string, std::string>*)cls;
if (strcasecmp(cmp->first.c_str(), key) == 0 && value)
{
cmp->second = value;
return MHD_NO;
}
return MHD_YES;
}
static int value_sum_iterator(void *cls,
enum MHD_ValueKind kind,
const char *key,
const char *value)
{
size_t& count = *(size_t*)cls;
count++;
return MHD_YES;
}
static int value_copy_iterator(void *cls,
enum MHD_ValueKind kind,
const char *key,
const char *value)
{
std::string k = key;
if (value)
{
k += "=";
k += value;
}
char**& dest = *(char***) cls;
*dest = MXS_STRDUP_A(k.c_str());
dest++;
return MHD_YES;
}
class HttpRequest
{
HttpRequest(const HttpRequest&);
HttpRequest& operator = (const HttpRequest);
public:
/**
* @brief Parse a request
*
* @param request Request to parse
*
* @return Parsed statement or NULL if request is not valid
*/
HttpRequest(struct MHD_Connection *connection, std::string url, std::string method, json_t* data);
~HttpRequest();
/**
* @brief Return request verb type
*
* @return One of the HTTP verb values
*/
const std::string& get_verb() const
{
return m_verb;
}
/**
* @brief Get header value
*
* @param header Header to get
*
* @return Header value or empty string if the header was not found
*/
std::string get_header(const std::string& header) const
{
std::pair<std::string, std::string> p;
p.first = header;
MHD_get_connection_values(m_connection, MHD_HEADER_KIND,
value_iterator, &p);
return p.second;
}
/**
* @brief Get option value
*
* @param header Option to get
*
* @return Option value or empty string if the option was not found
*/
std::string get_option(const std::string& option) const
{
std::pair<std::string, std::string> p;
p.first = option;
MHD_get_connection_values(m_connection, MHD_GET_ARGUMENT_KIND,
value_iterator, &p);
return p.second;
}
/**
* @brief Get request option count
*
* @return Number of options in the request
*/
size_t get_option_count() const
{
size_t rval = 0;
MHD_get_connection_values(m_connection, MHD_GET_ARGUMENT_KIND,
value_sum_iterator, &rval);
return rval;
}
/**
* @brief Copy options to an array
*
* The @c dest parameter must be able to hold at least get_option_count()
* pointers. The values stored need to be freed by the caller.
*
* @param dest Destination where options are copied
*/
void copy_options(char** dest) const
{
MHD_get_connection_values(m_connection, MHD_GET_ARGUMENT_KIND,
value_copy_iterator, &dest);
}
/**
* @brief Return request body
*
* @return Request body or empty string if no body is defined
*/
const std::string& get_json_str() const
{
return m_json_string;
}
/**
* @brief Return raw JSON body
*
* @return Raw JSON body or NULL if no body is defined
*/
json_t* get_json() const
{
return m_json.get();
}
/**
* @brief Get complete request URI
*
* @return The complete request URI
*/
std::string get_uri() const
{
return m_resource;
}
/**
* @brief Get URI part
*
* @param idx Zero indexed part number in URI
*
* @return The request URI part or empty string if no part was found
*/
std::string uri_part(uint32_t idx) const
{
return m_resource_parts.size() > idx ? m_resource_parts[idx] : "";
}
/**
* @brief Return a segment of the URI
*
* Combines a range of parts into a segment of the URI. Each part is
* separated by a forward slash.
*
* @param start Start of range
* @param end End of range, not inclusive
*
* @return The URI segment that matches this range
*/
std::string uri_segment(uint32_t start, uint32_t end) const
{
std::string rval;
for (uint32_t i = start; i < end && i < m_resource_parts.size(); i++)
{
if (i > start)
{
rval += "/";
}
rval += m_resource_parts[i];
}
return rval;
}
/**
* @brief Return how many parts are in the URI
*
* @return Number of URI parts
*/
size_t uri_part_count() const
{
return m_resource_parts.size();
}
/**
* @brief Return the last part of the URI
*
* @return The last URI part
*/
std::string last_uri_part() const
{
return m_resource_parts.size() > 0 ? m_resource_parts[m_resource_parts.size() - 1] : "";
}
/**
* @brief Return the value of the Host header
*
* @return The value of the Host header
*/
const char* host() const
{
return m_hostname.c_str();
}
/**
* @brief Convert request to string format
*
* The returned string should be logically equivalent to the original request.
*
* @return The request in string format
*/
std::string to_string() const;
/**
* @brief Drop the API version prefix
*
* @return True if prefix is present and was successfully removed
*/
bool validate_api_version();
private:
/** Constants */
static const std::string HTTP_PREFIX;
static const std::string HTTPS_PREFIX;
std::map<std::string, std::string> m_options; /**< Request options */
mxs::Closer<json_t*> m_json; /**< Request body */
std::string m_json_string; /**< String version of @c m_json */
std::string m_resource; /**< Requested resource */
std::deque<std::string> m_resource_parts; /**< @c m_resource split into parts */
std::string m_verb; /**< Request method */
std::string m_hostname; /**< The value of the Host header */
struct MHD_Connection* m_connection;
};