Skip to content

Commit 38bcf25

Browse files
authored
Added accessors to test IP addresses in URL class (#52)
* Added accessors to test if the hostname is a valid IP address or domain name * Added tests for ipv4 address endianness * Added missing header * Added endianness header, needed for correct behaviour of ipv4_ and ipv6_address * Added endianness.hpp to CMakeLists.txt * Added check for hostname bounds in ipv6 address accessor * Fixed warnings and errors on CI * Added more unit tests for opaque hosts
1 parent 22ff7ec commit 38bcf25

File tree

13 files changed

+250
-26
lines changed

13 files changed

+250
-26
lines changed

CMakeLists.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,7 @@ install(TARGETS skyr-url
116116
ARCHIVE DESTINATION lib
117117
LIBRARY DESTINATION lib)
118118

119-
120-
install(EXPORT ${PROJECT_NAME}-targets
119+
install(EXPORT ${PROJECT_NAME}-targets
121120
DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME}"
122121
NAMESPACE skyr::
123122
FILE "${PROJECT_NAME}-targets.cmake")

include/skyr/url.hpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include <skyr/url/url_record.hpp>
2020
#include <skyr/url/url_error.hpp>
2121
#include <skyr/url/url_search_parameters.hpp>
22+
#include <skyr/url/ipv4_address.hpp>
23+
#include <skyr/url/ipv6_address.hpp>
2224
#include <skyr/url/details/to_bytes.hpp>
2325

2426
#if defined(SKYR_PLATFORM_MSVC)
@@ -364,6 +366,26 @@ class url {
364366
/// \returns An error on failure to parse the new URL
365367
tl::expected<void, std::error_code> set_hostname(string_view hostname);
366368

369+
/// Checks if the hostname is a valid IPv4 address
370+
[[nodiscard]] bool is_ipv4_address() const;
371+
372+
/// Returns an optional ipv4_address value if the hostname is a
373+
/// valid IPv4 address
374+
[[nodiscard]] std::optional<skyr::ipv4_address> ipv4_address() const;
375+
376+
/// Checks if the hostname is a valid IPv6 address
377+
[[nodiscard]] bool is_ipv6_address() const;
378+
379+
/// Returns an optional ipv6_address value if the hostname is a
380+
/// valid IPv6 address
381+
[[nodiscard]] std::optional<skyr::ipv6_address> ipv6_address() const;
382+
383+
/// Checks if the hostname is a valid domain name
384+
[[nodiscard]] bool is_domain() const;
385+
386+
/// Checks if the hostname is a valid domain name
387+
[[nodiscard]] bool is_opaque() const;
388+
367389
/// Returns the [URL port](https://url.spec.whatwg.org/#dom-url-port)
368390
///
369391
/// \returns The [URL port](https://url.spec.whatwg.org/#dom-url-port)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2020 Glyn Matthews.
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// (See accompanying file LICENSE_1_0.txt or copy at
4+
// http://www.boost.org/LICENSE_1_0.txt)
5+
6+
#ifndef SKYR_URL_ENDIANNESS_HPP
7+
#define SKYR_URL_ENDIANNESS_HPP
8+
9+
#include <type_traits>
10+
#include <array>
11+
12+
namespace skyr {
13+
inline namespace v1 {
14+
namespace details {
15+
inline bool is_big_endian() noexcept {
16+
const auto word = 0x0001;
17+
auto bytes = static_cast<const unsigned char *>(static_cast<const void *>(&word));
18+
return bytes[0] != 0x01;
19+
}
20+
21+
template <typename intT>
22+
inline intT swap_endianness(intT v, typename std::enable_if<std::is_integral<intT>::value>::type * = nullptr) noexcept {
23+
constexpr auto byte_count = sizeof(v);
24+
std::array<unsigned char, byte_count> bytes{};
25+
for (auto i = 0UL; i < byte_count; ++i) {
26+
bytes[i] = (v >> (i * 8));
27+
}
28+
return *static_cast<const intT *>(static_cast<const void *>(bytes.data()));
29+
}
30+
31+
inline unsigned int to_network_byte_order(unsigned int v) noexcept {
32+
return (is_big_endian()) ? v : swap_endianness(v);
33+
}
34+
35+
inline unsigned int from_network_byte_order(unsigned int v) noexcept {
36+
return (is_big_endian()) ? v : swap_endianness(v);
37+
}
38+
} // namespace details
39+
} // namespace v1
40+
} // namespace skyr
41+
42+
#endif //SKYR_URL_ENDIANNESS_HPP
Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
// Copyright 2018 Glyn Matthews.
1+
// Copyright 2018-20 Glyn Matthews.
22
// Distributed under the Boost Software License, Version 1.0.
33
// (See accompanying file LICENSE_1_0.txt or copy at
44
// http://www.boost.org/LICENSE_1_0.txt)
55

66
#ifndef SKYR_IPV4_ADDRESS_INC
77
#define SKYR_IPV4_ADDRESS_INC
88

9+
#include <array>
910
#include <string>
1011
#include <string_view>
1112
#include <system_error>
1213
#include <optional>
1314
#include <tl/expected.hpp>
15+
#include <skyr/url/details/endianness.hpp>
1416

1517
namespace skyr {
1618
inline namespace v1 {
@@ -45,12 +47,23 @@ class ipv4_address {
4547
/// Constructor
4648
/// \param address Sets the IPv4 address to `address`
4749
explicit ipv4_address(unsigned int address)
48-
: address_(address) {}
50+
: address_(details::to_network_byte_order(address)) {}
4951

5052
/// The address value
5153
/// \returns The address value
5254
[[nodiscard]] unsigned int address() const noexcept {
53-
return address_;
55+
return details::from_network_byte_order(address_);
56+
}
57+
58+
/// The address in bytes in network byte order
59+
/// \returns The address in bytes
60+
[[nodiscard]] std::array<unsigned char, 4> to_bytes() const noexcept {
61+
return {{
62+
static_cast<unsigned char>(address_ >> 24u),
63+
static_cast<unsigned char>(address_ >> 16u),
64+
static_cast<unsigned char>(address_ >> 8u),
65+
static_cast<unsigned char>(address_ >> 0u)
66+
}};
5467
}
5568

5669
/// \returns The address as a string
Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2018 Glyn Matthews.
1+
// Copyright 2018-20 Glyn Matthews.
22
// Distributed under the Boost Software License, Version 1.0.
33
// (See accompanying file LICENSE_1_0.txt or copy at
44
// http://www.boost.org/LICENSE_1_0.txt)
@@ -13,6 +13,7 @@
1313
#include <iterator>
1414
#include <system_error>
1515
#include <tl/expected.hpp>
16+
#include <skyr/url/details/endianness.hpp>
1617

1718
namespace skyr {
1819
inline namespace v1 {
@@ -42,19 +43,31 @@ class ipv6_address {
4243

4344
std::array<unsigned short, 8> address_ = {0, 0, 0, 0, 0, 0, 0, 0};
4445

45-
using repr_type = decltype(address_);
46-
4746
public:
4847

4948
/// Constructor
5049
ipv6_address() = default;
5150

5251
/// Constructor
5352
/// \param address Sets the IPv6 address to `address`
54-
explicit ipv6_address(std::array<unsigned short, 8> address)
55-
: address_(address) {}
53+
explicit ipv6_address(std::array<unsigned short, 8> address) {
54+
for (auto i = 0UL; i < address.size(); ++i) {
55+
address_[i] = details::to_network_byte_order(address[i]);
56+
}
57+
}
58+
59+
/// The address in bytes in network byte order
60+
/// \returns The address in bytes
61+
[[nodiscard]] std::array<unsigned char, 16> to_bytes() const noexcept {
62+
std::array<unsigned char, 16> bytes{};
63+
for (auto i = 0UL; i < address_.size(); ++i) {
64+
bytes[i * 2 ] = static_cast<unsigned char>(address_[i] >> 8u);
65+
bytes[i * 2 + 1] = static_cast<unsigned char>(address_[i]);
66+
}
67+
return bytes;
68+
}
5669

57-
/// \returns The IPv4 address as a string
70+
/// \returns The IPv6 address as a string
5871
[[nodiscard]] std::string to_string() const;
5972

6073
};

src/CMakeLists.txt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ set(skyr_SRCS
1616
url/url_parser_context.cpp
1717
url/url_record.cpp
1818
url/ipv4_address.cpp
19-
url/ipv4_address.hpp
2019
url/ipv6_address.cpp
21-
url/ipv6_address.hpp
2220
url/algorithms.hpp
2321
url/url_parse.cpp
2422
url/url_parse_impl.hpp
@@ -33,7 +31,6 @@ set(skyr_SRCS
3331

3432
${PROJECT_SOURCE_DIR}/include/skyr/config.hpp
3533
${PROJECT_SOURCE_DIR}/include/skyr/version.hpp
36-
${PROJECT_SOURCE_DIR}/include/skyr/url/details/to_bytes.hpp
3734
${PROJECT_SOURCE_DIR}/include/skyr/traits/string_traits.hpp
3835
${PROJECT_SOURCE_DIR}/include/skyr/unicode/errors.hpp
3936
${PROJECT_SOURCE_DIR}/include/skyr/unicode/core.hpp
@@ -48,10 +45,14 @@ set(skyr_SRCS
4845
${PROJECT_SOURCE_DIR}/include/skyr/unicode/ranges/transforms/u32_transform.hpp
4946
${PROJECT_SOURCE_DIR}/include/skyr/unicode/idna.hpp
5047
${PROJECT_SOURCE_DIR}/include/skyr/unicode/domain.hpp
48+
${PROJECT_SOURCE_DIR}/include/skyr/url/details/to_bytes.hpp
49+
${PROJECT_SOURCE_DIR}/include/skyr/url/details/endianness.hpp
5150
${PROJECT_SOURCE_DIR}/include/skyr/url/percent_encoding/errors.hpp
5251
${PROJECT_SOURCE_DIR}/include/skyr/url/percent_encoding/percent_decode_range.hpp
5352
${PROJECT_SOURCE_DIR}/include/skyr/url/percent_encoding/percent_encode_range.hpp
5453
${PROJECT_SOURCE_DIR}/include/skyr/url/percent_encoding/percent_encoded_char.hpp
54+
${PROJECT_SOURCE_DIR}/include/skyr/url/ipv4_address.hpp
55+
${PROJECT_SOURCE_DIR}/include/skyr/url/ipv6_address.hpp
5556
${PROJECT_SOURCE_DIR}/include/skyr/url/url_record.hpp
5657
${PROJECT_SOURCE_DIR}/include/skyr/url/url_parse.hpp
5758
${PROJECT_SOURCE_DIR}/include/skyr/url/url_serialize.hpp
@@ -82,6 +83,3 @@ target_include_directories(skyr-url
8283
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
8384
PRIVATE
8485
${PROJECT_SOURCE_DIR}/src)
85-
#
86-
##propagate sources to parent scope for one-lib-build
87-
#set(skyr_SRCS ${skyr_SRCS} PARENT_SCOPE)

src/url/ipv4_address.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#include <optional>
1212
#include <cstdlib>
1313
#include <cerrno>
14-
#include "ipv4_address.hpp"
14+
#include <skyr/url/ipv4_address.hpp>
1515

1616
namespace skyr {
1717
inline namespace v1 {

src/url/ipv6_address.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2018 Glyn Matthews.
1+
// Copyright 2018-20 Glyn Matthews.
22
// Distributed under the Boost Software License, Version 1.0.
33
// (See accompanying file LICENSE_1_0.txt or copy at
44
// http://www.boost.org/LICENSE_1_0.txt)
@@ -10,7 +10,7 @@
1010
#include <locale>
1111
#include <algorithm>
1212
#include <optional>
13-
#include "ipv6_address.hpp"
13+
#include <skyr/url/ipv6_address.hpp>
1414
#include "algorithms.hpp"
1515

1616
namespace skyr {

src/url/url.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,47 @@ tl::expected<void, std::error_code> url::set_hostname(string_view hostname) {
188188
});
189189
}
190190

191+
bool url::is_ipv4_address() const {
192+
return parse_ipv4_address(hostname()).has_value();
193+
}
194+
195+
std::optional<skyr::ipv4_address> url::ipv4_address() const {
196+
auto address = parse_ipv4_address(hostname());
197+
return address.has_value() ? std::make_optional(address.value()) : std::nullopt;
198+
}
199+
200+
bool url::is_ipv6_address() const {
201+
if (!url_.host) {
202+
return false;
203+
}
204+
auto view = std::string_view(url_.host.value());
205+
if ((view.size() <= 2) || view.front() != '[' || view.back() != ']') {
206+
return false;
207+
}
208+
return parse_ipv6_address(view.substr(1, view.size() - 2)).has_value();
209+
}
210+
211+
std::optional<skyr::ipv6_address> url::ipv6_address() const {
212+
if (!url_.host) {
213+
return std::nullopt;
214+
}
215+
auto view = std::string_view(url_.host.value());
216+
if ((view.size() <= 2) || view.front() != '[' || view.back() != ']') {
217+
return std::nullopt;
218+
}
219+
220+
auto address = parse_ipv6_address(view.substr(1, view.size() - 2));
221+
return address.has_value() ? std::make_optional(address.value()) : std::nullopt;
222+
}
223+
224+
bool url::is_domain() const {
225+
return details::is_special(url_.scheme) && !hostname().empty() && !is_ipv4_address() && !is_ipv6_address();
226+
}
227+
228+
bool url::is_opaque() const {
229+
return !details::is_special(url_.scheme) && !hostname().empty();
230+
}
231+
191232
url::string_type url::port() const {
192233
if (!url_.port) {
193234
return {};

src/url/url_parser_context.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
#include <skyr/url/percent_encoding/percent_encode_range.hpp>
1818
#include "url_parser_context.hpp"
1919
#include "url_schemes.hpp"
20-
#include "ipv4_address.hpp"
21-
#include "ipv6_address.hpp"
20+
#include "skyr/url/ipv4_address.hpp"
21+
#include "skyr/url/ipv6_address.hpp"
2222
#include "algorithms.hpp"
2323

2424
namespace skyr {

0 commit comments

Comments
 (0)