aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib
diff options
context:
space:
mode:
authorHåvard Pettersen <havardpe@oath.com>2019-01-09 15:01:22 +0000
committerHåvard Pettersen <havardpe@oath.com>2019-01-09 15:01:22 +0000
commit5934b46fa964fabd9cd774f67c43fc1e879a1b0e (patch)
tree5bbfdd4c1256144055e187bd0a673d710e75e590 /vespalib
parent5251626df44e98457ea111f440d9a79cb6033075 (diff)
add support for uri dequoting and query parameter parsing
Diffstat (limited to 'vespalib')
-rw-r--r--vespalib/src/tests/portal/http_request/http_request_test.cpp43
-rw-r--r--vespalib/src/vespa/vespalib/portal/http_request.cpp82
-rw-r--r--vespalib/src/vespa/vespalib/portal/http_request.h5
3 files changed, 127 insertions, 3 deletions
diff --git a/vespalib/src/tests/portal/http_request/http_request_test.cpp b/vespalib/src/tests/portal/http_request/http_request_test.cpp
index 6e1527efa4b..d369dc274c4 100644
--- a/vespalib/src/tests/portal/http_request/http_request_test.cpp
+++ b/vespalib/src/tests/portal/http_request/http_request_test.cpp
@@ -2,6 +2,7 @@
#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/vespalib/portal/http_request.h>
+#include <vespa/vespalib/util/stringfmt.h>
using namespace vespalib;
using namespace vespalib::portal;
@@ -117,4 +118,46 @@ TEST("require that header line must contain separator") {
"missing separator\r\n"));
}
+TEST("require that uri parameters can be parsed") {
+ auto req = make_request("GET /my/path?foo=bar&baz HTTP/1.1\r\n\r\n");
+ EXPECT_EQUAL(req.get_uri(), "/my/path?foo=bar&baz");
+ EXPECT_EQUAL(req.get_path(), "/my/path");
+ EXPECT_TRUE(req.has_param("foo"));
+ EXPECT_TRUE(!req.has_param("bar"));
+ EXPECT_TRUE(req.has_param("baz"));
+ EXPECT_EQUAL(req.get_param("foo"), "bar");
+ EXPECT_EQUAL(req.get_param("bar"), "");
+ EXPECT_EQUAL(req.get_param("baz"), "");
+}
+
+TEST("require that byte values in uri segments (path, key, value) are dequoted as expected") {
+ vespalib::string str = "0123456789aBcDeF";
+ for (size_t a = 0; a < 16; ++a) {
+ for (size_t b = 0; b < 16; ++b) {
+ vespalib::string expect = " foo ";
+ expect.push_back((a * 16) + b);
+ expect.push_back((a * 16) + b);
+ expect.append(" bar ");
+ vespalib::string input = vespalib::make_string("+foo+%%%c%c%%%c%c+bar+",
+ str[a], str[b], str[a], str[b]);
+ vespalib::string uri = vespalib::make_string("%s?%s=%s&extra=yes",
+ input.c_str(), input.c_str(), input.c_str());
+ auto req = make_request(vespalib::make_string("GET %s HTTP/1.1\r\n\r\n",
+ uri.c_str()));
+ EXPECT_EQUAL(req.get_uri(), uri);
+ EXPECT_EQUAL(req.get_path(), expect);
+ EXPECT_TRUE(req.has_param(expect));
+ EXPECT_EQUAL(req.get_param(expect), expect);
+ EXPECT_TRUE(req.has_param("extra"));
+ EXPECT_EQUAL(req.get_param("extra"), "yes");
+ }
+ }
+}
+
+TEST("require that percent character becomes plain if not followed by exactly 2 hex digits") {
+ auto req = make_request("GET %/5%5:%@5%5G%`5%5g%5?% HTTP/1.1\r\n\r\n");
+ EXPECT_EQUAL(req.get_path(), "%/5%5:%@5%5G%`5%5g%5");
+ EXPECT_TRUE(req.has_param("%"));
+}
+
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vespalib/src/vespa/vespalib/portal/http_request.cpp b/vespalib/src/vespa/vespalib/portal/http_request.cpp
index d49fc2e70f4..abd690897c6 100644
--- a/vespalib/src/vespa/vespalib/portal/http_request.cpp
+++ b/vespalib/src/vespa/vespalib/portal/http_request.cpp
@@ -15,11 +15,11 @@ void strip_cr(vespalib::string &str) {
}
}
-std::vector<vespalib::string> split(vespalib::stringref str, vespalib::stringref sep) {
+std::vector<vespalib::string> split(vespalib::stringref str, char sep) {
vespalib::string token;
std::vector<vespalib::string> list;
for (char c: str) {
- if (sep.find(c) == vespalib::stringref::npos) {
+ if (c != sep) {
token.push_back(c);
} else if (!token.empty()) {
list.push_back(token);
@@ -32,6 +32,49 @@ std::vector<vespalib::string> split(vespalib::stringref str, vespalib::stringref
return list;
}
+int decode_hex_digit(char c) {
+ if ((c >= '0') && (c <= '9')) {
+ return (c - '0');
+ }
+ if ((c >= 'a') && (c <= 'f')) {
+ return ((c - 'a') + 10);
+ }
+ if ((c >= 'A') && (c <= 'F')) {
+ return ((c - 'A') + 10);
+ }
+ return -1;
+}
+
+int decode_hex_num(vespalib::stringref src, size_t idx) {
+ if (src.size() < (idx + 2)) {
+ return -1;
+ }
+ int a = decode_hex_digit(src[idx]);
+ int b = decode_hex_digit(src[idx + 1]);
+ if ((a < 0) || (b < 0)) {
+ return -1;
+ }
+ return ((a << 4) | b);
+}
+
+vespalib::string dequote(vespalib::stringref src) {
+ vespalib::string dst;
+ for (size_t idx = 0; idx < src.size(); ++idx) {
+ char c = src[idx];
+ if (c == '+') {
+ c = ' ';
+ } else if (c == '%') {
+ int x = decode_hex_num(src, idx + 1);
+ if (x >= 0) {
+ c = x;
+ idx += 2;
+ }
+ }
+ dst.push_back(c);
+ }
+ return dst;
+}
+
} // namespace vespalib::portal::<unnamed>
void
@@ -49,13 +92,30 @@ HttpRequest::set_error()
void
HttpRequest::handle_request_line(const vespalib::string &line)
{
- auto parts = split(line, " ");
+ auto parts = split(line, ' ');
if (parts.size() != 3) {
return set_error(); // malformed request line
}
_method = parts[0];
_uri = parts[1];
_version = parts[2];
+ size_t query_sep = _uri.find("?");
+ if (query_sep == vespalib::string::npos) {
+ _path = dequote(_uri);
+ } else {
+ _path = dequote(_uri.substr(0, query_sep));
+ auto query = split(_uri.substr(query_sep + 1), '&');
+ for (const auto &param: query) {
+ size_t value_sep = param.find("=");
+ if (value_sep == vespalib::string::npos) {
+ _params[dequote(param)] = "";
+ } else {
+ auto key = param.substr(0, value_sep);
+ auto value = param.substr(value_sep + 1);
+ _params[dequote(key)] = dequote(value);
+ }
+ }
+ }
}
void
@@ -163,4 +223,20 @@ HttpRequest::get_header(const vespalib::string &name) const
return pos->second;
}
+bool
+HttpRequest::has_param(const vespalib::string &name) const
+{
+ return (_params.find(name) != _params.end());
+}
+
+const vespalib::string &
+HttpRequest::get_param(const vespalib::string &name) const
+{
+ auto pos = _params.find(name);
+ if (pos == _params.end()) {
+ return _empty;
+ }
+ return pos->second;
+}
+
} // namespace vespalib::portal
diff --git a/vespalib/src/vespa/vespalib/portal/http_request.h b/vespalib/src/vespa/vespalib/portal/http_request.h
index 51c7ab08da9..db586f08ea2 100644
--- a/vespalib/src/vespa/vespalib/portal/http_request.h
+++ b/vespalib/src/vespa/vespalib/portal/http_request.h
@@ -14,6 +14,8 @@ private:
// http stuff
vespalib::string _method;
vespalib::string _uri;
+ vespalib::string _path;
+ std::map<vespalib::string, vespalib::string> _params;
vespalib::string _version;
std::map<vespalib::string, vespalib::string> _headers;
vespalib::string _host;
@@ -43,6 +45,9 @@ public:
const vespalib::string &get_header(const vespalib::string &name) const;
const vespalib::string &get_host() const { return _host; }
const vespalib::string &get_uri() const { return _uri; }
+ const vespalib::string &get_path() const { return _path; }
+ bool has_param(const vespalib::string &name) const;
+ const vespalib::string &get_param(const vespalib::string &name) const;
};
} // namespace vespalib::portal