MXS-1220: Rename and reorganize HttpParser
The HttpParser class was renamed to HttpRequest as it parses and processes only HTTP requests. A second class that creates a HTTP response needs to be created to handle the response generation. Moved some of the HTTP constants and helper functions to a separate http.hh header.
This commit is contained in:

committed by
Markus Mäkelä

parent
e34b65658e
commit
4eb121ce35
@ -14,7 +14,7 @@ add_library(maxscale-common SHARED
|
||||
hashtable.cc
|
||||
hint.cc
|
||||
housekeeper.cc
|
||||
httpparser.cc
|
||||
httprequest.cc
|
||||
listener.cc
|
||||
load_utils.cc
|
||||
log_manager.cc
|
||||
|
@ -12,7 +12,7 @@
|
||||
*/
|
||||
|
||||
#include "maxscale/adminclient.hh"
|
||||
#include "maxscale/httpparser.hh"
|
||||
#include "maxscale/httprequest.hh"
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
@ -77,9 +77,14 @@ static bool read_request(int fd, string& output)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool write_response(int fd, string input)
|
||||
static bool write_response(int fd, enum http_code code, const string& body)
|
||||
{
|
||||
return write(fd, input.c_str(), input.length()) != -1;
|
||||
string payload = "HTTP/1.1 ";
|
||||
payload += http_code_to_string(code);
|
||||
payload += "\r\n\r\n";
|
||||
payload += body;
|
||||
|
||||
return write(fd, payload.c_str(), payload.length()) != -1;
|
||||
}
|
||||
|
||||
void AdminClient::process()
|
||||
@ -89,15 +94,12 @@ void AdminClient::process()
|
||||
|
||||
if (read_request(m_fd, request))
|
||||
{
|
||||
SHttpParser parser(HttpParser::parse(request));
|
||||
SHttpRequest parser(HttpRequest::parse(request));
|
||||
|
||||
string status = parser.get() ? "200 OK" : "400 Bad Request";
|
||||
|
||||
stringstream resp;
|
||||
resp << "HTTP/1.1 " << status << "\r\n\r\n" << parser->get_body() << "\r\n";
|
||||
enum http_code status = parser.get() ? HTTP_200_OK : HTTP_400_BAD_REQUEST;
|
||||
|
||||
atomic_write_int64(&m_last_activity, hkheartbeat);
|
||||
write_response(m_fd, resp.str());
|
||||
write_response(m_fd, HTTP_200_OK, parser->get_body());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -11,41 +11,34 @@
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#include "maxscale/httpparser.hh"
|
||||
#include "maxscale/httprequest.hh"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
static enum http_verb string_to_http_verb(string& verb)
|
||||
/** TODO: Move this to a C++ string utility header */
|
||||
namespace maxscale
|
||||
{
|
||||
if (verb == "GET")
|
||||
static inline string& trim(string& str)
|
||||
{
|
||||
while (isspace(*str.begin()))
|
||||
{
|
||||
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;
|
||||
str.erase(str.begin());
|
||||
}
|
||||
|
||||
return HTTP_UNKNOWN;
|
||||
while (isspace(*str.rbegin()))
|
||||
{
|
||||
str.erase(str.rbegin().base());
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
HttpParser* HttpParser::parse(string request)
|
||||
HttpRequest* HttpRequest::parse(string data)
|
||||
{
|
||||
size_t pos = request.find("\r\n");
|
||||
string request_line = request.substr(0, pos);
|
||||
request.erase(0, pos + 2);
|
||||
size_t pos = data.find("\r\n");
|
||||
string request_line = data.substr(0, pos);
|
||||
data.erase(0, pos + 2);
|
||||
|
||||
pos = request_line.find(" ");
|
||||
string verb = request_line.substr(0, pos);
|
||||
@ -61,10 +54,10 @@ HttpParser* HttpParser::parse(string request)
|
||||
|
||||
map<string, string> headers;
|
||||
|
||||
while ((pos = request.find("\r\n")) != string::npos)
|
||||
while ((pos = data.find("\r\n")) != string::npos)
|
||||
{
|
||||
string header_line = request.substr(0, pos);
|
||||
request.erase(0, pos + 2);
|
||||
string header_line = data.substr(0, pos);
|
||||
data.erase(0, pos + 2);
|
||||
|
||||
if (header_line.length() == 0)
|
||||
{
|
||||
@ -76,37 +69,34 @@ HttpParser* HttpParser::parse(string request)
|
||||
{
|
||||
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;
|
||||
headers[key] = mxs::trim(header_line);
|
||||
}
|
||||
}
|
||||
|
||||
HttpParser* parser = NULL;
|
||||
/** The headers are now processed and consumed. The message body is
|
||||
* the only thing left in the request string. */
|
||||
|
||||
HttpRequest* request = 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;
|
||||
request = new HttpRequest();
|
||||
request->m_verb = verb_value;
|
||||
request->m_resource = uri;
|
||||
request->m_headers = headers;
|
||||
request->m_body = data;
|
||||
}
|
||||
|
||||
return parser;
|
||||
return request;
|
||||
}
|
||||
|
||||
HttpParser::HttpParser()
|
||||
HttpRequest::HttpRequest()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HttpParser::~HttpParser()
|
||||
HttpRequest::~HttpRequest()
|
||||
{
|
||||
|
||||
}
|
221
server/core/maxscale/http.hh
Normal file
221
server/core/maxscale/http.hh
Normal file
@ -0,0 +1,221 @@
|
||||
#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 <maxscale/debug.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
/** Supported HTTP verbs */
|
||||
enum http_verb
|
||||
{
|
||||
HTTP_UNKNOWN,
|
||||
HTTP_GET,
|
||||
HTTP_PUT,
|
||||
HTTP_POST,
|
||||
HTTP_OPTIONS,
|
||||
HTTP_PATCH
|
||||
};
|
||||
|
||||
/** Possible HTTP return codes */
|
||||
enum http_code
|
||||
{
|
||||
HTTP_200_OK,
|
||||
HTTP_201_CREATED,
|
||||
HTTP_202_ACCEPTED,
|
||||
HTTP_204_NO_CONTENT,
|
||||
HTTP_301_MOVED_PERMANENTLY,
|
||||
HTTP_302_FOUND,
|
||||
HTTP_303_SEE_OTHER,
|
||||
HTTP_304_NOT_MODIFIED,
|
||||
HTTP_307_TEMPORARY_REDIRECT,
|
||||
HTTP_308_PERMANENT_REDIRECT,
|
||||
HTTP_400_BAD_REQUEST,
|
||||
HTTP_401_UNAUTHORIZED,
|
||||
HTTP_403_FORBIDDEN,
|
||||
HTTP_404_NOT_FOUND,
|
||||
HTTP_405_METHOD_NOT_ALLOWED,
|
||||
HTTP_406_NOT_ACCEPTABLE,
|
||||
HTTP_409_CONFLICT,
|
||||
HTTP_411_LENGTH_REQUIRED,
|
||||
HTTP_412_PRECONDITION_FAILED,
|
||||
HTTP_413_PAYLOAD_TOO_LARGE,
|
||||
HTTP_414_URI_TOO_LONG,
|
||||
HTTP_415_UNSUPPORTED_MEDIA_TYPE,
|
||||
HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
HTTP_423_LOCKED,
|
||||
HTTP_428_PRECONDITION_REQUIRED,
|
||||
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE,
|
||||
HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
HTTP_501_NOT_IMPLEMENTED,
|
||||
HTTP_502_BAD_GATEWAY,
|
||||
HTTP_503_SERVICE_UNAVAILABLE,
|
||||
HTTP_504_GATEWAY_TIMEOUT,
|
||||
HTTP_505_HTTP_VERSION_NOT_SUPPORTED,
|
||||
HTTP_506_VARIANT_ALSO_NEGOTIATES,
|
||||
HTTP_507_INSUFFICIENT_STORAGE,
|
||||
HTTP_508_LOOP_DETECTED,
|
||||
HTTP_510_NOT_EXTENDED
|
||||
} ;
|
||||
|
||||
/**
|
||||
* @brief Convert string to HTTP verb
|
||||
*
|
||||
* @param verb String containing HTTP verb
|
||||
*
|
||||
* @return Enum value of the verb
|
||||
*/
|
||||
static inline 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert HTTP verb enum to string
|
||||
*
|
||||
* @param verb Enum to convert
|
||||
*
|
||||
* @return String representation of the enum
|
||||
*/
|
||||
static inline const char* http_verb_to_string(enum http_verb verb)
|
||||
{
|
||||
switch (verb)
|
||||
{
|
||||
case HTTP_GET:
|
||||
return "GET";
|
||||
case HTTP_POST:
|
||||
return "POST";
|
||||
case HTTP_PUT:
|
||||
return "PUT";
|
||||
case HTTP_PATCH:
|
||||
return "PATCH";
|
||||
case HTTP_OPTIONS:
|
||||
return "OPTIONS";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert HTTP code to string
|
||||
*
|
||||
* @param code The code to convert
|
||||
*
|
||||
* @return The HTTP/1.1 string version of the code
|
||||
*/
|
||||
static inline const char* http_code_to_string(enum http_code code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case HTTP_200_OK:
|
||||
return "200 OK";
|
||||
case HTTP_201_CREATED:
|
||||
return "201 Created";
|
||||
case HTTP_202_ACCEPTED:
|
||||
return "202 Accepted";
|
||||
case HTTP_204_NO_CONTENT:
|
||||
return "204 No Content";
|
||||
case HTTP_301_MOVED_PERMANENTLY:
|
||||
return "301 Moved Permanently";
|
||||
case HTTP_302_FOUND:
|
||||
return "302 Found";
|
||||
case HTTP_303_SEE_OTHER:
|
||||
return "303 See Other";
|
||||
case HTTP_304_NOT_MODIFIED:
|
||||
return "304 Not Modified";
|
||||
case HTTP_307_TEMPORARY_REDIRECT:
|
||||
return "307 Temporary Redirect";
|
||||
case HTTP_308_PERMANENT_REDIRECT:
|
||||
return "308 Permanent Redirect";
|
||||
case HTTP_400_BAD_REQUEST:
|
||||
return "400 Bad Request";
|
||||
case HTTP_401_UNAUTHORIZED:
|
||||
return "401 Unauthorized";
|
||||
case HTTP_403_FORBIDDEN:
|
||||
return "403 Forbidden";
|
||||
case HTTP_404_NOT_FOUND:
|
||||
return "404 Not Found";
|
||||
case HTTP_405_METHOD_NOT_ALLOWED:
|
||||
return "405 Method Not Allowed";
|
||||
case HTTP_406_NOT_ACCEPTABLE:
|
||||
return "406 Not Acceptable";
|
||||
case HTTP_409_CONFLICT:
|
||||
return "409 Conflict";
|
||||
case HTTP_411_LENGTH_REQUIRED:
|
||||
return "411 Length Required";
|
||||
case HTTP_412_PRECONDITION_FAILED:
|
||||
return "412 Precondition Failed";
|
||||
case HTTP_413_PAYLOAD_TOO_LARGE:
|
||||
return "413 Payload Too Large";
|
||||
case HTTP_414_URI_TOO_LONG:
|
||||
return "414 URI Too Long";
|
||||
case HTTP_415_UNSUPPORTED_MEDIA_TYPE:
|
||||
return "415 Unsupported Media Type";
|
||||
case HTTP_422_UNPROCESSABLE_ENTITY:
|
||||
return "422 Unprocessable Entity";
|
||||
case HTTP_423_LOCKED:
|
||||
return "423 Locked";
|
||||
case HTTP_428_PRECONDITION_REQUIRED:
|
||||
return "428 Precondition Required";
|
||||
case HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE:
|
||||
return "431 Request Header Fields Too Large";
|
||||
case HTTP_500_INTERNAL_SERVER_ERROR:
|
||||
return "500 Internal Server Error";
|
||||
case HTTP_501_NOT_IMPLEMENTED:
|
||||
return "501 Not Implemented";
|
||||
case HTTP_502_BAD_GATEWAY:
|
||||
return "502 Bad Gateway";
|
||||
case HTTP_503_SERVICE_UNAVAILABLE:
|
||||
return "503 Service Unavailable";
|
||||
case HTTP_504_GATEWAY_TIMEOUT:
|
||||
return "504 Gateway Timeout";
|
||||
case HTTP_505_HTTP_VERSION_NOT_SUPPORTED:
|
||||
return "505 HTTP Version Not Supported";
|
||||
case HTTP_506_VARIANT_ALSO_NEGOTIATES:
|
||||
return "506 Variant Also Negotiates";
|
||||
case HTTP_507_INSUFFICIENT_STORAGE:
|
||||
return "507 Insufficient Storage";
|
||||
case HTTP_508_LOOP_DETECTED:
|
||||
return "508 Loop Detected";
|
||||
case HTTP_510_NOT_EXTENDED:
|
||||
return "510 Not Extended";
|
||||
default:
|
||||
ss_dassert(false);
|
||||
return "500 Internal Server Error";
|
||||
}
|
||||
}
|
@ -18,26 +18,18 @@
|
||||
#include <map>
|
||||
#include <tr1/memory>
|
||||
|
||||
#include "http.hh"
|
||||
|
||||
using std::shared_ptr;
|
||||
using std::string;
|
||||
using std::map;
|
||||
|
||||
class HttpParser;
|
||||
class HttpRequest;
|
||||
|
||||
/** Typedef for managed pointer */
|
||||
typedef std::shared_ptr<HttpParser> SHttpParser;
|
||||
typedef std::shared_ptr<HttpRequest> SHttpRequest;
|
||||
|
||||
enum http_verb
|
||||
{
|
||||
HTTP_UNKNOWN,
|
||||
HTTP_GET,
|
||||
HTTP_PUT,
|
||||
HTTP_POST,
|
||||
HTTP_OPTIONS,
|
||||
HTTP_PATCH
|
||||
};
|
||||
|
||||
class HttpParser
|
||||
class HttpRequest
|
||||
{
|
||||
public:
|
||||
/**
|
||||
@ -47,9 +39,9 @@ public:
|
||||
*
|
||||
* @return Parsed statement or NULL if request is not valid
|
||||
*/
|
||||
static HttpParser* parse(string request);
|
||||
static HttpRequest* parse(string request);
|
||||
|
||||
~HttpParser();
|
||||
~HttpRequest();
|
||||
|
||||
/**
|
||||
* @brief Return request verb type
|
||||
@ -113,11 +105,6 @@ public:
|
||||
return m_body;
|
||||
}
|
||||
|
||||
void set_body(string body)
|
||||
{
|
||||
m_body = body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get request resource
|
||||
*
|
||||
@ -129,9 +116,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
HttpParser();
|
||||
HttpParser(const HttpParser&);
|
||||
HttpParser& operator = (const HttpParser&);
|
||||
HttpRequest();
|
||||
HttpRequest(const HttpRequest&);
|
||||
HttpRequest& operator = (const HttpRequest&);
|
||||
|
||||
map<string, string> m_headers;
|
||||
string m_body;
|
@ -11,7 +11,7 @@
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#include "../maxscale/httpparser.hh"
|
||||
#include "../maxscale/httprequest.hh"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@ -88,7 +88,7 @@ int test_basic()
|
||||
{
|
||||
stringstream ss;
|
||||
ss << verbs_pass[i] << " " << paths_pass[j] << " " << proto_pass[k] << "\r\n\r\n";
|
||||
SHttpParser parser(HttpParser::parse(ss.str()));
|
||||
SHttpRequest parser(HttpRequest::parse(ss.str()));
|
||||
TEST(parser.get() != NULL, "Valid HTTP request should be parsed: %s", ss.str().c_str());
|
||||
TEST(parser->get_resource() == string(paths_pass[j]),
|
||||
"The request path '%s' should be correct: %s",
|
||||
@ -106,7 +106,7 @@ int test_basic()
|
||||
{
|
||||
stringstream ss;
|
||||
ss << verbs_fail[i] << " " << paths_fail[j] << " " << proto_fail[k] << "\r\n\r\n";
|
||||
SHttpParser parser(HttpParser::parse(ss.str()));
|
||||
SHttpRequest parser(HttpRequest::parse(ss.str()));
|
||||
TEST(parser.get() == NULL, "Invalid HTTP request should not be parsed: %s", ss.str().c_str());
|
||||
}
|
||||
}
|
||||
@ -148,8 +148,8 @@ int test_headers()
|
||||
{
|
||||
stringstream ss;
|
||||
ss << "GET / HTTP/1.1\r\n" << headers_pass[i].key << ": "
|
||||
<< headers_pass[i].value <<"\r\n\r\n";
|
||||
SHttpParser parser(HttpParser::parse(ss.str()));
|
||||
<< headers_pass[i].value << "\r\n\r\n";
|
||||
SHttpRequest parser(HttpRequest::parse(ss.str()));
|
||||
TEST(parser.get() != NULL, "Valid HTTP request should be parsed: %s", ss.str().c_str());
|
||||
TEST(parser->get_header(headers_pass[i].key).length() > 0, "Header should be found");
|
||||
TEST(parser->get_header(headers_pass[i].key) == string(headers_pass[i].value),
|
||||
|
Reference in New Issue
Block a user