 19c4016e65
			
		
	
	19c4016e65
	
	
	
		
			
			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.
		
			
				
	
	
		
			207 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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();
 | |
| }
 |