1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 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/cppalliance/http
7  
// Official repository: https://github.com/cppalliance/http
8  
//
8  
//
9  

9  

10  
#include "src/server/detail/pct_decode.hpp"
10  
#include "src/server/detail/pct_decode.hpp"
11  

11  

12  
namespace boost {
12  
namespace boost {
13  
namespace http {
13  
namespace http {
14  
namespace detail {
14  
namespace detail {
15  

15  

16  
bool
16  
bool
17  
ci_is_equal(
17  
ci_is_equal(
18  
    core::string_view s0,
18  
    core::string_view s0,
19  
    core::string_view s1) noexcept
19  
    core::string_view s1) noexcept
20  
{
20  
{
21  
    auto n = s0.size();
21  
    auto n = s0.size();
22  
    if(s1.size() != n)
22  
    if(s1.size() != n)
23  
        return false;
23  
        return false;
24  
    auto p1 = s0.data();
24  
    auto p1 = s0.data();
25  
    auto p2 = s1.data();
25  
    auto p2 = s1.data();
26  
    char a, b;
26  
    char a, b;
27  
    // fast loop
27  
    // fast loop
28  
    while(n--)
28  
    while(n--)
29  
    {
29  
    {
30  
        a = *p1++;
30  
        a = *p1++;
31  
        b = *p2++;
31  
        b = *p2++;
32  
        if(a != b)
32  
        if(a != b)
33  
            goto slow;
33  
            goto slow;
34  
    }
34  
    }
35  
    return true;
35  
    return true;
36  
    do
36  
    do
37  
    {
37  
    {
38  
        a = *p1++;
38  
        a = *p1++;
39  
        b = *p2++;
39  
        b = *p2++;
40  
    slow:
40  
    slow:
41  
        if( grammar::to_lower(a) !=
41  
        if( grammar::to_lower(a) !=
42  
            grammar::to_lower(b))
42  
            grammar::to_lower(b))
43  
            return false;
43  
            return false;
44  
    }
44  
    }
45  
    while(n--);
45  
    while(n--);
46  
    return true;
46  
    return true;
47  
}
47  
}
48  

48  

49  
std::string
49  
std::string
50  
pct_decode(
50  
pct_decode(
51  
    urls::pct_string_view s)
51  
    urls::pct_string_view s)
52  
{
52  
{
53  
    std::string result;
53  
    std::string result;
54  
    core::string_view sv(s);
54  
    core::string_view sv(s);
55  
    result.reserve(s.size());
55  
    result.reserve(s.size());
56  
    auto it = sv.data();
56  
    auto it = sv.data();
57  
    auto const end = it + sv.size();
57  
    auto const end = it + sv.size();
58  
    for(;;)
58  
    for(;;)
59  
    {
59  
    {
60  
        if(it == end)
60  
        if(it == end)
61  
            break;
61  
            break;
62  
        if(*it != '%')
62  
        if(*it != '%')
63  
        {
63  
        {
64  
            result.push_back(*it++);
64  
            result.push_back(*it++);
65  
            continue;
65  
            continue;
66  
        }
66  
        }
67  
        ++it;
67  
        ++it;
68  
#if 0
68  
#if 0
69  
        // pct_string_view can never have invalid pct-encodings
69  
        // pct_string_view can never have invalid pct-encodings
70  
        if(it == end)
70  
        if(it == end)
71  
            goto invalid;
71  
            goto invalid;
72  
#endif
72  
#endif
73  
        auto d0 = urls::grammar::hexdig_value(*it++);
73  
        auto d0 = urls::grammar::hexdig_value(*it++);
74  
#if 0
74  
#if 0
75  
        // pct_string_view can never have invalid pct-encodings
75  
        // pct_string_view can never have invalid pct-encodings
76  
        if( d0 < 0 ||
76  
        if( d0 < 0 ||
77  
            it == end)
77  
            it == end)
78  
            goto invalid;
78  
            goto invalid;
79  
#endif
79  
#endif
80  
        auto d1 = urls::grammar::hexdig_value(*it++);
80  
        auto d1 = urls::grammar::hexdig_value(*it++);
81  
#if 0
81  
#if 0
82  
        // pct_string_view can never have invalid pct-encodings
82  
        // pct_string_view can never have invalid pct-encodings
83  
        if(d1 < 0)
83  
        if(d1 < 0)
84  
            goto invalid;
84  
            goto invalid;
85  
#endif
85  
#endif
86  
        result.push_back(d0 * 16 + d1);
86  
        result.push_back(d0 * 16 + d1);
87  
    }
87  
    }
88  
    return result;
88  
    return result;
89  
#if 0
89  
#if 0
90  
invalid:
90  
invalid:
91  
    // can't get here, as received a pct_string_view
91  
    // can't get here, as received a pct_string_view
92  
    detail::throw_invalid_argument();
92  
    detail::throw_invalid_argument();
93  
#endif
93  
#endif
94  
}
94  
}
95  

95  

96  
// decode all percent escapes except forward slash '/'
96  
// decode all percent escapes except forward slash '/'
97  
// (backslash is decoded since it's not a path separator in URLs)
97  
// (backslash is decoded since it's not a path separator in URLs)
98  
std::string
98  
std::string
99  
pct_decode_path(
99  
pct_decode_path(
100  
    urls::pct_string_view s)
100  
    urls::pct_string_view s)
101  
{
101  
{
102  
    std::string result;
102  
    std::string result;
103  
    core::string_view sv(s);
103  
    core::string_view sv(s);
104  
    result.reserve(s.size());
104  
    result.reserve(s.size());
105  
    auto it = sv.data();
105  
    auto it = sv.data();
106  
    auto const end = it + sv.size();
106  
    auto const end = it + sv.size();
107  
    for(;;)
107  
    for(;;)
108  
    {
108  
    {
109  
        if(it == end)
109  
        if(it == end)
110  
            break;
110  
            break;
111  
        if(*it != '%')
111  
        if(*it != '%')
112  
        {
112  
        {
113  
            result.push_back(*it++);
113  
            result.push_back(*it++);
114  
            continue;
114  
            continue;
115  
        }
115  
        }
116  
        ++it;
116  
        ++it;
117  
#if 0
117  
#if 0
118  
        // pct_string_view can never have invalid pct-encodings
118  
        // pct_string_view can never have invalid pct-encodings
119  
        if(it == end)
119  
        if(it == end)
120  
            goto invalid;
120  
            goto invalid;
121  
#endif
121  
#endif
122  
        auto d0 = urls::grammar::hexdig_value(*it++);
122  
        auto d0 = urls::grammar::hexdig_value(*it++);
123  
#if 0
123  
#if 0
124  
        // pct_string_view can never have invalid pct-encodings
124  
        // pct_string_view can never have invalid pct-encodings
125  
        if( d0 < 0 ||
125  
        if( d0 < 0 ||
126  
            it == end)
126  
            it == end)
127  
            goto invalid;
127  
            goto invalid;
128  
#endif
128  
#endif
129  
        auto d1 = urls::grammar::hexdig_value(*it++);
129  
        auto d1 = urls::grammar::hexdig_value(*it++);
130  
#if 0
130  
#if 0
131  
        // pct_string_view can never have invalid pct-encodings
131  
        // pct_string_view can never have invalid pct-encodings
132  
        if(d1 < 0)
132  
        if(d1 < 0)
133  
            goto invalid;
133  
            goto invalid;
134  
#endif
134  
#endif
135  
        char c = d0 * 16 + d1;
135  
        char c = d0 * 16 + d1;
136  
        if(c != '/')
136  
        if(c != '/')
137  
        {
137  
        {
138  
            result.push_back(c);
138  
            result.push_back(c);
139  
            continue;
139  
            continue;
140  
        }
140  
        }
141  
        result.append(it - 3, 3);
141  
        result.append(it - 3, 3);
142  
    }
142  
    }
143  
    return result;
143  
    return result;
144  
#if 0
144  
#if 0
145  
invalid:
145  
invalid:
146  
    // can't get here, as received a pct_string_view
146  
    // can't get here, as received a pct_string_view
147  
    detail::throw_invalid_argument();
147  
    detail::throw_invalid_argument();
148  
#endif
148  
#endif
149  
}
149  
}
150  

150  

151  
} // detail
151  
} // detail
152  
} // http
152  
} // http
153  
} // boost
153  
} // boost