1  
//
1  
//
2  
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/boostorg/url
7  
// Official repository: https://github.com/boostorg/url
8  
//
8  
//
9  

9  

10  

10  

11  
#include <boost/url/detail/config.hpp>
11  
#include <boost/url/detail/config.hpp>
12  
#include <boost/url/authority_view.hpp>
12  
#include <boost/url/authority_view.hpp>
13  
#include "detail/normalize.hpp"
13  
#include "detail/normalize.hpp"
14  
#include <boost/url/grammar/parse.hpp>
14  
#include <boost/url/grammar/parse.hpp>
15  
#include <boost/url/rfc/authority_rule.hpp>
15  
#include <boost/url/rfc/authority_rule.hpp>
16  
#include <boost/url/rfc/pct_encoded_rule.hpp>
16  
#include <boost/url/rfc/pct_encoded_rule.hpp>
17  
#include <array>
17  
#include <array>
18  
#include <ostream>
18  
#include <ostream>
19  

19  

20  
namespace boost {
20  
namespace boost {
21  
namespace urls {
21  
namespace urls {
22  

22  

23  
//------------------------------------------------
23  
//------------------------------------------------
24  

24  

25  
namespace detail {
25  
namespace detail {
26  

26  

27  
authority_view
27  
authority_view
28  
url_impl::
28  
url_impl::
29  
construct_authority() const noexcept
29  
construct_authority() const noexcept
30  
{
30  
{
31  
    return authority_view(*this);
31  
    return authority_view(*this);
32  
}
32  
}
33  

33  

34  
} // detail
34  
} // detail
35  

35  

36  
//------------------------------------------------
36  
//------------------------------------------------
37  

37  

38  
authority_view::
38  
authority_view::
39  
authority_view(
39  
authority_view(
40  
    detail::url_impl const& u) noexcept
40  
    detail::url_impl const& u) noexcept
41  
    : u_(u)
41  
    : u_(u)
42  
{
42  
{
43  
}
43  
}
44  

44  

45  
//------------------------------------------------
45  
//------------------------------------------------
46  

46  

47  
authority_view::
47  
authority_view::
48  
~authority_view()
48  
~authority_view()
49  
{
49  
{
50  
}
50  
}
51  

51  

52  
authority_view::
52  
authority_view::
53  
authority_view() noexcept
53  
authority_view() noexcept
54  
    : u_(from::authority)
54  
    : u_(from::authority)
55  
{
55  
{
56  
}
56  
}
57  

57  

58  
authority_view::
58  
authority_view::
59  
authority_view(
59  
authority_view(
60  
    core::string_view s)
60  
    core::string_view s)
61  
    : authority_view(
61  
    : authority_view(
62  
        parse_authority(s
62  
        parse_authority(s
63  
            ).value(BOOST_URL_POS))
63  
            ).value(BOOST_URL_POS))
64  
{
64  
{
65  
}
65  
}
66  

66  

67  
authority_view::
67  
authority_view::
68  
authority_view(
68  
authority_view(
69  
    authority_view const&) noexcept = default;
69  
    authority_view const&) noexcept = default;
70  

70  

71  
authority_view&
71  
authority_view&
72  
authority_view::
72  
authority_view::
73  
operator=(
73  
operator=(
74  
    authority_view const&) noexcept = default;
74  
    authority_view const&) noexcept = default;
75  

75  

76  
//------------------------------------------------
76  
//------------------------------------------------
77  
//
77  
//
78  
// Userinfo
78  
// Userinfo
79  
//
79  
//
80  
//------------------------------------------------
80  
//------------------------------------------------
81  

81  

82  
bool
82  
bool
83  
authority_view::
83  
authority_view::
84  
has_userinfo() const noexcept
84  
has_userinfo() const noexcept
85  
{
85  
{
86  
    auto n = u_.len(id_pass);
86  
    auto n = u_.len(id_pass);
87  
    if(n == 0)
87  
    if(n == 0)
88  
        return false;
88  
        return false;
89  
    BOOST_ASSERT(u_.get(
89  
    BOOST_ASSERT(u_.get(
90  
        id_pass).ends_with('@'));
90  
        id_pass).ends_with('@'));
91  
    return true;
91  
    return true;
92  
}
92  
}
93  

93  

94  
pct_string_view
94  
pct_string_view
95  
authority_view::
95  
authority_view::
96  
encoded_userinfo() const noexcept
96  
encoded_userinfo() const noexcept
97  
{
97  
{
98  
    auto s = u_.get(
98  
    auto s = u_.get(
99  
        id_user, id_host);
99  
        id_user, id_host);
100  
    if(s.empty())
100  
    if(s.empty())
101  
        return s;
101  
        return s;
102  
    BOOST_ASSERT(
102  
    BOOST_ASSERT(
103  
        s.ends_with('@'));
103  
        s.ends_with('@'));
104  
    s.remove_suffix(1);
104  
    s.remove_suffix(1);
105  
    return make_pct_string_view_unsafe(
105  
    return make_pct_string_view_unsafe(
106  
        s.data(),
106  
        s.data(),
107  
        s.size(),
107  
        s.size(),
108  
        u_.decoded_[id_user] +
108  
        u_.decoded_[id_user] +
109  
            u_.decoded_[id_pass] +
109  
            u_.decoded_[id_pass] +
110  
            has_password());
110  
            has_password());
111  
}
111  
}
112  

112  

113  
pct_string_view
113  
pct_string_view
114  
authority_view::
114  
authority_view::
115  
encoded_user() const noexcept
115  
encoded_user() const noexcept
116  
{
116  
{
117  
    auto s = u_.get(id_user);
117  
    auto s = u_.get(id_user);
118  
    return make_pct_string_view_unsafe(
118  
    return make_pct_string_view_unsafe(
119  
        s.data(),
119  
        s.data(),
120  
        s.size(),
120  
        s.size(),
121  
        u_.decoded_[id_user]);
121  
        u_.decoded_[id_user]);
122  
}
122  
}
123  

123  

124  
bool
124  
bool
125  
authority_view::
125  
authority_view::
126  
has_password() const noexcept
126  
has_password() const noexcept
127  
{
127  
{
128  
    auto const n = u_.len(id_pass);
128  
    auto const n = u_.len(id_pass);
129  
    if(n > 1)
129  
    if(n > 1)
130  
    {
130  
    {
131  
        BOOST_ASSERT(u_.get(id_pass
131  
        BOOST_ASSERT(u_.get(id_pass
132  
            ).starts_with(':'));
132  
            ).starts_with(':'));
133  
        BOOST_ASSERT(u_.get(id_pass
133  
        BOOST_ASSERT(u_.get(id_pass
134  
            ).ends_with('@'));
134  
            ).ends_with('@'));
135  
        return true;
135  
        return true;
136  
    }
136  
    }
137  
    BOOST_ASSERT(n == 0 || u_.get(
137  
    BOOST_ASSERT(n == 0 || u_.get(
138  
        id_pass).ends_with('@'));
138  
        id_pass).ends_with('@'));
139  
    return false;
139  
    return false;
140  
}
140  
}
141  

141  

142  
pct_string_view
142  
pct_string_view
143  
authority_view::
143  
authority_view::
144  
encoded_password() const noexcept
144  
encoded_password() const noexcept
145  
{
145  
{
146  
    auto s = u_.get(id_pass);
146  
    auto s = u_.get(id_pass);
147  
    switch(s.size())
147  
    switch(s.size())
148  
    {
148  
    {
149  
    case 1:
149  
    case 1:
150  
        BOOST_ASSERT(
150  
        BOOST_ASSERT(
151  
            s.starts_with('@'));
151  
            s.starts_with('@'));
152  
        s.remove_prefix(1);
152  
        s.remove_prefix(1);
153  
        BOOST_FALLTHROUGH;
153  
        BOOST_FALLTHROUGH;
154  
    case 0:
154  
    case 0:
155  
        return make_pct_string_view_unsafe(
155  
        return make_pct_string_view_unsafe(
156  
            s.data(), s.size(), 0);
156  
            s.data(), s.size(), 0);
157  
    default:
157  
    default:
158  
        break;
158  
        break;
159  
    }
159  
    }
160  
    BOOST_ASSERT(s.ends_with('@'));
160  
    BOOST_ASSERT(s.ends_with('@'));
161  
    BOOST_ASSERT(s.starts_with(':'));
161  
    BOOST_ASSERT(s.starts_with(':'));
162  
    return make_pct_string_view_unsafe(
162  
    return make_pct_string_view_unsafe(
163  
        s.data() + 1,
163  
        s.data() + 1,
164  
        s.size() - 2,
164  
        s.size() - 2,
165  
        u_.decoded_[id_pass]);
165  
        u_.decoded_[id_pass]);
166  
}
166  
}
167  

167  

168  
//------------------------------------------------
168  
//------------------------------------------------
169  
//
169  
//
170  
// Host
170  
// Host
171  
//
171  
//
172  
//------------------------------------------------
172  
//------------------------------------------------
173  
/*
173  
/*
174  
host_type       host_type()                 // ipv4, ipv6, ipvfuture, name
174  
host_type       host_type()                 // ipv4, ipv6, ipvfuture, name
175  

175  

176  
std::string     host()                      // return encoded_host().decode()
176  
std::string     host()                      // return encoded_host().decode()
177  
pct_string_view encoded_host()              // return host part, as-is
177  
pct_string_view encoded_host()              // return host part, as-is
178  
std::string     host_address()              // return encoded_host_address().decode()
178  
std::string     host_address()              // return encoded_host_address().decode()
179  
pct_string_view encoded_host_address()      // ipv4, ipv6, ipvfut, or encoded name, no brackets
179  
pct_string_view encoded_host_address()      // ipv4, ipv6, ipvfut, or encoded name, no brackets
180  

180  

181  
ipv4_address    host_ipv4_address()         // return ipv4_address or {}
181  
ipv4_address    host_ipv4_address()         // return ipv4_address or {}
182  
ipv6_address    host_ipv6_address()         // return ipv6_address or {}
182  
ipv6_address    host_ipv6_address()         // return ipv6_address or {}
183  
core::string_view     host_ipvfuture()            // return ipvfuture or {}
183  
core::string_view     host_ipvfuture()            // return ipvfuture or {}
184  
std::string     host_name()                 // return decoded name or ""
184  
std::string     host_name()                 // return decoded name or ""
185  
pct_string_view encoded_host_name()         // return encoded host name or ""
185  
pct_string_view encoded_host_name()         // return encoded host name or ""
186  
*/
186  
*/
187  

187  

188  
pct_string_view
188  
pct_string_view
189  
authority_view::
189  
authority_view::
190  
encoded_host() const noexcept
190  
encoded_host() const noexcept
191  
{
191  
{
192  
    return u_.pct_get(id_host);
192  
    return u_.pct_get(id_host);
193  
}
193  
}
194  

194  

195  
pct_string_view
195  
pct_string_view
196  
authority_view::
196  
authority_view::
197  
encoded_host_address() const noexcept
197  
encoded_host_address() const noexcept
198  
{
198  
{
199  
    core::string_view s = u_.get(id_host);
199  
    core::string_view s = u_.get(id_host);
200  
    std::size_t n;
200  
    std::size_t n;
201  
    switch(u_.host_type_)
201  
    switch(u_.host_type_)
202  
    {
202  
    {
203  
    case urls::host_type::name:
203  
    case urls::host_type::name:
204  
    case urls::host_type::ipv4:
204  
    case urls::host_type::ipv4:
205  
        n = u_.decoded_[id_host];
205  
        n = u_.decoded_[id_host];
206  
        break;
206  
        break;
207  

207  

208  
    case urls::host_type::ipv6:
208  
    case urls::host_type::ipv6:
209  
    case urls::host_type::ipvfuture:
209  
    case urls::host_type::ipvfuture:
210  
    {
210  
    {
211  
        BOOST_ASSERT(
211  
        BOOST_ASSERT(
212  
            u_.decoded_[id_host] ==
212  
            u_.decoded_[id_host] ==
213  
                s.size());
213  
                s.size());
214  
        BOOST_ASSERT(s.size() >= 2);
214  
        BOOST_ASSERT(s.size() >= 2);
215  
        BOOST_ASSERT(s.front() == '[');
215  
        BOOST_ASSERT(s.front() == '[');
216  
        BOOST_ASSERT(s.back() == ']');
216  
        BOOST_ASSERT(s.back() == ']');
217  
        s = s.substr(1, s.size() - 2);
217  
        s = s.substr(1, s.size() - 2);
218  
        n = u_.decoded_[id_host] - 2;
218  
        n = u_.decoded_[id_host] - 2;
219  
        break;
219  
        break;
220  
    }
220  
    }
221  
    // LCOV_EXCL_START
221  
    // LCOV_EXCL_START
222  
    default:
222  
    default:
223  
    case urls::host_type::none:
223  
    case urls::host_type::none:
224  
        /*
224  
        /*
225  
         * This condition is for correctness
225  
         * This condition is for correctness
226  
         * only.
226  
         * only.
227  
         * This should never happen, because
227  
         * This should never happen, because
228  
         * the `host_rule` will set the host
228  
         * the `host_rule` will set the host
229  
         * type to `name` when it's empty.
229  
         * type to `name` when it's empty.
230  
         * This is correct because `reg-name`
230  
         * This is correct because `reg-name`
231  
         * accepts empty strings.
231  
         * accepts empty strings.
232  
         */
232  
         */
233  
        BOOST_ASSERT(s.empty());
233  
        BOOST_ASSERT(s.empty());
234  
        n = 0;
234  
        n = 0;
235  
        break;
235  
        break;
236  
    // LCOV_EXCL_STOP
236  
    // LCOV_EXCL_STOP
237  
    }
237  
    }
238  
    return make_pct_string_view_unsafe(
238  
    return make_pct_string_view_unsafe(
239  
        s.data(), s.size(), n);
239  
        s.data(), s.size(), n);
240  
}
240  
}
241  

241  

242  
urls::ipv4_address
242  
urls::ipv4_address
243  
authority_view::
243  
authority_view::
244  
host_ipv4_address() const noexcept
244  
host_ipv4_address() const noexcept
245  
{
245  
{
246  
    if(u_.host_type_ !=
246  
    if(u_.host_type_ !=
247  
            urls::host_type::ipv4)
247  
            urls::host_type::ipv4)
248  
        return {};
248  
        return {};
249  
    ipv4_address::bytes_type b{{}};
249  
    ipv4_address::bytes_type b{{}};
250  
    std::memcpy(
250  
    std::memcpy(
251  
        &b[0], &u_.ip_addr_[0], b.size());
251  
        &b[0], &u_.ip_addr_[0], b.size());
252  
    return urls::ipv4_address(b);
252  
    return urls::ipv4_address(b);
253  
}
253  
}
254  

254  

255  
urls::ipv6_address
255  
urls::ipv6_address
256  
authority_view::
256  
authority_view::
257  
host_ipv6_address() const noexcept
257  
host_ipv6_address() const noexcept
258  
{
258  
{
259  
    if(u_.host_type_ !=
259  
    if(u_.host_type_ !=
260  
            urls::host_type::ipv6)
260  
            urls::host_type::ipv6)
261  
        return {};
261  
        return {};
262  
    ipv6_address::bytes_type b{{}};
262  
    ipv6_address::bytes_type b{{}};
263  
    std::memcpy(
263  
    std::memcpy(
264  
        &b[0], &u_.ip_addr_[0], b.size());
264  
        &b[0], &u_.ip_addr_[0], b.size());
265  
    return urls::ipv6_address(b);
265  
    return urls::ipv6_address(b);
266  
}
266  
}
267  

267  

268  
core::string_view
268  
core::string_view
269  
authority_view::
269  
authority_view::
270  
host_ipvfuture() const noexcept
270  
host_ipvfuture() const noexcept
271  
{
271  
{
272  
    if(u_.host_type_ !=
272  
    if(u_.host_type_ !=
273  
            urls::host_type::ipvfuture)
273  
            urls::host_type::ipvfuture)
274  
        return {};
274  
        return {};
275  
    core::string_view s = u_.get(id_host);
275  
    core::string_view s = u_.get(id_host);
276  
    BOOST_ASSERT(s.size() >= 6);
276  
    BOOST_ASSERT(s.size() >= 6);
277  
    BOOST_ASSERT(s.front() == '[');
277  
    BOOST_ASSERT(s.front() == '[');
278  
    BOOST_ASSERT(s.back() == ']');
278  
    BOOST_ASSERT(s.back() == ']');
279  
    s = s.substr(1, s.size() - 2);
279  
    s = s.substr(1, s.size() - 2);
280  
    return s;
280  
    return s;
281  
}
281  
}
282  

282  

283  
pct_string_view
283  
pct_string_view
284  
authority_view::
284  
authority_view::
285  
encoded_host_name() const noexcept
285  
encoded_host_name() const noexcept
286  
{
286  
{
287  
    if(u_.host_type_ !=
287  
    if(u_.host_type_ !=
288  
            urls::host_type::name)
288  
            urls::host_type::name)
289  
        return {};
289  
        return {};
290  
    return u_.pct_get(id_host);
290  
    return u_.pct_get(id_host);
291  
}
291  
}
292  

292  

293  
//------------------------------------------------
293  
//------------------------------------------------
294  
//
294  
//
295  
// Port
295  
// Port
296  
//
296  
//
297  
//------------------------------------------------
297  
//------------------------------------------------
298  

298  

299  
bool
299  
bool
300  
authority_view::
300  
authority_view::
301  
has_port() const noexcept
301  
has_port() const noexcept
302  
{
302  
{
303  
    auto const n = u_.len(id_port);
303  
    auto const n = u_.len(id_port);
304  
    if(n == 0)
304  
    if(n == 0)
305  
        return false;
305  
        return false;
306  
    BOOST_ASSERT(
306  
    BOOST_ASSERT(
307  
        u_.get(id_port).starts_with(':'));
307  
        u_.get(id_port).starts_with(':'));
308  
    return true;
308  
    return true;
309  
}
309  
}
310  

310  

311  
core::string_view
311  
core::string_view
312  
authority_view::
312  
authority_view::
313  
port() const noexcept
313  
port() const noexcept
314  
{
314  
{
315  
    auto s = u_.get(id_port);
315  
    auto s = u_.get(id_port);
316  
    if(s.empty())
316  
    if(s.empty())
317  
        return s;
317  
        return s;
318  
    BOOST_ASSERT(has_port());
318  
    BOOST_ASSERT(has_port());
319  
    return s.substr(1);
319  
    return s.substr(1);
320  
}
320  
}
321  

321  

322  
std::uint16_t
322  
std::uint16_t
323  
authority_view::
323  
authority_view::
324  
port_number() const noexcept
324  
port_number() const noexcept
325  
{
325  
{
326  
    BOOST_ASSERT(
326  
    BOOST_ASSERT(
327  
        has_port() ||
327  
        has_port() ||
328  
        u_.port_number_ == 0);
328  
        u_.port_number_ == 0);
329  
    return u_.port_number_;
329  
    return u_.port_number_;
330  
}
330  
}
331  

331  

332  
pct_string_view
332  
pct_string_view
333  
authority_view::
333  
authority_view::
334  
encoded_host_and_port() const noexcept
334  
encoded_host_and_port() const noexcept
335  
{
335  
{
336  
    return u_.get(id_host, id_end);
336  
    return u_.get(id_host, id_end);
337  
}
337  
}
338  

338  

339  
//------------------------------------------------
339  
//------------------------------------------------
340  
//
340  
//
341  
// Parsing
341  
// Parsing
342  
//
342  
//
343  
//------------------------------------------------
343  
//------------------------------------------------
344  

344  

345  
system::result<authority_view>
345  
system::result<authority_view>
346  
parse_authority(
346  
parse_authority(
347  
    core::string_view s) noexcept
347  
    core::string_view s) noexcept
348  
{
348  
{
349  
    return grammar::parse(s, authority_rule);
349  
    return grammar::parse(s, authority_rule);
350  
}
350  
}
351  

351  

352  
//------------------------------------------------
352  
//------------------------------------------------
353  
//
353  
//
354  
// Comparisons
354  
// Comparisons
355  
//
355  
//
356  
//------------------------------------------------
356  
//------------------------------------------------
357  

357  

358  
int
358  
int
359  
authority_view::
359  
authority_view::
360  
compare(const authority_view& other) const noexcept
360  
compare(const authority_view& other) const noexcept
361  
{
361  
{
362  
    auto comp = static_cast<int>(has_userinfo()) -
362  
    auto comp = static_cast<int>(has_userinfo()) -
363  
        static_cast<int>(other.has_userinfo());
363  
        static_cast<int>(other.has_userinfo());
364  
    if ( comp != 0 )
364  
    if ( comp != 0 )
365  
        return comp;
365  
        return comp;
366  

366  

367  
    if (has_userinfo())
367  
    if (has_userinfo())
368  
    {
368  
    {
369  
        comp = detail::compare_encoded(
369  
        comp = detail::compare_encoded(
370  
            encoded_user(),
370  
            encoded_user(),
371  
            other.encoded_user());
371  
            other.encoded_user());
372  
        if ( comp != 0 )
372  
        if ( comp != 0 )
373  
            return comp;
373  
            return comp;
374  

374  

375  
        comp = static_cast<int>(has_password()) -
375  
        comp = static_cast<int>(has_password()) -
376  
               static_cast<int>(other.has_password());
376  
               static_cast<int>(other.has_password());
377  
        if ( comp != 0 )
377  
        if ( comp != 0 )
378  
            return comp;
378  
            return comp;
379  

379  

380  
        if (has_password())
380  
        if (has_password())
381  
        {
381  
        {
382  
            comp = detail::compare_encoded(
382  
            comp = detail::compare_encoded(
383  
                encoded_password(),
383  
                encoded_password(),
384  
                other.encoded_password());
384  
                other.encoded_password());
385  
            if ( comp != 0 )
385  
            if ( comp != 0 )
386  
                return comp;
386  
                return comp;
387  
        }
387  
        }
388  
    }
388  
    }
389  

389  

390  
    comp = detail::ci_compare_encoded(
390  
    comp = detail::ci_compare_encoded(
391  
        encoded_host(),
391  
        encoded_host(),
392  
        other.encoded_host());
392  
        other.encoded_host());
393  
    if ( comp != 0 )
393  
    if ( comp != 0 )
394  
        return comp;
394  
        return comp;
395  

395  

396  
    comp = static_cast<int>(has_port()) -
396  
    comp = static_cast<int>(has_port()) -
397  
           static_cast<int>(other.has_port());
397  
           static_cast<int>(other.has_port());
398  
    if ( comp != 0 )
398  
    if ( comp != 0 )
399  
        return comp;
399  
        return comp;
400  

400  

401  
    if (has_port())
401  
    if (has_port())
402  
    {
402  
    {
403  
        comp = detail::compare(
403  
        comp = detail::compare(
404  
            port(),
404  
            port(),
405  
            other.port());
405  
            other.port());
406  
        if ( comp != 0 )
406  
        if ( comp != 0 )
407  
            return comp;
407  
            return comp;
408  
    }
408  
    }
409  

409  

410  
    return 0;
410  
    return 0;
411  
}
411  
}
412  

412  

413  
} // urls
413  
} // urls
414  
} // boost
414  
} // boost
415  

415