| // Copyright 2018 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "net-base/http_url.h" | |
| #include <compare> | |
| #include <optional> | |
| #include <string> | |
| #include <string_view> | |
| #include <vector> | |
| #include <base/strings/string_number_conversions.h> | |
| #include <base/strings/string_split.h> | |
| #include <base/strings/string_util.h> | |
| namespace net_base { | |
| namespace { | |
| constexpr char kDelimiters[] = " /#?"; | |
| constexpr char kPortSeparator = ':'; | |
| constexpr char kPrefixHttp[] = "http://"; | |
| constexpr char kPrefixHttps[] = "https://"; | |
| } // namespace | |
| const int HttpUrl::kDefaultHttpPort = 80; | |
| const int HttpUrl::kDefaultHttpsPort = 443; | |
| HttpUrl::HttpUrl() : port_(kDefaultHttpPort), protocol_(Protocol::kHttp) {} | |
| HttpUrl::~HttpUrl() = default; | |
| // static | |
| std::optional<HttpUrl> HttpUrl::CreateFromString(std::string_view url_string) { | |
| HttpUrl url; | |
| if (!url.ParseFromString(url_string)) { | |
| return std::nullopt; | |
| } | |
| return url; | |
| } | |
| bool HttpUrl::ParseFromString(std::string_view url_string) { | |
| Protocol protocol = Protocol::kUnknown; | |
| size_t host_start = 0; | |
| int port = 0; | |
| const std::string http_url_prefix(kPrefixHttp); | |
| const std::string https_url_prefix(kPrefixHttps); | |
| if (url_string.substr(0, http_url_prefix.length()) == http_url_prefix) { | |
| host_start = http_url_prefix.length(); | |
| port = kDefaultHttpPort; | |
| protocol = Protocol::kHttp; | |
| } else if (url_string.substr(0, https_url_prefix.length()) == | |
| https_url_prefix) { | |
| host_start = https_url_prefix.length(); | |
| port = kDefaultHttpsPort; | |
| protocol = Protocol::kHttps; | |
| } else { | |
| return false; | |
| } | |
| size_t host_end = url_string.find_first_of(kDelimiters, host_start); | |
| if (host_end == std::string::npos) { | |
| host_end = url_string.length(); | |
| } | |
| const auto host_parts = base::SplitString( | |
| url_string.substr(host_start, host_end - host_start), | |
| std::string{kPortSeparator}, base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); | |
| if (host_parts.empty() || host_parts[0].empty() || host_parts.size() > 2) { | |
| return false; | |
| } | |
| if (host_parts.size() == 2) { | |
| if (!base::StringToInt(host_parts[1], &port)) { | |
| return false; | |
| } | |
| } | |
| protocol_ = protocol; | |
| host_ = host_parts[0]; | |
| port_ = port; | |
| path_ = url_string.substr(host_end); | |
| if (path_.empty() || path_[0] != '/') { | |
| path_ = "/" + path_; | |
| } | |
| return true; | |
| } | |
| std::string HttpUrl::ToString() const { | |
| int port = port_; | |
| std::string url_string; | |
| switch (protocol_) { | |
| case Protocol::kUnknown: | |
| return "<invalid>"; | |
| case Protocol::kHttp: | |
| url_string = kPrefixHttp; | |
| if (port == kDefaultHttpPort) { | |
| port = 0; | |
| } | |
| break; | |
| case Protocol::kHttps: | |
| url_string = kPrefixHttps; | |
| if (port == kDefaultHttpsPort) { | |
| port = 0; | |
| } | |
| break; | |
| } | |
| url_string += host_; | |
| if (port != 0) { | |
| url_string += ":" + std::to_string(port); | |
| } | |
| if (path_ != "/") { | |
| if (base::StartsWith(path_, "/?")) { | |
| url_string += path_.substr(1); | |
| } else { | |
| url_string += path_; | |
| } | |
| } | |
| return url_string; | |
| } | |
| bool HttpUrl::operator==(const HttpUrl& rhs) const = default; | |
| bool HttpUrl::operator!=(const HttpUrl& rhs) const = default; | |
| } // namespace net_base |