|
|
|
@ -16,138 +16,171 @@ |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
// TODO(hork): rewrite with googletest
|
|
|
|
|
|
|
|
|
|
#include "src/core/lib/uri/uri_parser.h" |
|
|
|
|
|
|
|
|
|
#include <string.h> |
|
|
|
|
#include "absl/strings/str_join.h" |
|
|
|
|
#include "absl/strings/str_split.h" |
|
|
|
|
|
|
|
|
|
#include <grpc/grpc.h> |
|
|
|
|
#include <grpc/support/log.h> |
|
|
|
|
|
|
|
|
|
#include "src/core/lib/iomgr/exec_ctx.h" |
|
|
|
|
#include "test/core/util/test_config.h" |
|
|
|
|
|
|
|
|
|
static void test_succeeds(const char* uri_text, const char* scheme, |
|
|
|
|
const char* authority, const char* path, |
|
|
|
|
const char* query, const char* fragment) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
grpc_uri* uri = grpc_uri_parse(uri_text, false); |
|
|
|
|
GPR_ASSERT(uri); |
|
|
|
|
GPR_ASSERT(0 == strcmp(scheme, uri->scheme)); |
|
|
|
|
GPR_ASSERT(0 == strcmp(authority, uri->authority)); |
|
|
|
|
GPR_ASSERT(0 == strcmp(path, uri->path)); |
|
|
|
|
GPR_ASSERT(0 == strcmp(query, uri->query)); |
|
|
|
|
GPR_ASSERT(0 == strcmp(fragment, uri->fragment)); |
|
|
|
|
|
|
|
|
|
grpc_uri_destroy(uri); |
|
|
|
|
static void test_succeeds( |
|
|
|
|
absl::string_view uri_text, absl::string_view scheme, |
|
|
|
|
absl::string_view authority, absl::string_view path, |
|
|
|
|
const std::map<std::string, std::string>& query_param_map, |
|
|
|
|
const std::vector<grpc_core::URI::QueryParam> query_param_pairs, |
|
|
|
|
absl::string_view fragment) { |
|
|
|
|
absl::StatusOr<grpc_core::URI> uri = grpc_core::URI::Parse(uri_text); |
|
|
|
|
GPR_ASSERT(uri.ok()); |
|
|
|
|
GPR_ASSERT(scheme == uri->scheme()); |
|
|
|
|
GPR_ASSERT(authority == uri->authority()); |
|
|
|
|
GPR_ASSERT(path == uri->path()); |
|
|
|
|
// query param map
|
|
|
|
|
GPR_ASSERT(uri->query_parameter_map().size() == query_param_map.size()); |
|
|
|
|
for (const auto& expected_kv : query_param_map) { |
|
|
|
|
const auto it = uri->query_parameter_map().find(expected_kv.first); |
|
|
|
|
GPR_ASSERT(it != uri->query_parameter_map().end()); |
|
|
|
|
GPR_ASSERT(it->second == expected_kv.second); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_fails(const char* uri_text) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
GPR_ASSERT(nullptr == grpc_uri_parse(uri_text, 0)); |
|
|
|
|
// query param pairs
|
|
|
|
|
GPR_ASSERT(uri->query_parameter_pairs() == query_param_pairs); |
|
|
|
|
GPR_ASSERT(fragment == uri->fragment()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_query_parts() { |
|
|
|
|
{ |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
const char* uri_text = "http://foo/path?a&b=B&c=&#frag"; |
|
|
|
|
grpc_uri* uri = grpc_uri_parse(uri_text, false); |
|
|
|
|
GPR_ASSERT(uri); |
|
|
|
|
|
|
|
|
|
GPR_ASSERT(0 == strcmp("http", uri->scheme)); |
|
|
|
|
GPR_ASSERT(0 == strcmp("foo", uri->authority)); |
|
|
|
|
GPR_ASSERT(0 == strcmp("/path", uri->path)); |
|
|
|
|
GPR_ASSERT(0 == strcmp("a&b=B&c=&", uri->query)); |
|
|
|
|
GPR_ASSERT(4 == uri->num_query_parts); |
|
|
|
|
|
|
|
|
|
GPR_ASSERT(0 == strcmp("a", uri->query_parts[0])); |
|
|
|
|
GPR_ASSERT(nullptr == uri->query_parts_values[0]); |
|
|
|
|
|
|
|
|
|
GPR_ASSERT(0 == strcmp("b", uri->query_parts[1])); |
|
|
|
|
GPR_ASSERT(0 == strcmp("B", uri->query_parts_values[1])); |
|
|
|
|
|
|
|
|
|
GPR_ASSERT(0 == strcmp("c", uri->query_parts[2])); |
|
|
|
|
GPR_ASSERT(0 == strcmp("", uri->query_parts_values[2])); |
|
|
|
|
|
|
|
|
|
GPR_ASSERT(0 == strcmp("", uri->query_parts[3])); |
|
|
|
|
GPR_ASSERT(nullptr == uri->query_parts_values[3]); |
|
|
|
|
|
|
|
|
|
GPR_ASSERT(nullptr == grpc_uri_get_query_arg(uri, "a")); |
|
|
|
|
GPR_ASSERT(0 == strcmp("B", grpc_uri_get_query_arg(uri, "b"))); |
|
|
|
|
GPR_ASSERT(0 == strcmp("", grpc_uri_get_query_arg(uri, "c"))); |
|
|
|
|
GPR_ASSERT(nullptr == grpc_uri_get_query_arg(uri, "")); |
|
|
|
|
|
|
|
|
|
GPR_ASSERT(0 == strcmp("frag", uri->fragment)); |
|
|
|
|
|
|
|
|
|
grpc_uri_destroy(uri); |
|
|
|
|
static void test_fails(absl::string_view uri_text) { |
|
|
|
|
absl::StatusOr<grpc_core::URI> uri = grpc_core::URI::Parse(uri_text); |
|
|
|
|
GPR_ASSERT(!uri.ok()); |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
/* test the current behavior of multiple query part values */ |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
const char* uri_text = "http://auth/path?foo=bar=baz&foobar=="; |
|
|
|
|
grpc_uri* uri = grpc_uri_parse(uri_text, false); |
|
|
|
|
GPR_ASSERT(uri); |
|
|
|
|
|
|
|
|
|
GPR_ASSERT(0 == strcmp("http", uri->scheme)); |
|
|
|
|
GPR_ASSERT(0 == strcmp("auth", uri->authority)); |
|
|
|
|
GPR_ASSERT(0 == strcmp("/path", uri->path)); |
|
|
|
|
GPR_ASSERT(0 == strcmp("foo=bar=baz&foobar==", uri->query)); |
|
|
|
|
GPR_ASSERT(2 == uri->num_query_parts); |
|
|
|
|
|
|
|
|
|
GPR_ASSERT(0 == strcmp("bar", grpc_uri_get_query_arg(uri, "foo"))); |
|
|
|
|
GPR_ASSERT(0 == strcmp("", grpc_uri_get_query_arg(uri, "foobar"))); |
|
|
|
|
static void test_query_param_map() { |
|
|
|
|
test_succeeds("http://localhost:8080/whatzit?mi_casa=su_casa", "http", |
|
|
|
|
"localhost:8080", "/whatzit", {{"mi_casa", "su_casa"}}, |
|
|
|
|
{{"mi_casa", "su_casa"}}, ""); |
|
|
|
|
test_succeeds("http://localhost:8080/whatzit?1=2#buckle/my/shoe", "http", |
|
|
|
|
"localhost:8080", "/whatzit", {{"1", "2"}}, {{"1", "2"}}, |
|
|
|
|
"buckle/my/shoe"); |
|
|
|
|
test_succeeds( |
|
|
|
|
"http://localhost:8080/?too=many=equals&are=present=here#fragged", "http", |
|
|
|
|
"localhost:8080", "/", {{"are", "present=here"}, {"too", "many=equals"}}, |
|
|
|
|
{{"too", "many=equals"}, {"are", "present=here"}}, "fragged"); |
|
|
|
|
test_succeeds("http://foo/path?a&b=B&c=&#frag", "http", "foo", "/path", |
|
|
|
|
{{"c", ""}, {"a", ""}, {"b", "B"}}, |
|
|
|
|
{{"a", ""}, {"b", "B"}, {"c", ""}}, "frag"); |
|
|
|
|
test_succeeds("http://auth/path?foo=bar=baz&foobar===", "http", "auth", |
|
|
|
|
"/path", {{"foo", "bar=baz"}, {"foobar", "=="}}, |
|
|
|
|
{{"foo", "bar=baz"}, {"foobar", "=="}}, ""); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_uri_destroy(uri); |
|
|
|
|
static void test_repeated_query_param_pairs() { |
|
|
|
|
absl::StatusOr<grpc_core::URI> uri = |
|
|
|
|
grpc_core::URI::Parse("http://foo/path?a=2&a=1&a=3"); |
|
|
|
|
GPR_ASSERT(uri.ok()); |
|
|
|
|
GPR_ASSERT(uri->query_parameter_map().size() == 1); |
|
|
|
|
GPR_ASSERT(uri->query_parameter_map().find("a")->second == "3"); |
|
|
|
|
std::vector<grpc_core::URI::QueryParam> expected( |
|
|
|
|
{{"a", "2"}, {"a", "1"}, {"a", "3"}}); |
|
|
|
|
GPR_ASSERT(uri->query_parameter_pairs() == expected); |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
/* empty query */ |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
const char* uri_text = "http://foo/path"; |
|
|
|
|
grpc_uri* uri = grpc_uri_parse(uri_text, false); |
|
|
|
|
GPR_ASSERT(uri); |
|
|
|
|
|
|
|
|
|
GPR_ASSERT(0 == strcmp("http", uri->scheme)); |
|
|
|
|
GPR_ASSERT(0 == strcmp("foo", uri->authority)); |
|
|
|
|
GPR_ASSERT(0 == strcmp("/path", uri->path)); |
|
|
|
|
GPR_ASSERT(0 == strcmp("", uri->query)); |
|
|
|
|
GPR_ASSERT(0 == uri->num_query_parts); |
|
|
|
|
GPR_ASSERT(nullptr == uri->query_parts); |
|
|
|
|
GPR_ASSERT(nullptr == uri->query_parts_values); |
|
|
|
|
GPR_ASSERT(0 == strcmp("", uri->fragment)); |
|
|
|
|
static void test_query_param_validity_after_move() { |
|
|
|
|
grpc_core::URI uri_copy; |
|
|
|
|
{ |
|
|
|
|
absl::StatusOr<grpc_core::URI> uri = |
|
|
|
|
grpc_core::URI::Parse("http://foo/path?a=2&b=1&c=3"); |
|
|
|
|
GPR_ASSERT(uri.ok()); |
|
|
|
|
uri_copy = std::move(*uri); |
|
|
|
|
} |
|
|
|
|
GPR_ASSERT(uri_copy.query_parameter_map().find("a")->second == "2"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_uri_destroy(uri); |
|
|
|
|
static void test_query_param_validity_after_copy() { |
|
|
|
|
// Since the query parameter map points to objects stored in the param pair
|
|
|
|
|
// vector, this test checks that the param map pointers remain valid after
|
|
|
|
|
// a copy. Ideally {a,m}san will catch this if there's a problem.
|
|
|
|
|
// testing copy operator=:
|
|
|
|
|
grpc_core::URI uri_copy; |
|
|
|
|
{ |
|
|
|
|
absl::StatusOr<grpc_core::URI> del_uri = |
|
|
|
|
grpc_core::URI::Parse("http://foo/path?a=2&b=1&c=3"); |
|
|
|
|
GPR_ASSERT(del_uri.ok()); |
|
|
|
|
uri_copy = *del_uri; |
|
|
|
|
} |
|
|
|
|
GPR_ASSERT(uri_copy.query_parameter_map().find("a")->second == "2"); |
|
|
|
|
// testing copy constructor:
|
|
|
|
|
grpc_core::URI* del_uri2 = new grpc_core::URI(uri_copy); |
|
|
|
|
grpc_core::URI uri_copy2(*del_uri2); |
|
|
|
|
delete del_uri2; |
|
|
|
|
GPR_ASSERT(uri_copy2.query_parameter_map().find("a")->second == "2"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int main(int argc, char** argv) { |
|
|
|
|
grpc::testing::TestEnvironment env(argc, argv); |
|
|
|
|
grpc_init(); |
|
|
|
|
test_succeeds("http://www.google.com", "http", "www.google.com", "", "", ""); |
|
|
|
|
test_succeeds("dns:///foo", "dns", "", "/foo", "", ""); |
|
|
|
|
test_succeeds("http://www.google.com:90", "http", "www.google.com:90", "", "", |
|
|
|
|
test_succeeds("http://www.google.com", "http", "www.google.com", "", {}, {}, |
|
|
|
|
""); |
|
|
|
|
test_succeeds("a192.4-df:foo.coom", "a192.4-df", "", "foo.coom", "", ""); |
|
|
|
|
test_succeeds("a+b:foo.coom", "a+b", "", "foo.coom", "", ""); |
|
|
|
|
test_succeeds("dns:///foo", "dns", "", "/foo", {}, {}, ""); |
|
|
|
|
test_succeeds("http://www.google.com:90", "http", "www.google.com:90", "", {}, |
|
|
|
|
{}, ""); |
|
|
|
|
test_succeeds("a192.4-df:foo.coom", "a192.4-df", "", "foo.coom", {}, {}, ""); |
|
|
|
|
test_succeeds("a+b:foo.coom", "a+b", "", "foo.coom", {}, {}, ""); |
|
|
|
|
test_succeeds("zookeeper://127.0.0.1:2181/foo/bar", "zookeeper", |
|
|
|
|
"127.0.0.1:2181", "/foo/bar", "", ""); |
|
|
|
|
"127.0.0.1:2181", "/foo/bar", {}, {}, ""); |
|
|
|
|
test_succeeds("http://www.google.com?yay-i'm-using-queries", "http", |
|
|
|
|
"www.google.com", "", "yay-i'm-using-queries", ""); |
|
|
|
|
test_succeeds("dns:foo.com#fragment-all-the-things", "dns", "", "foo.com", "", |
|
|
|
|
"fragment-all-the-things"); |
|
|
|
|
test_succeeds("http:?legit", "http", "", "", "legit", ""); |
|
|
|
|
test_succeeds("unix:#this-is-ok-too", "unix", "", "", "", "this-is-ok-too"); |
|
|
|
|
test_succeeds("http:?legit#twice", "http", "", "", "legit", "twice"); |
|
|
|
|
test_succeeds("http://foo?bar#lol?", "http", "foo", "", "bar", "lol?"); |
|
|
|
|
test_succeeds("http://foo?bar#lol?/", "http", "foo", "", "bar", "lol?/"); |
|
|
|
|
"www.google.com", "", {{"yay-i'm-using-queries", ""}}, |
|
|
|
|
{{"yay-i'm-using-queries", ""}}, ""); |
|
|
|
|
test_succeeds("dns:foo.com#fragment-all-the-things", "dns", "", "foo.com", {}, |
|
|
|
|
{}, "fragment-all-the-things"); |
|
|
|
|
test_succeeds("http:?legit", "http", "", "", {{"legit", ""}}, {{"legit", ""}}, |
|
|
|
|
""); |
|
|
|
|
test_succeeds("unix:#this-is-ok-too", "unix", "", "", {}, {}, |
|
|
|
|
"this-is-ok-too"); |
|
|
|
|
test_succeeds("http:?legit#twice", "http", "", "", {{"legit", ""}}, |
|
|
|
|
{{"legit", ""}}, "twice"); |
|
|
|
|
test_succeeds("http://foo?bar#lol?", "http", "foo", "", {{"bar", ""}}, |
|
|
|
|
{{"bar", ""}}, "lol?"); |
|
|
|
|
test_succeeds("http://foo?bar#lol?/", "http", "foo", "", {{"bar", ""}}, |
|
|
|
|
{{"bar", ""}}, "lol?/"); |
|
|
|
|
test_succeeds("ipv6:[2001:db8::1%252]:12345", "ipv6", "", |
|
|
|
|
"[2001:db8::1%2]:12345", "", ""); |
|
|
|
|
|
|
|
|
|
"[2001:db8::1%2]:12345", {}, {}, ""); |
|
|
|
|
test_succeeds("https://www.google.com/?a=1%26b%3D2&c=3", "https", |
|
|
|
|
"www.google.com", "/", {{"c", "3"}, {"a", "1&b=2"}}, |
|
|
|
|
{{"a", "1&b=2"}, {"c", "3"}}, ""); |
|
|
|
|
// Artificial examples to show that embedded nulls are supported.
|
|
|
|
|
test_succeeds(std::string("unix-abstract:\0should-be-ok", 27), |
|
|
|
|
"unix-abstract", "", std::string("\0should-be-ok", 13), {}, {}, |
|
|
|
|
""); |
|
|
|
|
test_succeeds( |
|
|
|
|
"https://foo.com:5555/v1/" |
|
|
|
|
"token-exchange?subject_token=eyJhbGciO&subject_token_type=urn:ietf:" |
|
|
|
|
"params:oauth:token-type:id_token", |
|
|
|
|
"https", "foo.com:5555", "/v1/token-exchange", |
|
|
|
|
{{"subject_token", "eyJhbGciO"}, |
|
|
|
|
{"subject_token_type", "urn:ietf:params:oauth:token-type:id_token"}}, |
|
|
|
|
{{"subject_token", "eyJhbGciO"}, |
|
|
|
|
{"subject_token_type", "urn:ietf:params:oauth:token-type:id_token"}}, |
|
|
|
|
""); |
|
|
|
|
test_succeeds("http:?dangling-pct-%0", "http", "", "", |
|
|
|
|
{{"dangling-pct-%0", ""}}, {{"dangling-pct-%0", ""}}, ""); |
|
|
|
|
test_succeeds("unix-abstract:%00x", "unix-abstract", "", |
|
|
|
|
std::string("\0x", 2), {}, {}, ""); |
|
|
|
|
test_succeeds("x:y?%xx", "x", "", "y", {{"%xx", ""}}, {{"%xx", ""}}, ""); |
|
|
|
|
test_succeeds("scheme:path//is/ok", "scheme", "", "path//is/ok", {}, {}, ""); |
|
|
|
|
test_succeeds("fake:///", "fake", "", "/", {}, {}, ""); |
|
|
|
|
test_fails("xyz"); |
|
|
|
|
test_fails("http:?dangling-pct-%0"); |
|
|
|
|
test_fails("http://foo?[bar]"); |
|
|
|
|
test_fails("http://foo?x[bar]"); |
|
|
|
|
test_fails("http://foo?bar#lol#"); |
|
|
|
|
|
|
|
|
|
test_query_parts(); |
|
|
|
|
test_fails(""); |
|
|
|
|
test_fails(":no_scheme"); |
|
|
|
|
test_fails("0invalid_scheme:must_start/with?alpha"); |
|
|
|
|
test_query_param_map(); |
|
|
|
|
test_repeated_query_param_pairs(); |
|
|
|
|
test_query_param_validity_after_move(); |
|
|
|
|
test_query_param_validity_after_copy(); |
|
|
|
|
grpc_shutdown(); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|