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 <boost/http/server/encode_url.hpp>
10  
#include <boost/http/server/encode_url.hpp>
11  

11  

12  
namespace boost {
12  
namespace boost {
13  
namespace http {
13  
namespace http {
14  

14  

15  
namespace {
15  
namespace {
16  

16  

17  
// Check if character needs encoding
17  
// Check if character needs encoding
18  
// Unreserved + reserved chars that are allowed in URLs
18  
// Unreserved + reserved chars that are allowed in URLs
19  
bool
19  
bool
20  
is_safe( char c ) noexcept
20  
is_safe( char c ) noexcept
21  
{
21  
{
22  
    // Unreserved: A-Z a-z 0-9 - _ . ~
22  
    // Unreserved: A-Z a-z 0-9 - _ . ~
23  
    if( ( c >= 'A' && c <= 'Z' ) ||
23  
    if( ( c >= 'A' && c <= 'Z' ) ||
24  
        ( c >= 'a' && c <= 'z' ) ||
24  
        ( c >= 'a' && c <= 'z' ) ||
25  
        ( c >= '0' && c <= '9' ) ||
25  
        ( c >= '0' && c <= '9' ) ||
26  
        c == '-' || c == '_' || c == '.' || c == '~' )
26  
        c == '-' || c == '_' || c == '.' || c == '~' )
27  
        return true;
27  
        return true;
28  

28  

29  
    // Reserved chars allowed in URLs: ! # $ & ' ( ) * + , / : ; = ? @
29  
    // Reserved chars allowed in URLs: ! # $ & ' ( ) * + , / : ; = ? @
30  
    switch( c )
30  
    switch( c )
31  
    {
31  
    {
32  
    case '!':
32  
    case '!':
33  
    case '#':
33  
    case '#':
34  
    case '$':
34  
    case '$':
35  
    case '&':
35  
    case '&':
36  
    case '\'':
36  
    case '\'':
37  
    case '(':
37  
    case '(':
38  
    case ')':
38  
    case ')':
39  
    case '*':
39  
    case '*':
40  
    case '+':
40  
    case '+':
41  
    case ',':
41  
    case ',':
42  
    case '/':
42  
    case '/':
43  
    case ':':
43  
    case ':':
44  
    case ';':
44  
    case ';':
45  
    case '=':
45  
    case '=':
46  
    case '?':
46  
    case '?':
47  
    case '@':
47  
    case '@':
48  
        return true;
48  
        return true;
49  
    default:
49  
    default:
50  
        return false;
50  
        return false;
51  
    }
51  
    }
52  
}
52  
}
53  

53  

54  
constexpr char hex_chars[] = "0123456789ABCDEF";
54  
constexpr char hex_chars[] = "0123456789ABCDEF";
55  

55  

56  
} // (anon)
56  
} // (anon)
57  

57  

58  
std::string
58  
std::string
59  
encode_url( core::string_view url )
59  
encode_url( core::string_view url )
60  
{
60  
{
61  
    std::string result;
61  
    std::string result;
62  
    result.reserve( url.size() );
62  
    result.reserve( url.size() );
63  

63  

64  
    for( unsigned char c : url )
64  
    for( unsigned char c : url )
65  
    {
65  
    {
66  
        if( is_safe( static_cast<char>( c ) ) )
66  
        if( is_safe( static_cast<char>( c ) ) )
67  
        {
67  
        {
68  
            result.push_back( static_cast<char>( c ) );
68  
            result.push_back( static_cast<char>( c ) );
69  
        }
69  
        }
70  
        else
70  
        else
71  
        {
71  
        {
72  
            result.push_back( '%' );
72  
            result.push_back( '%' );
73  
            result.push_back( hex_chars[c >> 4] );
73  
            result.push_back( hex_chars[c >> 4] );
74  
            result.push_back( hex_chars[c & 0x0F] );
74  
            result.push_back( hex_chars[c & 0x0F] );
75  
        }
75  
        }
76  
    }
76  
    }
77  

77  

78  
    return result;
78  
    return result;
79  
}
79  
}
80  

80  

81  
} // http
81  
} // http
82  
} // boost
82  
} // boost