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/mime_db.hpp>
10  
#include <boost/http/server/mime_db.hpp>
11  
#include <algorithm>
11  
#include <algorithm>
12  
#include <cctype>
12  
#include <cctype>
13  

13  

14  
namespace boost {
14  
namespace boost {
15  
namespace http {
15  
namespace http {
16  
namespace mime_db {
16  
namespace mime_db {
17  

17  

18  
namespace {
18  
namespace {
19  

19  

20  
// Static database of common MIME types
20  
// Static database of common MIME types
21  
// Sorted by type for binary search
21  
// Sorted by type for binary search
22  
constexpr mime_type_entry db[] = {
22  
constexpr mime_type_entry db[] = {
23  
    { "application/gzip", "", false },
23  
    { "application/gzip", "", false },
24  
    { "application/javascript", "UTF-8", true },
24  
    { "application/javascript", "UTF-8", true },
25  
    { "application/json", "UTF-8", true },
25  
    { "application/json", "UTF-8", true },
26  
    { "application/octet-stream", "", false },
26  
    { "application/octet-stream", "", false },
27  
    { "application/pdf", "", false },
27  
    { "application/pdf", "", false },
28  
    { "application/rtf", "UTF-8", true },
28  
    { "application/rtf", "UTF-8", true },
29  
    { "application/wasm", "", false },
29  
    { "application/wasm", "", false },
30  
    { "application/x-7z-compressed", "", false },
30  
    { "application/x-7z-compressed", "", false },
31  
    { "application/x-bzip", "", false },
31  
    { "application/x-bzip", "", false },
32  
    { "application/x-bzip2", "", false },
32  
    { "application/x-bzip2", "", false },
33  
    { "application/x-tar", "", false },
33  
    { "application/x-tar", "", false },
34  
    { "application/xhtml+xml", "UTF-8", true },
34  
    { "application/xhtml+xml", "UTF-8", true },
35  
    { "application/xml", "UTF-8", true },
35  
    { "application/xml", "UTF-8", true },
36  
    { "application/zip", "", false },
36  
    { "application/zip", "", false },
37  
    { "audio/aac", "", false },
37  
    { "audio/aac", "", false },
38  
    { "audio/flac", "", false },
38  
    { "audio/flac", "", false },
39  
    { "audio/mp4", "", false },
39  
    { "audio/mp4", "", false },
40  
    { "audio/mpeg", "", false },
40  
    { "audio/mpeg", "", false },
41  
    { "audio/ogg", "", false },
41  
    { "audio/ogg", "", false },
42  
    { "audio/wav", "", false },
42  
    { "audio/wav", "", false },
43  
    { "audio/webm", "", false },
43  
    { "audio/webm", "", false },
44  
    { "font/otf", "", false },
44  
    { "font/otf", "", false },
45  
    { "font/ttf", "", false },
45  
    { "font/ttf", "", false },
46  
    { "font/woff", "", false },
46  
    { "font/woff", "", false },
47  
    { "font/woff2", "", false },
47  
    { "font/woff2", "", false },
48  
    { "image/avif", "", false },
48  
    { "image/avif", "", false },
49  
    { "image/bmp", "", false },
49  
    { "image/bmp", "", false },
50  
    { "image/gif", "", false },
50  
    { "image/gif", "", false },
51  
    { "image/jpeg", "", false },
51  
    { "image/jpeg", "", false },
52  
    { "image/png", "", false },
52  
    { "image/png", "", false },
53  
    { "image/svg+xml", "UTF-8", true },
53  
    { "image/svg+xml", "UTF-8", true },
54  
    { "image/tiff", "", false },
54  
    { "image/tiff", "", false },
55  
    { "image/webp", "", false },
55  
    { "image/webp", "", false },
56  
    { "image/x-icon", "", false },
56  
    { "image/x-icon", "", false },
57  
    { "text/cache-manifest", "UTF-8", true },
57  
    { "text/cache-manifest", "UTF-8", true },
58  
    { "text/calendar", "UTF-8", true },
58  
    { "text/calendar", "UTF-8", true },
59  
    { "text/css", "UTF-8", true },
59  
    { "text/css", "UTF-8", true },
60  
    { "text/csv", "UTF-8", true },
60  
    { "text/csv", "UTF-8", true },
61  
    { "text/html", "UTF-8", true },
61  
    { "text/html", "UTF-8", true },
62  
    { "text/javascript", "UTF-8", true },
62  
    { "text/javascript", "UTF-8", true },
63  
    { "text/markdown", "UTF-8", true },
63  
    { "text/markdown", "UTF-8", true },
64  
    { "text/plain", "UTF-8", true },
64  
    { "text/plain", "UTF-8", true },
65  
    { "text/xml", "UTF-8", true },
65  
    { "text/xml", "UTF-8", true },
66  
    { "video/mp4", "", false },
66  
    { "video/mp4", "", false },
67  
    { "video/mpeg", "", false },
67  
    { "video/mpeg", "", false },
68  
    { "video/ogg", "", false },
68  
    { "video/ogg", "", false },
69  
    { "video/webm", "", false },
69  
    { "video/webm", "", false },
70  
};
70  
};
71  

71  

72  
constexpr std::size_t db_size = sizeof( db ) / sizeof( db[0] );
72  
constexpr std::size_t db_size = sizeof( db ) / sizeof( db[0] );
73  

73  

74  
// Case-insensitive comparison
74  
// Case-insensitive comparison
75  
int
75  
int
76  
compare_icase( core::string_view a, core::string_view b ) noexcept
76  
compare_icase( core::string_view a, core::string_view b ) noexcept
77  
{
77  
{
78  
    auto const n = ( std::min )( a.size(), b.size() );
78  
    auto const n = ( std::min )( a.size(), b.size() );
79  
    for( std::size_t i = 0; i < n; ++i )
79  
    for( std::size_t i = 0; i < n; ++i )
80  
    {
80  
    {
81  
        auto const ca = static_cast<unsigned char>(
81  
        auto const ca = static_cast<unsigned char>(
82  
            std::tolower( static_cast<unsigned char>( a[i] ) ) );
82  
            std::tolower( static_cast<unsigned char>( a[i] ) ) );
83  
        auto const cb = static_cast<unsigned char>(
83  
        auto const cb = static_cast<unsigned char>(
84  
            std::tolower( static_cast<unsigned char>( b[i] ) ) );
84  
            std::tolower( static_cast<unsigned char>( b[i] ) ) );
85  
        if( ca < cb )
85  
        if( ca < cb )
86  
            return -1;
86  
            return -1;
87  
        if( ca > cb )
87  
        if( ca > cb )
88  
            return 1;
88  
            return 1;
89  
    }
89  
    }
90  
    if( a.size() < b.size() )
90  
    if( a.size() < b.size() )
91  
        return -1;
91  
        return -1;
92  
    if( a.size() > b.size() )
92  
    if( a.size() > b.size() )
93  
        return 1;
93  
        return 1;
94  
    return 0;
94  
    return 0;
95  
}
95  
}
96  

96  

97  
} // (anon)
97  
} // (anon)
98  

98  

99  
mime_type_entry const*
99  
mime_type_entry const*
100  
lookup( core::string_view type ) noexcept
100  
lookup( core::string_view type ) noexcept
101  
{
101  
{
102  
    // Binary search
102  
    // Binary search
103  
    std::size_t lo = 0;
103  
    std::size_t lo = 0;
104  
    std::size_t hi = db_size;
104  
    std::size_t hi = db_size;
105  
    while( lo < hi )
105  
    while( lo < hi )
106  
    {
106  
    {
107  
        auto const mid = lo + ( hi - lo ) / 2;
107  
        auto const mid = lo + ( hi - lo ) / 2;
108  
        auto const cmp = compare_icase( db[mid].type, type );
108  
        auto const cmp = compare_icase( db[mid].type, type );
109  
        if( cmp < 0 )
109  
        if( cmp < 0 )
110  
            lo = mid + 1;
110  
            lo = mid + 1;
111  
        else if( cmp > 0 )
111  
        else if( cmp > 0 )
112  
            hi = mid;
112  
            hi = mid;
113  
        else
113  
        else
114  
            return &db[mid];
114  
            return &db[mid];
115  
    }
115  
    }
116  
    return nullptr;
116  
    return nullptr;
117  
}
117  
}
118  

118  

119  
} // mime_db
119  
} // mime_db
120  
} // http
120  
} // http
121  
} // boost
121  
} // boost