diff --git a/bazel/external/googleurl.patch b/bazel/external/googleurl.patch new file mode 100644 index 0000000000000..72e3991b4ff01 --- /dev/null +++ b/bazel/external/googleurl.patch @@ -0,0 +1,119 @@ +# TODO(dio): Consider to remove this patch when we have the ability to compile the project using +# clang-cl. Tracked in https://github.com/envoyproxy/envoy/issues/11974. + +diff --git a/base/compiler_specific.h b/base/compiler_specific.h +index 2962537..6193b56 100644 +--- a/base/compiler_specific.h ++++ b/base/compiler_specific.h +@@ -7,10 +7,6 @@ + + #include "build/build_config.h" + +-#if defined(COMPILER_MSVC) && !defined(__clang__) +-#error "Only clang-cl is supported on Windows, see https://crbug.com/988071" +-#endif +- + // Annotate a variable indicating it's ok if the variable is not used. + // (Typically used to silence a compiler warning when the assignment + // is important for some other reason.) +@@ -212,7 +208,9 @@ + #endif + #endif + +-#if defined(__clang__) && __has_attribute(uninitialized) ++#if defined(__clang__) ++#if defined(__has_attribute) ++#if __has_attribute(uninitialized) + // Attribute "uninitialized" disables -ftrivial-auto-var-init=pattern for + // the specified variable. + // Library-wide alternative is +@@ -243,6 +241,8 @@ + // E.g. platform, bot, benchmark or test name in patch description or next to + // the attribute. + #define STACK_UNINITIALIZED __attribute__((uninitialized)) ++#endif ++#endif + #else + #define STACK_UNINITIALIZED + #endif +diff --git a/base/strings/BUILD b/base/strings/BUILD +index 7a06170..7c86a5f 100644 +--- a/base/strings/BUILD ++++ b/base/strings/BUILD +@@ -6,23 +6,21 @@ load("//:build_config.bzl", "build_config") + cc_library( + name = "strings", + srcs = [ +- "string16.cc", + "string_piece.cc", + "string_util.cc", + "string_util_constants.cc", + "utf_string_conversion_utils.cc", + "utf_string_conversions.cc", +- ], ++ ] + build_config.strings_srcs, + hdrs = [ + "char_traits.h", + "string16.h", + "string_piece.h", + "string_piece_forward.h", + "string_util.h", +- "string_util_posix.h", + "utf_string_conversion_utils.h", + "utf_string_conversions.h", +- ], ++ ] + build_config.strings_hdrs, + copts = build_config.default_copts, + visibility = ["//visibility:public"], + deps = [ +diff --git a/build_config.bzl b/build_config.bzl +index d5fca65..fc0d7e5 100644 +--- a/build_config/build_config.bzl ++++ b/build_config/build_config.bzl +@@ -1,8 +1,25 @@ +-_default_copts = [ +- "-std=c++14", +- "-fno-strict-aliasing", +-] ++_default_copts = select({ ++ "@envoy//bazel:windows_x86_64": [ ++ "/std:c++14", ++ ], ++ "//conditions:default": [ ++ "-std=c++14", ++ "-fno-strict-aliasing", ++ ], ++}) ++ ++_strings_srcs = select({ ++ "@envoy//bazel:windows_x86_64": [], ++ "//conditions:default": ["string16.cc"], ++}) ++ ++_strings_hdrs = select({ ++ "@envoy//bazel:windows_x86_64": ["string_util_win.h"], ++ "//conditions:default": ["string_util_posix.h"], ++}) + + build_config = struct( + default_copts = _default_copts, ++ strings_srcs = _strings_srcs, ++ strings_hdrs = _strings_hdrs, + ) +diff --git a/url/BUILD b/url/BUILD +index 0126bdc..5d1a171 100644 +--- a/url/BUILD ++++ b/url/BUILD +@@ -43,11 +43,11 @@ cc_library( + "url_util.h", + ], + copts = build_config.default_copts, +- linkopts = ["-licuuc"], + visibility = ["//visibility:public"], + deps = [ + "//base", + "//base/strings", + "//polyfills", ++ "@org_unicode_icuuc//:common", + ], + ) diff --git a/bazel/external/icuuc.BUILD b/bazel/external/icuuc.BUILD new file mode 100644 index 0000000000000..e910a64af1aa0 --- /dev/null +++ b/bazel/external/icuuc.BUILD @@ -0,0 +1,59 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") + +licenses(["notice"]) # Apache 2 + +exports_files([ + "icu4c/LICENSE", + "icu4j/main/shared/licenses/LICENSE", +]) + +icuuc_copts = [ + "-DU_STATIC_IMPLEMENTATION", + "-DU_COMMON_IMPLEMENTATION", + "-DU_HAVE_STD_ATOMICS", +] + select({ + "@envoy//bazel:apple": [ + "-Wno-shorten-64-to-32", + "-Wno-unused-variable", + ], + "@envoy//bazel:windows_x86_64": [ + "/utf-8", + "/DLOCALE_ALLOW_NEUTRAL_NAMES=0", + ], + # TODO(dio): Add "@envoy//bazel:android" when we have it. + # "@envoy//bazel:android": [ + # "-fdata-sections", + # "-DU_HAVE_NL_LANGINFO_CODESET=0", + # "-Wno-deprecated-declarations", + # ], + "//conditions:default": [], +}) + +cc_library( + name = "headers", + hdrs = glob(["icu4c/source/common/unicode/*.h"]), + includes = ["icu4c/source/common"], + visibility = ["//visibility:public"], +) + +cc_library( + name = "common", + hdrs = glob(["icu4c/source/common/unicode/*.h"]), + includes = ["icu4c/source/common"], + visibility = ["//visibility:public"], + deps = [":icuuc"], +) + +cc_library( + name = "icuuc", + srcs = glob([ + "icu4c/source/common/*.c", + "icu4c/source/common/*.cpp", + "icu4c/source/stubdata/*.cpp", + ]), + hdrs = glob(["icu4c/source/common/*.h"]), + copts = icuuc_copts, + tags = ["requires-rtti"], + visibility = ["//visibility:private"], + deps = [":headers"], +) diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index d31cbe33d2672..cb7b3bfea5d9d 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -197,6 +197,7 @@ def envoy_dependencies(skip_targets = []): _repository_impl("bazel_compdb") _repository_impl("envoy_build_tools") _repository_impl("rules_cc") + _org_unicode_icuuc() # Unconditional, since we use this only for compiler-agnostic fuzzing utils. _org_llvm_releases_compiler_rt() @@ -699,6 +700,8 @@ def _com_googlesource_quiche(): def _com_googlesource_googleurl(): _repository_impl( name = "com_googlesource_googleurl", + patches = ["@envoy//bazel/external:googleurl.patch"], + patch_args = ["-p1"], ) native.bind( name = "googleurl", @@ -858,6 +861,17 @@ filegroup( **_get_location("kafka_python_client") ) +def _org_unicode_icuuc(): + _repository_impl( + name = "org_unicode_icuuc", + build_file = "@envoy//bazel/external:icuuc.BUILD", + # TODO(dio): Consider patching udata when we need to embed some data. + ) + native.bind( + name = "icuuc", + actual = "@org_unicode_icuuc//:common", + ) + def _foreign_cc_dependencies(): _repository_impl("rules_foreign_cc") diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 3f25e85da2635..5fabd0cbcb219 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -477,4 +477,11 @@ DEPENDENCY_REPOSITORIES = dict( urls = ["https://github.com/dpkp/kafka-python/archive/2.0.0.tar.gz"], use_category = ["test"], ), + org_unicode_icuuc = dict( + strip_prefix = "icu-release-64-2", + sha256 = "524960ac99d086cdb6988d2a92fc163436fd3c6ec0a84c475c6382fbf989be05", + urls = ["https://github.com/unicode-org/icu/archive/release-64-2.tar.gz"], + use_category = ["dataplane"], + cpe = "cpe:2.3:a:icu-project:international_components_for_unicode", + ), ) diff --git a/source/common/http/BUILD b/source/common/http/BUILD index 782eb8d28f16c..76c1f202e29e5 100644 --- a/source/common/http/BUILD +++ b/source/common/http/BUILD @@ -338,7 +338,6 @@ envoy_cc_library( hdrs = ["utility.h"], external_deps = [ "abseil_optional", - "http_parser", "nghttp2", ], deps = [ @@ -428,3 +427,17 @@ envoy_cc_library( "//source/common/common:assert_lib", ], ) + +envoy_cc_library( + name = "url_utility_lib", + srcs = ["url_utility.cc"], + hdrs = ["url_utility.h"], + external_deps = [ + "googleurl", + ], + deps = [ + "//source/common/common:assert_lib", + "//source/common/common:empty_string", + "//source/common/common:utility_lib", + ], +) diff --git a/source/common/http/http1/BUILD b/source/common/http/http1/BUILD index 042870491232a..278e9adaaae58 100644 --- a/source/common/http/http1/BUILD +++ b/source/common/http/http1/BUILD @@ -49,6 +49,7 @@ envoy_cc_library( "//source/common/http:header_utility_lib", "//source/common/http:headers_lib", "//source/common/http:status_lib", + "//source/common/http:url_utility_lib", "//source/common/http:utility_lib", "//source/common/runtime:runtime_features_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", diff --git a/source/common/http/http1/codec_impl.cc b/source/common/http/http1/codec_impl.cc index d622fe69016ed..472b707445074 100644 --- a/source/common/http/http1/codec_impl.cc +++ b/source/common/http/http1/codec_impl.cc @@ -16,6 +16,7 @@ #include "common/http/header_utility.h" #include "common/http/headers.h" #include "common/http/http1/header_formatter.h" +#include "common/http/url_utility.h" #include "common/http/utility.h" #include "common/runtime/runtime_features.h" diff --git a/source/common/http/url_utility.cc b/source/common/http/url_utility.cc new file mode 100644 index 0000000000000..d2fd43015280d --- /dev/null +++ b/source/common/http/url_utility.cc @@ -0,0 +1,95 @@ +#include "common/http/url_utility.h" + +#include + +#include +#include + +#include "common/common/assert.h" +#include "common/common/empty_string.h" +#include "common/common/utility.h" + +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" + +namespace Envoy { +namespace Http { +namespace Utility { + +bool Url::initialize(absl::string_view absolute_url, bool is_connect) { + // TODO(dio): When we have access to base::StringPiece, probably we can convert absolute_url to + // that instead. + GURL parsed(std::string{absolute_url}); + if (is_connect) { + return initializeForConnect(std::move(parsed)); + } + + // TODO(dio): Check if we need to accommodate to strictly validate only http(s) AND ws(s) schemes. + // Currently, we only accept http(s). + if (!parsed.is_valid() || !parsed.SchemeIsHTTPOrHTTPS()) { + return false; + } + + scheme_ = parsed.scheme(); + + // Only non-default ports will be rendered as part of host_and_port_. For example, + // http://www.host.com:80 has port component (i.e. 80). However, since 80 is a default port for + // http scheme, host_and_port_ will be rendered as www.host.com (without port). The same case with + // https scheme (with port 443) as well. + host_and_port_ = + absl::StrCat(parsed.host(), parsed.has_port() ? ":" : EMPTY_STRING, parsed.port()); + + const int port = parsed.EffectiveIntPort(); + if (port <= 0 || port > std::numeric_limits::max()) { + return false; + } + port_ = static_cast(port); + + // RFC allows the absolute URI to not end in "/", but the absolute path form must start with "/". + path_and_query_params_ = parsed.PathForRequest(); + if (parsed.has_ref()) { + absl::StrAppend(&path_and_query_params_, "#", parsed.ref()); + } + + return true; +} + +bool Url::initializeForConnect(GURL&& url) { + // CONNECT requests can only contain "hostname:port" + // https://github.com/nodejs/http-parser/blob/d9275da4650fd1133ddc96480df32a9efe4b059b/http_parser.c#L2503-L2506. + if (!url.is_valid() || url.IsStandard()) { + return false; + } + + const auto& parsed = url.parsed_for_possibly_invalid_spec(); + // The parsed.scheme contains the URL's hostname (stored by GURL). While host and port have -1 + // as its length. + if (parsed.scheme.len <= 0 || parsed.host.len > 0 || parsed.port.len > 0) { + return false; + } + + host_and_port_ = url.possibly_invalid_spec(); + const auto& parts = StringUtil::splitToken(host_and_port_, ":", /*keep_empty_string=*/true, + /*trim_whitespace=*/false); + if (parts.size() != 2 || static_cast(parsed.scheme.len) != parts.at(0).size() || + !validPortForConnect(parts.at(1))) { + return false; + } + + return true; +} + +bool Url::validPortForConnect(absl::string_view port_string) { + int port; + const bool valid = absl::SimpleAtoi(port_string, &port); + // Only a port value in valid range (1-65535) is allowed. + if (!valid || port <= 0 || port > std::numeric_limits::max()) { + return false; + } + port_ = static_cast(port); + return true; +} + +} // namespace Utility +} // namespace Http +} // namespace Envoy diff --git a/source/common/http/url_utility.h b/source/common/http/url_utility.h new file mode 100644 index 0000000000000..fa140c6d5f12a --- /dev/null +++ b/source/common/http/url_utility.h @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include "absl/strings/string_view.h" +#include "url/gurl.h" + +namespace Envoy { +namespace Http { +namespace Utility { + +/** + * Given a fully qualified URL, splits the string_view provided into scheme, host and path with + * query parameters components. + */ +class Url { +public: + /** + * Initializes a URL object from a URL string. + * @param absolute_url URL string to be parsed. + * @param is_connect whether to parse the absolute_url as CONNECT request URL or not. + * @return bool if the initialization is successful. + */ + bool initialize(absl::string_view absolute_url, bool is_connect); + + /** + * @return absl::string_view the scheme of a URL. + */ + absl::string_view scheme() const { return scheme_; } + + /** + * @return absl::string_view the host and port part of a URL. + */ + absl::string_view hostAndPort() const { return host_and_port_; } + + /** + * @return absl::string_view the path and query params part of a URL. + */ + absl::string_view pathAndQueryParams() const { return path_and_query_params_; } + + /** + * @return uint64_t the effective port of a URL. + */ + uint64_t port() const { return port_; } + +private: + bool initializeForConnect(GURL&& url); + bool validPortForConnect(absl::string_view port_string); + + std::string scheme_; + std::string host_and_port_; + std::string path_and_query_params_; + uint16_t port_{0}; +}; + +} // namespace Utility +} // namespace Http +} // namespace Envoy diff --git a/source/common/http/utility.cc b/source/common/http/utility.cc index a48bea5d08ae5..da6076f75689a 100644 --- a/source/common/http/utility.cc +++ b/source/common/http/utility.cc @@ -1,7 +1,5 @@ #include "common/http/utility.h" -#include - #include #include #include @@ -205,43 +203,6 @@ initializeAndValidateOptions(const envoy::config::core::v3::Http2ProtocolOptions namespace Http { -static const char kDefaultPath[] = "/"; - -bool Utility::Url::initialize(absl::string_view absolute_url, bool is_connect) { - struct http_parser_url u; - http_parser_url_init(&u); - const int result = - http_parser_parse_url(absolute_url.data(), absolute_url.length(), is_connect, &u); - - if (result != 0) { - return false; - } - if ((u.field_set & (1 << UF_HOST)) != (1 << UF_HOST) && - (u.field_set & (1 << UF_SCHEMA)) != (1 << UF_SCHEMA)) { - return false; - } - scheme_ = absl::string_view(absolute_url.data() + u.field_data[UF_SCHEMA].off, - u.field_data[UF_SCHEMA].len); - - uint16_t authority_len = u.field_data[UF_HOST].len; - if ((u.field_set & (1 << UF_PORT)) == (1 << UF_PORT)) { - authority_len = authority_len + u.field_data[UF_PORT].len + 1; - } - host_and_port_ = - absl::string_view(absolute_url.data() + u.field_data[UF_HOST].off, authority_len); - - // RFC allows the absolute-uri to not end in /, but the absolute path form - // must start with - uint64_t path_len = absolute_url.length() - (u.field_data[UF_HOST].off + hostAndPort().length()); - if (path_len > 0) { - uint64_t path_beginning = u.field_data[UF_HOST].off + hostAndPort().length(); - path_and_query_params_ = absl::string_view(absolute_url.data() + path_beginning, path_len); - } else if (!is_connect) { - path_and_query_params_ = absl::string_view(kDefaultPath, 1); - } - return true; -} - void Utility::appendXff(RequestHeaderMap& headers, const Network::Address::Instance& remote_address) { if (remote_address.type() != Network::Address::Type::Ip) { diff --git a/source/common/http/utility.h b/source/common/http/utility.h index e43f8fd977a8d..69778024e8a7e 100644 --- a/source/common/http/utility.h +++ b/source/common/http/utility.h @@ -122,23 +122,6 @@ initializeAndValidateOptions(const envoy::config::core::v3::Http2ProtocolOptions namespace Http { namespace Utility { -/** - * Given a fully qualified URL, splits the string_view provided into scheme, - * host and path with query parameters components. - */ -class Url { -public: - bool initialize(absl::string_view absolute_url, bool is_connect_request); - absl::string_view scheme() { return scheme_; } - absl::string_view hostAndPort() { return host_and_port_; } - absl::string_view pathAndQueryParams() { return path_and_query_params_; } - -private: - absl::string_view scheme_; - absl::string_view host_and_port_; - absl::string_view path_and_query_params_; -}; - class PercentEncoding { public: /** diff --git a/source/common/router/BUILD b/source/common/router/BUILD index bd500d2948884..5f9e2b8cbc96f 100644 --- a/source/common/router/BUILD +++ b/source/common/router/BUILD @@ -304,6 +304,7 @@ envoy_cc_library( "//source/common/http:header_map_lib", "//source/common/http:headers_lib", "//source/common/http:message_lib", + "//source/common/http:url_utility_lib", "//source/common/http:utility_lib", "//source/common/network:application_protocol_lib", "//source/common/network:transport_socket_options_lib", diff --git a/source/common/router/router.cc b/source/common/router/router.cc index 8a373f263530c..a466856315b3e 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -26,6 +26,7 @@ #include "common/http/header_map_impl.h" #include "common/http/headers.h" #include "common/http/message_impl.h" +#include "common/http/url_utility.h" #include "common/http/utility.h" #include "common/network/application_protocol.h" #include "common/network/transport_socket_options_impl.h" diff --git a/source/extensions/filters/http/csrf/BUILD b/source/extensions/filters/http/csrf/BUILD index a9361502dd107..c82dbf9764e2a 100644 --- a/source/extensions/filters/http/csrf/BUILD +++ b/source/extensions/filters/http/csrf/BUILD @@ -22,6 +22,7 @@ envoy_cc_library( "//source/common/common:matchers_lib", "//source/common/http:header_map_lib", "//source/common/http:headers_lib", + "//source/common/http:url_utility_lib", "//source/common/http:utility_lib", "//source/extensions/filters/http:well_known_names", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", diff --git a/source/extensions/filters/http/csrf/csrf_filter.cc b/source/extensions/filters/http/csrf/csrf_filter.cc index d852a78e31a5b..eb68859368934 100644 --- a/source/extensions/filters/http/csrf/csrf_filter.cc +++ b/source/extensions/filters/http/csrf/csrf_filter.cc @@ -6,6 +6,7 @@ #include "common/common/empty_string.h" #include "common/http/header_map_impl.h" #include "common/http/headers.h" +#include "common/http/url_utility.h" #include "common/http/utility.h" #include "extensions/filters/http/well_known_names.h" @@ -39,7 +40,7 @@ bool isModifyMethod(const Http::RequestHeaderMap& headers) { absl::string_view hostAndPort(const absl::string_view header) { Http::Utility::Url absolute_url; if (!header.empty()) { - if (absolute_url.initialize(header, false)) { + if (absolute_url.initialize(header, /*is_connect=*/false)) { return absolute_url.hostAndPort(); } return header; diff --git a/source/extensions/quic_listeners/quiche/BUILD b/source/extensions/quic_listeners/quiche/BUILD index 3082bdf98ecac..d79c1a355e3af 100644 --- a/source/extensions/quic_listeners/quiche/BUILD +++ b/source/extensions/quic_listeners/quiche/BUILD @@ -265,6 +265,7 @@ envoy_cc_library( ":envoy_quic_server_connection_lib", ":envoy_quic_server_session_lib", "//include/envoy/network:listener_interface", + "//source/common/http:utility_lib", "//source/server:connection_handler_lib", "@com_googlesource_quiche//:quic_core_server_lib", "@com_googlesource_quiche//:quic_core_utils_lib", diff --git a/source/extensions/quic_listeners/quiche/platform/BUILD b/source/extensions/quic_listeners/quiche/platform/BUILD index 4ef4fbbc8d649..9c9857842e75c 100644 --- a/source/extensions/quic_listeners/quiche/platform/BUILD +++ b/source/extensions/quic_listeners/quiche/platform/BUILD @@ -188,7 +188,7 @@ envoy_cc_library( "//source/common/common:assert_lib", "//source/common/filesystem:directory_lib", "//source/common/filesystem:filesystem_lib", - "//source/common/http:utility_lib", + "//source/common/http:url_utility_lib", ], ) diff --git a/source/extensions/quic_listeners/quiche/platform/quic_hostname_utils_impl.cc b/source/extensions/quic_listeners/quiche/platform/quic_hostname_utils_impl.cc index bcbafb56639ed..7b26dac94e267 100644 --- a/source/extensions/quic_listeners/quiche/platform/quic_hostname_utils_impl.cc +++ b/source/extensions/quic_listeners/quiche/platform/quic_hostname_utils_impl.cc @@ -1,3 +1,4 @@ + // NOLINT(namespace-envoy) // This file is part of the QUICHE platform implementation, and is not to be @@ -8,7 +9,7 @@ #include -#include "common/http/utility.h" +#include "common/http/url_utility.h" #include "absl/strings/ascii.h" #include "absl/strings/str_cat.h" diff --git a/test/common/http/BUILD b/test/common/http/BUILD index e723a48abeb01..eeee6f0d6a09c 100644 --- a/test/common/http/BUILD +++ b/test/common/http/BUILD @@ -363,6 +363,7 @@ envoy_cc_test( deps = [ "//source/common/http:exception_lib", "//source/common/http:header_map_lib", + "//source/common/http:url_utility_lib", "//source/common/http:utility_lib", "//source/common/network:address_lib", "//test/mocks/http:http_mocks", diff --git a/test/common/http/utility_test.cc b/test/common/http/utility_test.cc index 8751acddb0248..4a42d7d8fd9eb 100644 --- a/test/common/http/utility_test.cc +++ b/test/common/http/utility_test.cc @@ -9,6 +9,7 @@ #include "common/common/fmt.h" #include "common/http/exception.h" #include "common/http/header_map_impl.h" +#include "common/http/url_utility.h" #include "common/http/utility.h" #include "common/network/address_impl.h" @@ -1084,87 +1085,110 @@ TEST(HttpUtility, TestRejectTeHeaderTooLong) { TEST(Url, ParsingFails) { Utility::Url url; - EXPECT_FALSE(url.initialize("", false)); - EXPECT_FALSE(url.initialize("foo", false)); - EXPECT_FALSE(url.initialize("http://", false)); - EXPECT_FALSE(url.initialize("random_scheme://host.com/path", false)); - EXPECT_FALSE(url.initialize("http://www.foo.com", true)); - EXPECT_FALSE(url.initialize("foo.com", true)); + const bool is_connect = true; + EXPECT_FALSE(url.initialize("", !is_connect)); + EXPECT_FALSE(url.initialize("foo", !is_connect)); + EXPECT_FALSE(url.initialize("http://", !is_connect)); + EXPECT_FALSE(url.initialize("random_scheme://host.com/path", !is_connect)); + // Only port value in valid range (1-65535) is allowed. + EXPECT_FALSE(url.initialize("http://host.com:65536/path", !is_connect)); + EXPECT_FALSE(url.initialize("http://host.com:0/path", !is_connect)); + EXPECT_FALSE(url.initialize("http://host.com:-1/path", !is_connect)); + EXPECT_FALSE(url.initialize("http://host.com:port/path", !is_connect)); + + // Test parsing fails for CONNECT request URLs. + EXPECT_FALSE(url.initialize("http://www.foo.com", is_connect)); + EXPECT_FALSE(url.initialize("foo.com", is_connect)); + // Only port value in valid range (1-65535) is allowed. + EXPECT_FALSE(url.initialize("foo.com:65536", is_connect)); + EXPECT_FALSE(url.initialize("foo.com:0", is_connect)); + EXPECT_FALSE(url.initialize("foo.com:-1", is_connect)); + EXPECT_FALSE(url.initialize("foo.com:port", is_connect)); } void validateUrl(absl::string_view raw_url, absl::string_view expected_scheme, - absl::string_view expected_host_port, absl::string_view expected_path) { + absl::string_view expected_host_port, absl::string_view expected_path, + uint16_t expected_port) { Utility::Url url; - ASSERT_TRUE(url.initialize(raw_url, false)) << "Failed to initialize " << raw_url; + ASSERT_TRUE(url.initialize(raw_url, /*is_connect=*/false)) << "Failed to initialize " << raw_url; EXPECT_EQ(url.scheme(), expected_scheme); EXPECT_EQ(url.hostAndPort(), expected_host_port); EXPECT_EQ(url.pathAndQueryParams(), expected_path); -} - -void validateConnectUrl(absl::string_view raw_url, absl::string_view expected_host_port) { - Utility::Url url; - ASSERT_TRUE(url.initialize(raw_url, true)) << "Failed to initialize " << raw_url; - EXPECT_TRUE(url.scheme().empty()); - EXPECT_TRUE(url.pathAndQueryParams().empty()); - EXPECT_EQ(url.hostAndPort(), expected_host_port); + EXPECT_EQ(url.port(), expected_port); } TEST(Url, ParsingTest) { - // Test url with no explicit path (with and without port) - validateUrl("http://www.host.com", "http", "www.host.com", "/"); - validateUrl("http://www.host.com:80", "http", "www.host.com:80", "/"); + // Test url with no explicit path (with and without port). + validateUrl("http://www.host.com", "http", "www.host.com", "/", 80); + validateUrl("http://www.host.com:80", "http", "www.host.com", "/", 80); // Test url with "/" path. - validateUrl("http://www.host.com:80/", "http", "www.host.com:80", "/"); - validateUrl("http://www.host.com/", "http", "www.host.com", "/"); + validateUrl("http://www.host.com:80/", "http", "www.host.com", "/", 80); + validateUrl("http://www.host.com/", "http", "www.host.com", "/", 80); // Test url with "?". - validateUrl("http://www.host.com:80/?", "http", "www.host.com:80", "/?"); - validateUrl("http://www.host.com/?", "http", "www.host.com", "/?"); + validateUrl("http://www.host.com:80/?", "http", "www.host.com", "/?", 80); + validateUrl("http://www.host.com/?", "http", "www.host.com", "/?", 80); // Test url with "?" but without slash. - validateUrl("http://www.host.com:80?", "http", "www.host.com:80", "?"); - validateUrl("http://www.host.com?", "http", "www.host.com", "?"); + validateUrl("http://www.host.com:80?", "http", "www.host.com", "/?", 80); + validateUrl("http://www.host.com?", "http", "www.host.com", "/?", 80); - // Test url with multi-character path - validateUrl("http://www.host.com:80/path", "http", "www.host.com:80", "/path"); - validateUrl("http://www.host.com/path", "http", "www.host.com", "/path"); + // Test url with multi-character path. + validateUrl("http://www.host.com:80/path", "http", "www.host.com", "/path", 80); + validateUrl("http://www.host.com/path", "http", "www.host.com", "/path", 80); - // Test url with multi-character path and ? at the end - validateUrl("http://www.host.com:80/path?", "http", "www.host.com:80", "/path?"); - validateUrl("http://www.host.com/path?", "http", "www.host.com", "/path?"); + // Test url with multi-character path and ? at the end. + validateUrl("http://www.host.com:80/path?", "http", "www.host.com", "/path?", 80); + validateUrl("http://www.host.com/path?", "http", "www.host.com", "/path?", 80); - // Test https scheme - validateUrl("https://www.host.com", "https", "www.host.com", "/"); + // Test https scheme. + validateUrl("https://www.host.com", "https", "www.host.com", "/", 443); - // Test url with query parameter - validateUrl("http://www.host.com:80/?query=param", "http", "www.host.com:80", "/?query=param"); - validateUrl("http://www.host.com/?query=param", "http", "www.host.com", "/?query=param"); + // Test url with query parameter. + validateUrl("http://www.host.com:80/?query=param", "http", "www.host.com", "/?query=param", 80); + validateUrl("http://www.host.com/?query=param", "http", "www.host.com", "/?query=param", 80); - // Test url with query parameter but without slash - validateUrl("http://www.host.com:80?query=param", "http", "www.host.com:80", "?query=param"); - validateUrl("http://www.host.com?query=param", "http", "www.host.com", "?query=param"); + // Test url with query parameter but without slash. It will be normalized. + validateUrl("http://www.host.com:80?query=param", "http", "www.host.com", "/?query=param", 80); + validateUrl("http://www.host.com?query=param", "http", "www.host.com", "/?query=param", 80); - // Test url with multi-character path and query parameter - validateUrl("http://www.host.com:80/path?query=param", "http", "www.host.com:80", - "/path?query=param"); - validateUrl("http://www.host.com/path?query=param", "http", "www.host.com", "/path?query=param"); + // Test url with multi-character path and query parameter. + validateUrl("http://www.host.com:80/path?query=param", "http", "www.host.com", + "/path?query=param", 80); + validateUrl("http://www.host.com/path?query=param", "http", "www.host.com", "/path?query=param", + 80); - // Test url with multi-character path and more than one query parameter - validateUrl("http://www.host.com:80/path?query=param&query2=param2", "http", "www.host.com:80", - "/path?query=param&query2=param2"); + // Test url with multi-character path and more than one query parameter. + validateUrl("http://www.host.com:80/path?query=param&query2=param2", "http", "www.host.com", + "/path?query=param&query2=param2", 80); validateUrl("http://www.host.com/path?query=param&query2=param2", "http", "www.host.com", - "/path?query=param&query2=param2"); + "/path?query=param&query2=param2", 80); + // Test url with multi-character path, more than one query parameter and fragment validateUrl("http://www.host.com:80/path?query=param&query2=param2#fragment", "http", - "www.host.com:80", "/path?query=param&query2=param2#fragment"); + "www.host.com", "/path?query=param&query2=param2#fragment", 80); validateUrl("http://www.host.com/path?query=param&query2=param2#fragment", "http", "www.host.com", - "/path?query=param&query2=param2#fragment"); + "/path?query=param&query2=param2#fragment", 80); + + // Test url with non-default ports. + validateUrl("https://www.host.com:8443", "https", "www.host.com:8443", "/", 8443); + validateUrl("http://www.host.com:8080", "http", "www.host.com:8080", "/", 8080); +} + +void validateConnectUrl(absl::string_view raw_url, absl::string_view expected_host_port, + uint16_t expected_port) { + Utility::Url url; + ASSERT_TRUE(url.initialize(raw_url, /*is_connect=*/true)) << "Failed to initialize " << raw_url; + EXPECT_TRUE(url.scheme().empty()); + EXPECT_TRUE(url.pathAndQueryParams().empty()); + EXPECT_EQ(url.hostAndPort(), expected_host_port); + EXPECT_EQ(url.port(), expected_port); } TEST(Url, ParsingForConnectTest) { - validateConnectUrl("host.com:443", "host.com:443"); - validateConnectUrl("host.com:80", "host.com:80"); + validateConnectUrl("host.com:443", "host.com:443", 443); + validateConnectUrl("host.com:80", "host.com:80", 80); } void validatePercentEncodingEncodeDecode(absl::string_view source, diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index 4a5aeb45e2def..4b8f9a058a249 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -344,6 +344,7 @@ WRONGPASS WRR WS WSA +WSS Welford's Wi XDS