The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#) https://grpc.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

473 lines
19 KiB

//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "src/core/util/uri.h"
#include <grpc/grpc.h>
#include <utility>
#include "absl/status/status.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "test/core/test_util/test_config.h"
using ::testing::ContainerEq;
using ::testing::Contains;
using ::testing::ElementsAre;
using ::testing::Pair;
namespace grpc_core {
class URIParserTest : public testing::Test {
protected:
static void TestSucceeds(
absl::string_view uri_text, absl::string_view scheme,
absl::string_view authority, absl::string_view path,
const std::map<absl::string_view, absl::string_view>& query_param_map,
const std::vector<URI::QueryParam>& query_param_pairs,
absl::string_view fragment) {
absl::StatusOr<URI> uri = URI::Parse(uri_text);
ASSERT_TRUE(uri.ok()) << uri.status().ToString();
EXPECT_EQ(scheme, uri->scheme());
EXPECT_EQ(authority, uri->authority());
EXPECT_EQ(path, uri->path());
EXPECT_THAT(uri->query_parameter_map(), ContainerEq(query_param_map));
EXPECT_THAT(uri->query_parameter_pairs(), ContainerEq(query_param_pairs));
EXPECT_EQ(fragment, uri->fragment());
}
static void TestFails(absl::string_view uri_text) {
absl::StatusOr<URI> uri = URI::Parse(uri_text);
ASSERT_FALSE(uri.ok());
}
};
TEST_F(URIParserTest, BasicExamplesAreParsedCorrectly) {
TestSucceeds("http://www.google.com", "http", "www.google.com", "", {}, {},
"");
TestSucceeds("dns:///foo", "dns", "", "/foo", {}, {}, "");
TestSucceeds("http://www.google.com:90", "http", "www.google.com:90", "", {},
{}, "");
TestSucceeds("a192.4-df:foo.coom", "a192.4-df", "", "foo.coom", {}, {}, "");
TestSucceeds("a+b:foo.coom", "a+b", "", "foo.coom", {}, {}, "");
TestSucceeds("zookeeper://127.0.0.1:2181/foo/bar", "zookeeper",
"127.0.0.1:2181", "/foo/bar", {}, {}, "");
TestSucceeds("dns:foo.com#fragment-all-the-things", "dns", "", "foo.com", {},
{}, "fragment-all-the-things");
TestSucceeds("http://localhost:8080/whatzit?mi_casa=su_casa", "http",
"localhost:8080", "/whatzit", {{"mi_casa", "su_casa"}},
{{"mi_casa", "su_casa"}}, "");
TestSucceeds("http://localhost:8080/whatzit?1=2#buckle/my/shoe", "http",
"localhost:8080", "/whatzit", {{"1", "2"}}, {{"1", "2"}},
"buckle/my/shoe");
}
TEST_F(URIParserTest, UncommonValidExamplesAreParsedCorrectly) {
TestSucceeds("scheme:path//is/ok", "scheme", "", "path//is/ok", {}, {}, "");
TestSucceeds("http:?legit", "http", "", "", {{"legit", ""}}, {{"legit", ""}},
"");
TestSucceeds("unix:#this-is-ok-too", "unix", "", "", {}, {},
"this-is-ok-too");
TestSucceeds("http:?legit#twice", "http", "", "", {{"legit", ""}},
{{"legit", ""}}, "twice");
TestSucceeds("fake:///", "fake", "", "/", {}, {}, "");
TestSucceeds("http://local%25host:8080/whatz%25it?1%25=2%25#fragment", "http",
"local%host:8080", "/whatz%it", {{"1%", "2%"}}, {{"1%", "2%"}},
"fragment");
}
TEST_F(URIParserTest, VariousKeyValueAndNonKVQueryParamsAreParsedCorrectly) {
TestSucceeds("http://foo/path?a&b=B&c=&#frag", "http", "foo", "/path",
{{"c", ""}, {"a", ""}, {"b", "B"}},
{{"a", ""}, {"b", "B"}, {"c", ""}}, "frag");
}
TEST_F(URIParserTest, ParserTreatsFirstEqualSignAsKVDelimiterInQueryString) {
TestSucceeds(
"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");
TestSucceeds("http://auth/path?foo=bar=baz&foobar===", "http", "auth",
"/path", {{"foo", "bar=baz"}, {"foobar", "=="}},
{{"foo", "bar=baz"}, {"foobar", "=="}}, "");
}
TEST_F(URIParserTest,
RepeatedQueryParamsAreSupportedInOrderedPairsButDeduplicatedInTheMap) {
absl::StatusOr<URI> uri = URI::Parse("http://foo/path?a=2&a=1&a=3");
ASSERT_TRUE(uri.ok()) << uri.status().ToString();
// The map stores the last found value.
ASSERT_THAT(uri->query_parameter_map(), ElementsAre(Pair("a", "3")));
// Order matters for query parameter pairs
ASSERT_THAT(uri->query_parameter_pairs(),
ElementsAre(URI::QueryParam{"a", "2"}, URI::QueryParam{"a", "1"},
URI::QueryParam{"a", "3"}));
}
TEST_F(URIParserTest, QueryParamMapRemainsValiditAfterMovingTheURI) {
URI uri_copy;
{
absl::StatusOr<URI> uri = URI::Parse("http://foo/path?a=2&b=1&c=3");
ASSERT_TRUE(uri.ok()) << uri.status().ToString();
uri_copy = std::move(*uri);
}
// ASSERT_EQ(uri_copy.query_parameter_map().find("a")->second, "2");
ASSERT_THAT(uri_copy.query_parameter_map(), Contains(Pair("a", "2")));
}
TEST_F(URIParserTest, QueryParamMapRemainsValidAfterCopyingTheURI) {
// 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=:
URI uri_copy;
{
absl::StatusOr<URI> del_uri = URI::Parse("http://foo/path?a=2&b=1&c=3");
ASSERT_TRUE(del_uri.ok()) << del_uri.status().ToString();
uri_copy = *del_uri;
}
ASSERT_THAT(uri_copy.query_parameter_map(), Contains(Pair("a", "2")));
URI* del_uri2 = new URI(uri_copy);
URI uri_copy2(*del_uri2);
delete del_uri2;
ASSERT_THAT(uri_copy2.query_parameter_map(), Contains(Pair("a", "2")));
}
TEST_F(URIParserTest, AWSExternalAccountRegressionTest) {
TestSucceeds(
"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_F(URIParserTest, NonKeyValueQueryStringsWork) {
TestSucceeds("http://www.google.com?yay-i'm-using-queries", "http",
"www.google.com", "", {{"yay-i'm-using-queries", ""}},
{{"yay-i'm-using-queries", ""}}, "");
}
TEST_F(URIParserTest, IPV6StringsAreParsedCorrectly) {
TestSucceeds("ipv6:[2001:db8::1%252]:12345", "ipv6", "",
"[2001:db8::1%2]:12345", {}, {}, "");
TestSucceeds("ipv6:[fe80::90%eth1.sky1]:6010", "ipv6", "",
"[fe80::90%eth1.sky1]:6010", {}, {}, "");
}
TEST_F(URIParserTest,
PreviouslyReservedCharactersInUnrelatedURIPartsAreIgnored) {
// The '?' and '/' characters are not reserved delimiter characters in the
// fragment. See http://go/rfc/3986#section-3.5
TestSucceeds("http://foo?bar#lol?", "http", "foo", "", {{"bar", ""}},
{{"bar", ""}}, "lol?");
TestSucceeds("http://foo?bar#lol?/", "http", "foo", "", {{"bar", ""}},
{{"bar", ""}}, "lol?/");
}
TEST_F(URIParserTest, EncodedCharactersInQueryStringAreParsedCorrectly) {
TestSucceeds("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"}}, "");
}
TEST_F(URIParserTest, InvalidPercentEncodingsArePassedThrough) {
TestSucceeds("x:y?%xx", "x", "", "y", {{"%xx", ""}}, {{"%xx", ""}}, "");
TestSucceeds("http:?dangling-pct-%0", "http", "", "",
{{"dangling-pct-%0", ""}}, {{"dangling-pct-%0", ""}}, "");
}
TEST_F(URIParserTest, NullCharactersInURIStringAreSupported) {
// Artificial examples to show that embedded nulls are supported.
TestSucceeds(std::string("unix-abstract:\0should-be-ok", 27), "unix-abstract",
"", std::string("\0should-be-ok", 13), {}, {}, "");
}
TEST_F(URIParserTest, EncodedNullsInURIStringAreSupported) {
TestSucceeds("unix-abstract:%00x", "unix-abstract", "", std::string("\0x", 2),
{}, {}, "");
}
TEST_F(URIParserTest, InvalidURIsResultInFailureStatuses) {
TestFails("xyz");
TestFails("http://foo?[bar]");
TestFails("http://foo?x[bar]");
TestFails("http://foo?bar#lol#");
TestFails("");
TestFails(":no_scheme");
TestFails("0invalid_scheme:must_start/with?alpha");
}
TEST(URITest, PercentEncodePath) {
EXPECT_EQ(URI::PercentEncodePath(
// These chars are allowed.
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789"
"/:@-._~!$&'()*+,;="
// These chars will be escaped.
"\\?%#[]^"),
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789"
"/:@-._~!$&'()*+,;="
"%5C%3F%25%23%5B%5D%5E");
}
TEST(URITest, Basic) {
auto uri =
URI::Create("http", "server.example.com", "/path/to/file.html", {}, "");
ASSERT_TRUE(uri.ok()) << uri.status().ToString();
EXPECT_EQ(uri->scheme(), "http");
EXPECT_EQ(uri->authority(), "server.example.com");
EXPECT_EQ(uri->path(), "/path/to/file.html");
EXPECT_THAT(uri->query_parameter_pairs(), testing::ElementsAre());
EXPECT_THAT(uri->query_parameter_map(), testing::ElementsAre());
EXPECT_EQ(uri->fragment(), "");
EXPECT_EQ("http://server.example.com/path/to/file.html", uri->ToString());
}
TEST(URITest, NoAuthority) {
auto uri = URI::Create("http", "", "/path/to/file.html", {}, "");
ASSERT_TRUE(uri.ok()) << uri.status().ToString();
EXPECT_EQ(uri->scheme(), "http");
EXPECT_EQ(uri->authority(), "");
EXPECT_EQ(uri->path(), "/path/to/file.html");
EXPECT_THAT(uri->query_parameter_pairs(), testing::ElementsAre());
EXPECT_THAT(uri->query_parameter_map(), testing::ElementsAre());
EXPECT_EQ(uri->fragment(), "");
EXPECT_EQ("http:/path/to/file.html", uri->ToString());
}
TEST(URITest, NoAuthorityRelativePath) {
auto uri = URI::Create("http", "", "path/to/file.html", {}, "");
ASSERT_TRUE(uri.ok()) << uri.status().ToString();
EXPECT_EQ(uri->scheme(), "http");
EXPECT_EQ(uri->authority(), "");
EXPECT_EQ(uri->path(), "path/to/file.html");
EXPECT_THAT(uri->query_parameter_pairs(), testing::ElementsAre());
EXPECT_THAT(uri->query_parameter_map(), testing::ElementsAre());
EXPECT_EQ(uri->fragment(), "");
EXPECT_EQ("http:path/to/file.html", uri->ToString());
}
TEST(URITest, AuthorityRelativePath) {
auto uri =
URI::Create("http", "server.example.com", "path/to/file.html", {}, "");
ASSERT_FALSE(uri.ok());
EXPECT_EQ(uri.status().code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(uri.status().message(),
"if authority is present, path must start with a '/'");
}
TEST(URITest, QueryParams) {
auto uri = URI::Create("http", "server.example.com", "/path/to/file.html",
{{"key", "value"}, {"key2", "value2"}}, "");
ASSERT_TRUE(uri.ok()) << uri.status().ToString();
EXPECT_EQ(uri->scheme(), "http");
EXPECT_EQ(uri->authority(), "server.example.com");
EXPECT_EQ(uri->path(), "/path/to/file.html");
EXPECT_THAT(
uri->query_parameter_pairs(),
testing::ElementsAre(
testing::AllOf(testing::Field(&URI::QueryParam::key, "key"),
testing::Field(&URI::QueryParam::value, "value")),
testing::AllOf(testing::Field(&URI::QueryParam::key, "key2"),
testing::Field(&URI::QueryParam::value, "value2"))));
EXPECT_THAT(uri->query_parameter_map(),
testing::ElementsAre(testing::Pair("key", "value"),
testing::Pair("key2", "value2")));
EXPECT_EQ(uri->fragment(), "");
EXPECT_EQ("http://server.example.com/path/to/file.html?key=value&key2=value2",
uri->ToString());
}
TEST(URITest, DuplicateQueryParams) {
auto uri = URI::Create(
"http", "server.example.com", "/path/to/file.html",
{{"key", "value"}, {"key2", "value2"}, {"key", "other_value"}}, "");
ASSERT_TRUE(uri.ok()) << uri.status().ToString();
EXPECT_EQ(uri->scheme(), "http");
EXPECT_EQ(uri->authority(), "server.example.com");
EXPECT_EQ(uri->path(), "/path/to/file.html");
EXPECT_THAT(
uri->query_parameter_pairs(),
testing::ElementsAre(
testing::AllOf(testing::Field(&URI::QueryParam::key, "key"),
testing::Field(&URI::QueryParam::value, "value")),
testing::AllOf(testing::Field(&URI::QueryParam::key, "key2"),
testing::Field(&URI::QueryParam::value, "value2")),
testing::AllOf(
testing::Field(&URI::QueryParam::key, "key"),
testing::Field(&URI::QueryParam::value, "other_value"))));
EXPECT_THAT(uri->query_parameter_map(),
testing::ElementsAre(testing::Pair("key", "other_value"),
testing::Pair("key2", "value2")));
EXPECT_EQ(uri->fragment(), "");
EXPECT_EQ(
"http://server.example.com/path/to/file.html"
"?key=value&key2=value2&key=other_value",
uri->ToString());
}
TEST(URITest, Fragment) {
auto uri = URI::Create("http", "server.example.com", "/path/to/file.html", {},
"fragment");
ASSERT_TRUE(uri.ok()) << uri.status().ToString();
EXPECT_EQ(uri->scheme(), "http");
EXPECT_EQ(uri->authority(), "server.example.com");
EXPECT_EQ(uri->path(), "/path/to/file.html");
EXPECT_THAT(uri->query_parameter_pairs(), testing::ElementsAre());
EXPECT_THAT(uri->query_parameter_map(), testing::ElementsAre());
EXPECT_EQ(uri->fragment(), "fragment");
EXPECT_EQ("http://server.example.com/path/to/file.html#fragment",
uri->ToString());
}
TEST(URITest, QueryParamsAndFragment) {
auto uri = URI::Create("http", "server.example.com", "/path/to/file.html",
{{"key", "value"}, {"key2", "value2"}}, "fragment");
ASSERT_TRUE(uri.ok()) << uri.status().ToString();
EXPECT_EQ(uri->scheme(), "http");
EXPECT_EQ(uri->authority(), "server.example.com");
EXPECT_EQ(uri->path(), "/path/to/file.html");
EXPECT_THAT(
uri->query_parameter_pairs(),
testing::ElementsAre(
testing::AllOf(testing::Field(&URI::QueryParam::key, "key"),
testing::Field(&URI::QueryParam::value, "value")),
testing::AllOf(testing::Field(&URI::QueryParam::key, "key2"),
testing::Field(&URI::QueryParam::value, "value2"))));
EXPECT_THAT(uri->query_parameter_map(),
testing::ElementsAre(testing::Pair("key", "value"),
testing::Pair("key2", "value2")));
EXPECT_EQ(uri->fragment(), "fragment");
EXPECT_EQ(
"http://server.example.com/path/to/"
"file.html?key=value&key2=value2#fragment",
uri->ToString());
}
TEST(URITest, ToStringPercentEncoding) {
auto uri = URI::Create(
// Scheme allowed chars.
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-."
// Scheme escaped chars.
"%:/?#[]@!$&'()*,;=",
// Authority allowed chars.
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-.+~!$&'()*+,;=:[]@"
// Authority escaped chars.
"%/?#",
// Path allowed chars.
"/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-._~!$&'()*+,;=:@"
// Path escaped chars.
"%?#[]",
{{// Query allowed chars.
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-._~!$'()*+,;:@/?"
// Query escaped chars.
"%=&#[]",
// Query allowed chars.
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-._~!$'()*+,;:@/?"
// Query escaped chars.
"%=&#[]"}},
// Fragment allowed chars.
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-._~!$'()*+,;:@/?=&"
// Fragment escaped chars.
"%#[]");
ASSERT_TRUE(uri.ok()) << uri.status().ToString();
EXPECT_EQ(uri->scheme(),
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-."
"%:/?#[]@!$&'()*,;=");
EXPECT_EQ(uri->authority(),
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-.+~!$&'()*+,;=:[]@"
"%/?#");
EXPECT_EQ(uri->path(),
"/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-._~!$&'()*+,;=:@"
"%?#[]");
EXPECT_THAT(
uri->query_parameter_pairs(),
testing::ElementsAre(testing::AllOf(
testing::Field(
&URI::QueryParam::key,
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-._~!$'()*+,;:@/?"
"%=&#[]"),
testing::Field(
&URI::QueryParam::value,
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-._~!$'()*+,;:@/?"
"%=&#[]"))));
EXPECT_THAT(
uri->query_parameter_map(),
testing::ElementsAre(testing::Pair(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-._~!$'()*+,;:@/?"
"%=&#[]",
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-._~!$'()*+,;:@/?"
"%=&#[]")));
EXPECT_EQ(uri->fragment(),
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-._~!$'()*+,;:@/?=&"
"%#[]");
EXPECT_EQ(
// Scheme
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-."
"%25%3A%2F%3F%23%5B%5D%40%21%24%26%27%28%29%2A%2C%3B%3D"
// Authority
"://abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-.+~!$&'()*+,;=:[]@"
"%25%2F%3F%23"
// Path
"/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-._~!$&'()*+,;=:@"
"%25%3F%23%5B%5D"
// Query
"?abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-._~!$'()*+,;:@/?"
"%25%3D%26%23%5B%5D"
"=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-._~!$'()*+,;:@/?"
"%25%3D%26%23%5B%5D"
// Fragment
"#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
"-._~!$'()*+,;:@/?=&"
"%25%23%5B%5D",
uri->ToString());
}
} // namespace grpc_core
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
grpc::testing::TestEnvironment env(&argc, argv);
grpc_init();
auto result = RUN_ALL_TESTS();
grpc_shutdown();
return result;
}