From c557e1d10b7befbc1c8b8d9b0a7e157f9b5eb37c Mon Sep 17 00:00:00 2001
From: Glyn Matthews <glyn.matthews@gmail.com>
Date: Tue, 30 Jun 2020 18:42:55 +0200
Subject: [PATCH 1/3] Check for protocol (scheme + ':') before getting the
 default port

---
 .clang-format                    | 2 ++
 include/skyr/v1/core/schemes.hpp | 3 +++
 include/skyr/v1/url.hpp          | 5 +++++
 tests/url/url_tests.cpp          | 6 ++++++
 4 files changed, 16 insertions(+)

diff --git a/.clang-format b/.clang-format
index ab938026..7a548466 100644
--- a/.clang-format
+++ b/.clang-format
@@ -32,3 +32,5 @@ BreakBeforeBraces: Attach
 SpacesInParentheses: false
 SpaceInEmptyParentheses: false
 SpacesInCStyleCastParentheses: false
+SortIncludes: false
+SortUsingDeclarations: false
diff --git a/include/skyr/v1/core/schemes.hpp b/include/skyr/v1/core/schemes.hpp
index 85745620..ab3ad118 100644
--- a/include/skyr/v1/core/schemes.hpp
+++ b/include/skyr/v1/core/schemes.hpp
@@ -46,6 +46,9 @@ inline auto is_special(std::string_view scheme) noexcept {
 /// \param scheme
 /// \returns
 inline auto default_port(std::string_view scheme) noexcept {
+  if (scheme.back() == ':') {
+    scheme.remove_suffix(1);
+  }
   auto it = std::lower_bound(cbegin(details::schemes), cend(details::schemes), scheme, details::scheme_less);
   return ((it != end(details::schemes)) && !(scheme < it->first)) ? it->second : std::nullopt;
 }
diff --git a/include/skyr/v1/url.hpp b/include/skyr/v1/url.hpp
index 8c52f22a..154c2f15 100644
--- a/include/skyr/v1/url.hpp
+++ b/include/skyr/v1/url.hpp
@@ -219,6 +219,11 @@ class url {
   /// \returns The [URL origin](https://url.spec.whatwg.org/#origin)
   [[nodiscard]] auto origin() const -> string_type;
 
+  /// The URL scheme
+  [[nodiscard]] auto scheme() const -> string_type {
+    return url_.scheme;
+  }
+
   /// The URL scheme + `":"`
   ///
   /// \returns The [URL protocol](https://url.spec.whatwg.org/#dom-url-protocol)
diff --git a/tests/url/url_tests.cpp b/tests/url/url_tests.cpp
index 25a5724e..38522df2 100644
--- a/tests/url/url_tests.cpp
+++ b/tests/url/url_tests.cpp
@@ -440,6 +440,12 @@ TEST_CASE("url_tests", "[url]") {
     CHECK_FALSE(port);
   }
 
+  SECTION("http_default_port_is_80_using_protocol") {
+    auto port = skyr::url::default_port("http:");
+    REQUIRE(port);
+    CHECK(80 == port.value());
+  }
+
   SECTION("about_blank") {
     auto instance = skyr::url("about:blank");
     CHECK("about:" == instance.protocol());

From 05323b16749162ddce4476cfbe63278888f2ad0d Mon Sep 17 00:00:00 2001
From: Glyn Matthews <glyn.matthews@gmail.com>
Date: Tue, 30 Jun 2020 19:01:36 +0200
Subject: [PATCH 2/3] constexpr on is_special and default_port

---
 include/skyr/v1/core/schemes.hpp | 31 +++++++++++++++++++------------
 1 file changed, 19 insertions(+), 12 deletions(-)

diff --git a/include/skyr/v1/core/schemes.hpp b/include/skyr/v1/core/schemes.hpp
index ab3ad118..d8f3705a 100644
--- a/include/skyr/v1/core/schemes.hpp
+++ b/include/skyr/v1/core/schemes.hpp
@@ -28,29 +28,36 @@ constexpr static auto schemes = default_port_list{{
                                                {"ws"sv, static_cast<std::uint16_t>(80)},
                                                {"wss"sv, static_cast<std::uint16_t>(443)},
                                            }};
-
-constexpr static auto scheme_less(
-    const default_port_list::value_type &special_scheme,
-    std::string_view scheme) {
-  return special_scheme.first < scheme;
-};
 }  // namespace details
 
 /// \param scheme
 /// \returns
-inline auto is_special(std::string_view scheme) noexcept {
-  auto it = std::lower_bound(cbegin(details::schemes), cend(details::schemes), scheme, details::scheme_less);
-  return ((it != end(details::schemes)) && !(scheme < it->first));
+constexpr inline auto is_special(std::string_view scheme) noexcept {
+  constexpr auto less = [] (const auto &special_scheme, auto scheme) {
+    return special_scheme.first < scheme;
+  };
+
+  if (scheme.back() == ':') {
+    scheme.remove_suffix(1);
+  }
+  auto first = std::cbegin(details::schemes), last = std::cend(details::schemes);
+  auto it = std::lower_bound(first, last, scheme, less);
+  return ((it != last) && !(scheme < it->first));
 }
 
 /// \param scheme
 /// \returns
-inline auto default_port(std::string_view scheme) noexcept {
+constexpr inline auto default_port(std::string_view scheme) noexcept {
+  constexpr auto less = [] (const auto &special_scheme, auto scheme) {
+    return special_scheme.first < scheme;
+  };
+
   if (scheme.back() == ':') {
     scheme.remove_suffix(1);
   }
-  auto it = std::lower_bound(cbegin(details::schemes), cend(details::schemes), scheme, details::scheme_less);
-  return ((it != end(details::schemes)) && !(scheme < it->first)) ? it->second : std::nullopt;
+  auto first = std::cbegin(details::schemes), last = std::cend(details::schemes);
+  auto it = std::lower_bound(first, last, scheme, less);
+  return ((it != last) && !(scheme < it->first)) ? it->second : std::nullopt;
 }
 }  // namespace v1
 }  // namespace skyr

From 0f022d815cdab039d8284de3662cf1483f1f889b Mon Sep 17 00:00:00 2001
From: Glyn Matthews <glyn.matthews@gmail.com>
Date: Tue, 30 Jun 2020 19:05:59 +0200
Subject: [PATCH 3/3] Not every compiler likes constexpr with std::lower_bound

---
 include/skyr/v1/core/schemes.hpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/include/skyr/v1/core/schemes.hpp b/include/skyr/v1/core/schemes.hpp
index d8f3705a..ca1fb983 100644
--- a/include/skyr/v1/core/schemes.hpp
+++ b/include/skyr/v1/core/schemes.hpp
@@ -32,7 +32,7 @@ constexpr static auto schemes = default_port_list{{
 
 /// \param scheme
 /// \returns
-constexpr inline auto is_special(std::string_view scheme) noexcept {
+inline auto is_special(std::string_view scheme) noexcept {
   constexpr auto less = [] (const auto &special_scheme, auto scheme) {
     return special_scheme.first < scheme;
   };
@@ -47,7 +47,7 @@ constexpr inline auto is_special(std::string_view scheme) noexcept {
 
 /// \param scheme
 /// \returns
-constexpr inline auto default_port(std::string_view scheme) noexcept {
+inline auto default_port(std::string_view scheme) noexcept {
   constexpr auto less = [] (const auto &special_scheme, auto scheme) {
     return special_scheme.first < scheme;
   };
