| // 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 |