diff options
author | Håvard Pettersen <havardpe@oath.com> | 2019-01-09 15:01:22 +0000 |
---|---|---|
committer | Håvard Pettersen <havardpe@oath.com> | 2019-01-09 15:01:22 +0000 |
commit | 5934b46fa964fabd9cd774f67c43fc1e879a1b0e (patch) | |
tree | 5bbfdd4c1256144055e187bd0a673d710e75e590 /vespalib | |
parent | 5251626df44e98457ea111f440d9a79cb6033075 (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.cpp | 43 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/portal/http_request.cpp | 82 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/portal/http_request.h | 5 |
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 ¶m: 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 |