MXS-1220: Split requested resource into parts
When a client requests a resource, the HttpRequest class now splits the requested resource into parts. This should help with the resource validation and navigation. Added test that checks that the resources are correctly split into the correct number of arguments and that the argument contents are correct.
This commit is contained in:
parent
4b4a87fb41
commit
e248178349
@ -50,6 +50,107 @@ static inline string& trim(string& str)
|
||||
}
|
||||
}
|
||||
|
||||
static void process_uri(string& uri, 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());
|
||||
}
|
||||
|
||||
if (my_uri.length() == 0)
|
||||
{
|
||||
/** Special handling for the / resource */
|
||||
uri_parts.push_back("");
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool process_options(string& uri, map<string, string>& options)
|
||||
{
|
||||
size_t pos = uri.find("?");
|
||||
|
||||
if (pos != string::npos)
|
||||
{
|
||||
string optionstr = uri.substr(pos + 1);
|
||||
uri.erase(pos);
|
||||
|
||||
char buf[optionstr.size() + 1];
|
||||
strcpy(buf, optionstr.c_str());
|
||||
char* saved;
|
||||
char* tok = strtok_r(buf, ",", &saved);
|
||||
|
||||
while (tok && *tok)
|
||||
{
|
||||
string opt(tok);
|
||||
pos = opt.find("=");
|
||||
|
||||
if (pos != string::npos)
|
||||
{
|
||||
string key = opt.substr(0, pos - 1);
|
||||
string value = opt.substr(pos + 1);
|
||||
options[key] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Invalid option */
|
||||
return false;
|
||||
}
|
||||
|
||||
tok = strtok_r(NULL, ",", &saved);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool process_headers(string& data, map<string, string>& headers)
|
||||
{
|
||||
size_t pos;
|
||||
|
||||
while ((pos = data.find("\r\n")) != string::npos)
|
||||
{
|
||||
string header_line = data.substr(0, pos);
|
||||
data.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);
|
||||
headers[key] = mxs::trim(header_line);
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Invalid header */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
HttpRequest* HttpRequest::parse(string data)
|
||||
{
|
||||
size_t pos = data.find("\r\n");
|
||||
@ -80,76 +181,29 @@ HttpRequest* HttpRequest::parse(string data)
|
||||
string uri = request_line.substr(0, pos);
|
||||
request_line.erase(0, pos + 1);
|
||||
|
||||
/** Process request options */
|
||||
pos = uri.find("?");
|
||||
map<string, string> options;
|
||||
|
||||
if (pos != string::npos)
|
||||
/** Process request options */
|
||||
if (!process_options(uri, options))
|
||||
{
|
||||
string optionstr = uri.substr(pos + 1);
|
||||
uri.erase(pos);
|
||||
|
||||
char buf[optionstr.size() + 1];
|
||||
strcpy(buf, optionstr.c_str());
|
||||
char* saved;
|
||||
char* tok = strtok_r(buf, ",", &saved);
|
||||
|
||||
while (tok && *tok)
|
||||
{
|
||||
string opt(tok);
|
||||
pos = opt.find("=");
|
||||
|
||||
if (pos != string::npos)
|
||||
{
|
||||
string key = opt.substr(0, pos - 1);
|
||||
string value = opt.substr(pos + 1);
|
||||
options[key] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Invalid option */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tok = strtok_r(NULL, ",", &saved);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Clean up trailing slashes in requested resource */
|
||||
while (uri.length() > 1 && *uri.rbegin() == '/')
|
||||
{
|
||||
pos = uri.find_last_of("/");
|
||||
uri.erase(pos);
|
||||
}
|
||||
/** Split the URI into separate parts */
|
||||
deque<string> uri_parts;
|
||||
process_uri(uri, uri_parts);
|
||||
|
||||
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 = data.find("\r\n")) != string::npos)
|
||||
/** Process request headers */
|
||||
if (!process_headers(data, headers))
|
||||
{
|
||||
string header_line = data.substr(0, pos);
|
||||
data.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);
|
||||
headers[key] = mxs::trim(header_line);
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Invalid header */
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -192,6 +246,7 @@ HttpRequest* HttpRequest::parse(string data)
|
||||
request->m_json.reset(body);
|
||||
request->m_json_string = data;
|
||||
request->m_resource = uri;
|
||||
request->m_resource_parts = uri_parts;
|
||||
request->m_verb = verb_value;
|
||||
}
|
||||
|
||||
|
@ -14,8 +14,9 @@
|
||||
|
||||
#include <maxscale/cppdefs.hh>
|
||||
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <tr1/memory>
|
||||
|
||||
#include <maxscale/jansson.hh>
|
||||
@ -26,6 +27,7 @@
|
||||
using std::shared_ptr;
|
||||
using std::string;
|
||||
using std::map;
|
||||
using std::deque;
|
||||
using mxs::Closer;
|
||||
|
||||
class HttpRequest;
|
||||
@ -132,22 +134,32 @@ public:
|
||||
/**
|
||||
* @brief Get request resource
|
||||
*
|
||||
* @return The request resoure
|
||||
* @return The request resource
|
||||
*/
|
||||
const string& get_resource() const
|
||||
{
|
||||
return m_resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get request resource parts
|
||||
*
|
||||
* @return The request resource split into parts
|
||||
*/
|
||||
const deque<string>& get_resource_parts() const
|
||||
{
|
||||
return m_resource_parts;
|
||||
}
|
||||
private:
|
||||
HttpRequest();
|
||||
HttpRequest(const HttpRequest&);
|
||||
HttpRequest& operator = (const HttpRequest&);
|
||||
|
||||
map<string, string> m_headers; /**< Request headers */
|
||||
map<string, string> m_options; /**< Request options */
|
||||
Closer<json_t*> m_json; /**< Request body */
|
||||
string m_json_string; /**< String version of @c m_json */
|
||||
string m_resource; /**< Requested resource */
|
||||
enum http_verb m_verb; /**< Request method */
|
||||
map<string, string> m_headers; /**< Request headers */
|
||||
map<string, string> m_options; /**< Request options */
|
||||
Closer<json_t*> m_json; /**< Request body */
|
||||
string m_json_string; /**< String version of @c m_json */
|
||||
string m_resource; /**< Requested resource */
|
||||
deque<string> m_resource_parts; /**< @c m_resource split into parts */
|
||||
enum http_verb m_verb; /**< Request method */
|
||||
};
|
||||
|
@ -385,6 +385,73 @@ int test_response()
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct
|
||||
{
|
||||
const char* input;
|
||||
const char* value;
|
||||
int offset;
|
||||
size_t size;
|
||||
} resource_parts[] =
|
||||
{
|
||||
{"/", "", 0, 1},
|
||||
{"/?a=b", "", 0, 1},
|
||||
|
||||
{"/servers/", "servers", 0, 1},
|
||||
{"/servers", "servers", 0, 1},
|
||||
{"servers", "servers", 0, 1},
|
||||
|
||||
{"/servers/my-server", "servers", 0, 2},
|
||||
{"/servers/my-server/", "servers", 0, 2},
|
||||
{"servers/my-server", "servers", 0, 2},
|
||||
{"/servers/my-server", "my-server", 1, 2},
|
||||
{"/servers/my-server/", "my-server", 1, 2},
|
||||
{"servers/my-server", "my-server", 1, 2},
|
||||
|
||||
{"/servers/my-server/user", "servers", 0, 3},
|
||||
{"/servers/my-server/user", "servers", 0, 3},
|
||||
{"servers/my-server/user", "servers", 0, 3},
|
||||
{"/servers/my-server/user", "my-server", 1, 3},
|
||||
{"/servers/my-server/user/", "my-server", 1, 3},
|
||||
{"servers/my-server/user", "my-server", 1, 3},
|
||||
{"/servers/my-server/user", "user", 2, 3},
|
||||
{"/servers/my-server/user/", "user", 2, 3},
|
||||
{"servers/my-server/user", "user", 2, 3},
|
||||
|
||||
{"/servers?a=b", "servers", 0, 1},
|
||||
{"/servers/?a=b", "servers", 0, 1},
|
||||
{"servers/?a=b", "servers", 0, 1},
|
||||
{"/servers/my-server?a=b", "my-server", 1, 2},
|
||||
{"/servers/my-server/?a=b", "my-server", 1, 2},
|
||||
{"servers/my-server/?a=b", "my-server", 1, 2},
|
||||
{"/servers/my-server/user?a=b", "user", 2, 3},
|
||||
{"/servers/my-server/user/?a=b", "user", 2, 3},
|
||||
{"servers/my-server/user?a=b", "user", 2, 3},
|
||||
|
||||
{}
|
||||
};
|
||||
|
||||
int test_resource_parts()
|
||||
{
|
||||
for (int i = 0; resource_parts[i].input; i++)
|
||||
{
|
||||
stringstream ss;
|
||||
ss << "GET " << resource_parts[i].input << " HTTP/1.1\r\n\r\n";
|
||||
SHttpRequest request(HttpRequest::parse(ss.str()));
|
||||
|
||||
TEST(request.get(), "Request should be OK: %s", ss.str().c_str());
|
||||
|
||||
TEST(request->get_resource_parts().size() == resource_parts[i].size,
|
||||
"Request should have %lu parts: %lu", resource_parts[i].size,
|
||||
request->get_resource_parts().size());
|
||||
|
||||
string value = request->get_resource_parts()[resource_parts[i].offset];
|
||||
TEST(value == resource_parts[i].value,
|
||||
"Request part at %d should be '%s': %s", resource_parts[i].offset,
|
||||
resource_parts[i].value, value.c_str());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int rc = 0;
|
||||
@ -393,6 +460,7 @@ int main(int argc, char** argv)
|
||||
rc += test_headers();
|
||||
rc += test_message_body();
|
||||
rc += test_response();
|
||||
rc += test_resource_parts();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user