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/accepts.hpp>
10  
#include <boost/http/server/accepts.hpp>
11  
#include <boost/http/server/mime_types.hpp>
11  
#include <boost/http/server/mime_types.hpp>
12  
#include <boost/http/field.hpp>
12  
#include <boost/http/field.hpp>
13  
#include <algorithm>
13  
#include <algorithm>
14  

14  

15  
namespace boost {
15  
namespace boost {
16  
namespace http {
16  
namespace http {
17  

17  

18  
namespace {
18  
namespace {
19  

19  

20  
//----------------------------------------------------------
20  
//----------------------------------------------------------
21  
// Helpers
21  
// Helpers
22  
//----------------------------------------------------------
22  
//----------------------------------------------------------
23  

23  

24  
std::string_view
24  
std::string_view
25  
trim_ows( std::string_view s ) noexcept
25  
trim_ows( std::string_view s ) noexcept
26  
{
26  
{
27  
    while( ! s.empty() &&
27  
    while( ! s.empty() &&
28  
        ( s.front() == ' ' || s.front() == '\t' ) )
28  
        ( s.front() == ' ' || s.front() == '\t' ) )
29  
        s.remove_prefix( 1 );
29  
        s.remove_prefix( 1 );
30  
    while( ! s.empty() &&
30  
    while( ! s.empty() &&
31  
        ( s.back() == ' ' || s.back() == '\t' ) )
31  
        ( s.back() == ' ' || s.back() == '\t' ) )
32  
        s.remove_suffix( 1 );
32  
        s.remove_suffix( 1 );
33  
    return s;
33  
    return s;
34  
}
34  
}
35  

35  

36  
bool
36  
bool
37  
iequals(
37  
iequals(
38  
    std::string_view a,
38  
    std::string_view a,
39  
    std::string_view b ) noexcept
39  
    std::string_view b ) noexcept
40  
{
40  
{
41  
    if( a.size() != b.size() )
41  
    if( a.size() != b.size() )
42  
        return false;
42  
        return false;
43  
    for( std::size_t i = 0; i < a.size(); ++i )
43  
    for( std::size_t i = 0; i < a.size(); ++i )
44  
    {
44  
    {
45  
        unsigned char ca = a[i];
45  
        unsigned char ca = a[i];
46  
        unsigned char cb = b[i];
46  
        unsigned char cb = b[i];
47  
        if( ca >= 'A' && ca <= 'Z' )
47  
        if( ca >= 'A' && ca <= 'Z' )
48  
            ca += 32;
48  
            ca += 32;
49  
        if( cb >= 'A' && cb <= 'Z' )
49  
        if( cb >= 'A' && cb <= 'Z' )
50  
            cb += 32;
50  
            cb += 32;
51  
        if( ca != cb )
51  
        if( ca != cb )
52  
            return false;
52  
            return false;
53  
    }
53  
    }
54  
    return true;
54  
    return true;
55  
}
55  
}
56  

56  

57  
// Returns quality as integer 0-1000
57  
// Returns quality as integer 0-1000
58  
int
58  
int
59  
parse_q( std::string_view s ) noexcept
59  
parse_q( std::string_view s ) noexcept
60  
{
60  
{
61  
    s = trim_ows( s );
61  
    s = trim_ows( s );
62  
    if( s.empty() )
62  
    if( s.empty() )
63  
        return 1000;
63  
        return 1000;
64  
    if( s[0] == '1' )
64  
    if( s[0] == '1' )
65  
        return 1000;
65  
        return 1000;
66  
    if( s[0] != '0' )
66  
    if( s[0] != '0' )
67  
        return 0;
67  
        return 0;
68  
    if( s.size() < 2 || s[1] != '.' )
68  
    if( s.size() < 2 || s[1] != '.' )
69  
        return 0;
69  
        return 0;
70  
    int result = 0;
70  
    int result = 0;
71  
    int mult = 100;
71  
    int mult = 100;
72  
    for( std::size_t i = 2;
72  
    for( std::size_t i = 2;
73  
        i < s.size() && i < 5; ++i )
73  
        i < s.size() && i < 5; ++i )
74  
    {
74  
    {
75  
        if( s[i] < '0' || s[i] > '9' )
75  
        if( s[i] < '0' || s[i] > '9' )
76  
            break;
76  
            break;
77  
        result += ( s[i] - '0' ) * mult;
77  
        result += ( s[i] - '0' ) * mult;
78  
        mult /= 10;
78  
        mult /= 10;
79  
    }
79  
    }
80  
    return result;
80  
    return result;
81  
}
81  
}
82  

82  

83  
// Extract q-value from parameters after first semicolon
83  
// Extract q-value from parameters after first semicolon
84  
int
84  
int
85  
extract_q( std::string_view params ) noexcept
85  
extract_q( std::string_view params ) noexcept
86  
{
86  
{
87  
    while( ! params.empty() )
87  
    while( ! params.empty() )
88  
    {
88  
    {
89  
        auto semi = params.find( ';' );
89  
        auto semi = params.find( ';' );
90  
        auto param = trim_ows(
90  
        auto param = trim_ows(
91  
            semi != std::string_view::npos
91  
            semi != std::string_view::npos
92  
                ? params.substr( 0, semi )
92  
                ? params.substr( 0, semi )
93  
                : params );
93  
                : params );
94  
        if( param.size() >= 2 &&
94  
        if( param.size() >= 2 &&
95  
            ( param[0] == 'q' || param[0] == 'Q' ) &&
95  
            ( param[0] == 'q' || param[0] == 'Q' ) &&
96  
            param[1] == '=' )
96  
            param[1] == '=' )
97  
        {
97  
        {
98  
            return parse_q( param.substr( 2 ) );
98  
            return parse_q( param.substr( 2 ) );
99  
        }
99  
        }
100  
        if( semi != std::string_view::npos )
100  
        if( semi != std::string_view::npos )
101  
            params.remove_prefix( semi + 1 );
101  
            params.remove_prefix( semi + 1 );
102  
        else
102  
        else
103  
            break;
103  
            break;
104  
    }
104  
    }
105  
    return 1000;
105  
    return 1000;
106  
}
106  
}
107  

107  

108  
//----------------------------------------------------------
108  
//----------------------------------------------------------
109  
// Negotiation priority
109  
// Negotiation priority
110  
//----------------------------------------------------------
110  
//----------------------------------------------------------
111  

111  

112  
struct priority
112  
struct priority
113  
{
113  
{
114  
    int q;
114  
    int q;
115  
    int specificity;
115  
    int specificity;
116  
    int order;
116  
    int order;
117  
};
117  
};
118  

118  

119  
bool
119  
bool
120  
is_better(
120  
is_better(
121  
    priority const& a,
121  
    priority const& a,
122  
    priority const& b ) noexcept
122  
    priority const& b ) noexcept
123  
{
123  
{
124  
    if( a.q != b.q )
124  
    if( a.q != b.q )
125  
        return a.q > b.q;
125  
        return a.q > b.q;
126  
    if( a.specificity != b.specificity )
126  
    if( a.specificity != b.specificity )
127  
        return a.specificity > b.specificity;
127  
        return a.specificity > b.specificity;
128  
    return a.order < b.order;
128  
    return a.order < b.order;
129  
}
129  
}
130  

130  

131  
//----------------------------------------------------------
131  
//----------------------------------------------------------
132  
// Media type parsing (Accept header)
132  
// Media type parsing (Accept header)
133  
//----------------------------------------------------------
133  
//----------------------------------------------------------
134  

134  

135  
struct media_range
135  
struct media_range
136  
{
136  
{
137  
    std::string_view type;
137  
    std::string_view type;
138  
    std::string_view subtype;
138  
    std::string_view subtype;
139  
    std::string_view full;
139  
    std::string_view full;
140  
    int q;
140  
    int q;
141  
    int order;
141  
    int order;
142  
};
142  
};
143  

143  

144  
std::vector<media_range>
144  
std::vector<media_range>
145  
parse_accept( std::string_view header )
145  
parse_accept( std::string_view header )
146  
{
146  
{
147  
    std::vector<media_range> result;
147  
    std::vector<media_range> result;
148  
    int order = 0;
148  
    int order = 0;
149  

149  

150  
    while( ! header.empty() )
150  
    while( ! header.empty() )
151  
    {
151  
    {
152  
        auto comma = header.find( ',' );
152  
        auto comma = header.find( ',' );
153  
        auto entry = ( comma != std::string_view::npos )
153  
        auto entry = ( comma != std::string_view::npos )
154  
            ? header.substr( 0, comma )
154  
            ? header.substr( 0, comma )
155  
            : header;
155  
            : header;
156  
        if( comma != std::string_view::npos )
156  
        if( comma != std::string_view::npos )
157  
            header.remove_prefix( comma + 1 );
157  
            header.remove_prefix( comma + 1 );
158  
        else
158  
        else
159  
            header = {};
159  
            header = {};
160  

160  

161  
        entry = trim_ows( entry );
161  
        entry = trim_ows( entry );
162  
        if( entry.empty() )
162  
        if( entry.empty() )
163  
            continue;
163  
            continue;
164  

164  

165  
        auto semi = entry.find( ';' );
165  
        auto semi = entry.find( ';' );
166  
        auto mime_part = trim_ows(
166  
        auto mime_part = trim_ows(
167  
            semi != std::string_view::npos
167  
            semi != std::string_view::npos
168  
                ? entry.substr( 0, semi )
168  
                ? entry.substr( 0, semi )
169  
                : entry );
169  
                : entry );
170  

170  

171  
        auto slash = mime_part.find( '/' );
171  
        auto slash = mime_part.find( '/' );
172  
        if( slash == std::string_view::npos )
172  
        if( slash == std::string_view::npos )
173  
            continue;
173  
            continue;
174  

174  

175  
        media_range mr;
175  
        media_range mr;
176  
        mr.type = mime_part.substr( 0, slash );
176  
        mr.type = mime_part.substr( 0, slash );
177  
        mr.subtype = mime_part.substr( slash + 1 );
177  
        mr.subtype = mime_part.substr( slash + 1 );
178  
        mr.full = mime_part;
178  
        mr.full = mime_part;
179  
        mr.q = ( semi != std::string_view::npos )
179  
        mr.q = ( semi != std::string_view::npos )
180  
            ? extract_q( entry.substr( semi + 1 ) )
180  
            ? extract_q( entry.substr( semi + 1 ) )
181  
            : 1000;
181  
            : 1000;
182  
        mr.order = order++;
182  
        mr.order = order++;
183  
        result.push_back( mr );
183  
        result.push_back( mr );
184  
    }
184  
    }
185  

185  

186  
    return result;
186  
    return result;
187  
}
187  
}
188  

188  

189  
// Returns specificity (0-6) or -1 for no match
189  
// Returns specificity (0-6) or -1 for no match
190  
int
190  
int
191  
match_media(
191  
match_media(
192  
    media_range const& range,
192  
    media_range const& range,
193  
    std::string_view type,
193  
    std::string_view type,
194  
    std::string_view subtype ) noexcept
194  
    std::string_view subtype ) noexcept
195  
{
195  
{
196  
    int s = 0;
196  
    int s = 0;
197  

197  

198  
    if( range.type == "*" )
198  
    if( range.type == "*" )
199  
    {
199  
    {
200  
        // wildcard type
200  
        // wildcard type
201  
    }
201  
    }
202  
    else if( iequals( range.type, type ) )
202  
    else if( iequals( range.type, type ) )
203  
    {
203  
    {
204  
        s |= 4;
204  
        s |= 4;
205  
    }
205  
    }
206  
    else
206  
    else
207  
    {
207  
    {
208  
        return -1;
208  
        return -1;
209  
    }
209  
    }
210  

210  

211  
    if( range.subtype == "*" )
211  
    if( range.subtype == "*" )
212  
    {
212  
    {
213  
        // wildcard subtype
213  
        // wildcard subtype
214  
    }
214  
    }
215  
    else if( iequals( range.subtype, subtype ) )
215  
    else if( iequals( range.subtype, subtype ) )
216  
    {
216  
    {
217  
        s |= 2;
217  
        s |= 2;
218  
    }
218  
    }
219  
    else
219  
    else
220  
    {
220  
    {
221  
        return -1;
221  
        return -1;
222  
    }
222  
    }
223  

223  

224  
    return s;
224  
    return s;
225  
}
225  
}
226  

226  

227  
//----------------------------------------------------------
227  
//----------------------------------------------------------
228  
// Simple token parsing (Accept-Encoding/Charset/Language)
228  
// Simple token parsing (Accept-Encoding/Charset/Language)
229  
//----------------------------------------------------------
229  
//----------------------------------------------------------
230  

230  

231  
struct simple_entry
231  
struct simple_entry
232  
{
232  
{
233  
    std::string_view value;
233  
    std::string_view value;
234  
    int q;
234  
    int q;
235  
    int order;
235  
    int order;
236  
};
236  
};
237  

237  

238  
std::vector<simple_entry>
238  
std::vector<simple_entry>
239  
parse_simple( std::string_view header )
239  
parse_simple( std::string_view header )
240  
{
240  
{
241  
    std::vector<simple_entry> result;
241  
    std::vector<simple_entry> result;
242  
    int order = 0;
242  
    int order = 0;
243  

243  

244  
    while( ! header.empty() )
244  
    while( ! header.empty() )
245  
    {
245  
    {
246  
        auto comma = header.find( ',' );
246  
        auto comma = header.find( ',' );
247  
        auto entry = ( comma != std::string_view::npos )
247  
        auto entry = ( comma != std::string_view::npos )
248  
            ? header.substr( 0, comma )
248  
            ? header.substr( 0, comma )
249  
            : header;
249  
            : header;
250  
        if( comma != std::string_view::npos )
250  
        if( comma != std::string_view::npos )
251  
            header.remove_prefix( comma + 1 );
251  
            header.remove_prefix( comma + 1 );
252  
        else
252  
        else
253  
            header = {};
253  
            header = {};
254  

254  

255  
        entry = trim_ows( entry );
255  
        entry = trim_ows( entry );
256  
        if( entry.empty() )
256  
        if( entry.empty() )
257  
            continue;
257  
            continue;
258  

258  

259  
        auto semi = entry.find( ';' );
259  
        auto semi = entry.find( ';' );
260  
        auto value = trim_ows(
260  
        auto value = trim_ows(
261  
            semi != std::string_view::npos
261  
            semi != std::string_view::npos
262  
                ? entry.substr( 0, semi )
262  
                ? entry.substr( 0, semi )
263  
                : entry );
263  
                : entry );
264  
        if( value.empty() )
264  
        if( value.empty() )
265  
            continue;
265  
            continue;
266  

266  

267  
        simple_entry se;
267  
        simple_entry se;
268  
        se.value = value;
268  
        se.value = value;
269  
        se.q = ( semi != std::string_view::npos )
269  
        se.q = ( semi != std::string_view::npos )
270  
            ? extract_q( entry.substr( semi + 1 ) )
270  
            ? extract_q( entry.substr( semi + 1 ) )
271  
            : 1000;
271  
            : 1000;
272  
        se.order = order++;
272  
        se.order = order++;
273  
        result.push_back( se );
273  
        result.push_back( se );
274  
    }
274  
    }
275  

275  

276  
    return result;
276  
    return result;
277  
}
277  
}
278  

278  

279  
//----------------------------------------------------------
279  
//----------------------------------------------------------
280  
// Matching helpers
280  
// Matching helpers
281  
//----------------------------------------------------------
281  
//----------------------------------------------------------
282  

282  

283  
// Exact or wildcard match (encoding, charset)
283  
// Exact or wildcard match (encoding, charset)
284  
int
284  
int
285  
match_exact(
285  
match_exact(
286  
    std::string_view spec,
286  
    std::string_view spec,
287  
    std::string_view offered ) noexcept
287  
    std::string_view offered ) noexcept
288  
{
288  
{
289  
    if( iequals( spec, offered ) )
289  
    if( iequals( spec, offered ) )
290  
        return 1;
290  
        return 1;
291  
    if( spec == "*" )
291  
    if( spec == "*" )
292  
        return 0;
292  
        return 0;
293  
    return -1;
293  
    return -1;
294  
}
294  
}
295  

295  

296  
// Language prefix: "en-US" -> "en"
296  
// Language prefix: "en-US" -> "en"
297  
std::string_view
297  
std::string_view
298  
lang_prefix( std::string_view tag ) noexcept
298  
lang_prefix( std::string_view tag ) noexcept
299  
{
299  
{
300  
    auto dash = tag.find( '-' );
300  
    auto dash = tag.find( '-' );
301  
    if( dash != std::string_view::npos )
301  
    if( dash != std::string_view::npos )
302  
        return tag.substr( 0, dash );
302  
        return tag.substr( 0, dash );
303  
    return tag;
303  
    return tag;
304  
}
304  
}
305  

305  

306  
// Language match with prefix support
306  
// Language match with prefix support
307  
int
307  
int
308  
match_language(
308  
match_language(
309  
    std::string_view spec,
309  
    std::string_view spec,
310  
    std::string_view offered ) noexcept
310  
    std::string_view offered ) noexcept
311  
{
311  
{
312  
    if( iequals( spec, offered ) )
312  
    if( iequals( spec, offered ) )
313  
        return 4;
313  
        return 4;
314  
    if( iequals( lang_prefix( spec ), offered ) )
314  
    if( iequals( lang_prefix( spec ), offered ) )
315  
        return 2;
315  
        return 2;
316  
    if( iequals( spec, lang_prefix( offered ) ) )
316  
    if( iequals( spec, lang_prefix( offered ) ) )
317  
        return 1;
317  
        return 1;
318  
    if( spec == "*" )
318  
    if( spec == "*" )
319  
        return 0;
319  
        return 0;
320  
    return -1;
320  
    return -1;
321  
}
321  
}
322  

322  

323  
//----------------------------------------------------------
323  
//----------------------------------------------------------
324  
// Generic negotiation for simple headers
324  
// Generic negotiation for simple headers
325  
//----------------------------------------------------------
325  
//----------------------------------------------------------
326  

326  

327  
template< class MatchFn >
327  
template< class MatchFn >
328  
std::string_view
328  
std::string_view
329  
negotiate(
329  
negotiate(
330  
    std::vector<simple_entry> const& entries,
330  
    std::vector<simple_entry> const& entries,
331  
    std::initializer_list<std::string_view> offered,
331  
    std::initializer_list<std::string_view> offered,
332  
    MatchFn match )
332  
    MatchFn match )
333  
{
333  
{
334  
    std::string_view best_val;
334  
    std::string_view best_val;
335  
    priority best_pri{ -1, -1, 0 };
335  
    priority best_pri{ -1, -1, 0 };
336  
    bool found = false;
336  
    bool found = false;
337  

337  

338  
    for( auto const& o : offered )
338  
    for( auto const& o : offered )
339  
    {
339  
    {
340  
        priority pri{ -1, -1, 0 };
340  
        priority pri{ -1, -1, 0 };
341  
        bool matched = false;
341  
        bool matched = false;
342  

342  

343  
        for( auto const& e : entries )
343  
        for( auto const& e : entries )
344  
        {
344  
        {
345  
            if( e.q <= 0 )
345  
            if( e.q <= 0 )
346  
                continue;
346  
                continue;
347  
            auto s = match( e.value, o );
347  
            auto s = match( e.value, o );
348  
            if( s < 0 )
348  
            if( s < 0 )
349  
                continue;
349  
                continue;
350  
            priority p{ e.q, s, e.order };
350  
            priority p{ e.q, s, e.order };
351  
            if( ! matched ||
351  
            if( ! matched ||
352  
                p.specificity > pri.specificity ||
352  
                p.specificity > pri.specificity ||
353  
                ( p.specificity == pri.specificity &&
353  
                ( p.specificity == pri.specificity &&
354  
                    p.q > pri.q ) ||
354  
                    p.q > pri.q ) ||
355  
                ( p.specificity == pri.specificity &&
355  
                ( p.specificity == pri.specificity &&
356  
                    p.q == pri.q &&
356  
                    p.q == pri.q &&
357  
                    p.order < pri.order ) )
357  
                    p.order < pri.order ) )
358  
            {
358  
            {
359  
                pri = p;
359  
                pri = p;
360  
                matched = true;
360  
                matched = true;
361  
            }
361  
            }
362  
        }
362  
        }
363  

363  

364  
        if( ! matched || pri.q <= 0 )
364  
        if( ! matched || pri.q <= 0 )
365  
            continue;
365  
            continue;
366  

366  

367  
        if( ! found || is_better( pri, best_pri ) )
367  
        if( ! found || is_better( pri, best_pri ) )
368  
        {
368  
        {
369  
            best_val = o;
369  
            best_val = o;
370  
            best_pri = pri;
370  
            best_pri = pri;
371  
            found = true;
371  
            found = true;
372  
        }
372  
        }
373  
    }
373  
    }
374  

374  

375  
    return found ? best_val : std::string_view{};
375  
    return found ? best_val : std::string_view{};
376  
}
376  
}
377  

377  

378  
// Return sorted values from simple entries
378  
// Return sorted values from simple entries
379  
std::vector<std::string_view>
379  
std::vector<std::string_view>
380  
sorted_values(
380  
sorted_values(
381  
    std::vector<simple_entry>& entries )
381  
    std::vector<simple_entry>& entries )
382  
{
382  
{
383  
    std::sort( entries.begin(), entries.end(),
383  
    std::sort( entries.begin(), entries.end(),
384  
        []( simple_entry const& a,
384  
        []( simple_entry const& a,
385  
            simple_entry const& b )
385  
            simple_entry const& b )
386  
        {
386  
        {
387  
            if( a.q != b.q )
387  
            if( a.q != b.q )
388  
                return a.q > b.q;
388  
                return a.q > b.q;
389  
            return a.order < b.order;
389  
            return a.order < b.order;
390  
        });
390  
        });
391  

391  

392  
    std::vector<std::string_view> result;
392  
    std::vector<std::string_view> result;
393  
    result.reserve( entries.size() );
393  
    result.reserve( entries.size() );
394  
    for( auto const& e : entries )
394  
    for( auto const& e : entries )
395  
    {
395  
    {
396  
        if( e.q <= 0 )
396  
        if( e.q <= 0 )
397  
            continue;
397  
            continue;
398  
        result.push_back( e.value );
398  
        result.push_back( e.value );
399  
    }
399  
    }
400  
    return result;
400  
    return result;
401  
}
401  
}
402  

402  

403  
} // (anon)
403  
} // (anon)
404  

404  

405  
//----------------------------------------------------------
405  
//----------------------------------------------------------
406  

406  

407  
accepts::accepts(
407  
accepts::accepts(
408  
    fields_base const& fields ) noexcept
408  
    fields_base const& fields ) noexcept
409  
    : fields_( fields )
409  
    : fields_( fields )
410  
{
410  
{
411  
}
411  
}
412  

412  

413  
std::string_view
413  
std::string_view
414  
accepts::type(
414  
accepts::type(
415  
    std::initializer_list<
415  
    std::initializer_list<
416  
        std::string_view> offered ) const
416  
        std::string_view> offered ) const
417  
{
417  
{
418  
    if( offered.size() == 0 )
418  
    if( offered.size() == 0 )
419  
        return {};
419  
        return {};
420  

420  

421  
    auto accept = fields_.value_or(
421  
    auto accept = fields_.value_or(
422  
        field::accept, "" );
422  
        field::accept, "" );
423  

423  

424  
    if( accept.empty() )
424  
    if( accept.empty() )
425  
        return *offered.begin();
425  
        return *offered.begin();
426  

426  

427  
    auto ranges = parse_accept( accept );
427  
    auto ranges = parse_accept( accept );
428  
    if( ranges.empty() )
428  
    if( ranges.empty() )
429  
        return *offered.begin();
429  
        return *offered.begin();
430  

430  

431  
    std::string_view best_val;
431  
    std::string_view best_val;
432  
    priority best_pri{ -1, -1, 0 };
432  
    priority best_pri{ -1, -1, 0 };
433  
    bool found = false;
433  
    bool found = false;
434  

434  

435  
    for( auto const& o : offered )
435  
    for( auto const& o : offered )
436  
    {
436  
    {
437  
        // Convert extension to MIME if needed
437  
        // Convert extension to MIME if needed
438  
        std::string_view mime_str = o;
438  
        std::string_view mime_str = o;
439  
        if( o.find( '/' ) == std::string_view::npos )
439  
        if( o.find( '/' ) == std::string_view::npos )
440  
        {
440  
        {
441  
            auto looked = mime_types::lookup( o );
441  
            auto looked = mime_types::lookup( o );
442  
            if( ! looked.empty() )
442  
            if( ! looked.empty() )
443  
                mime_str = looked;
443  
                mime_str = looked;
444  
            else
444  
            else
445  
                continue;
445  
                continue;
446  
        }
446  
        }
447  

447  

448  
        auto slash = mime_str.find( '/' );
448  
        auto slash = mime_str.find( '/' );
449  
        if( slash == std::string_view::npos )
449  
        if( slash == std::string_view::npos )
450  
            continue;
450  
            continue;
451  

451  

452  
        auto type = mime_str.substr( 0, slash );
452  
        auto type = mime_str.substr( 0, slash );
453  
        auto subtype = mime_str.substr( slash + 1 );
453  
        auto subtype = mime_str.substr( slash + 1 );
454  

454  

455  
        // Find best matching range for this type
455  
        // Find best matching range for this type
456  
        priority pri{ -1, -1, 0 };
456  
        priority pri{ -1, -1, 0 };
457  
        bool matched = false;
457  
        bool matched = false;
458  

458  

459  
        for( auto const& r : ranges )
459  
        for( auto const& r : ranges )
460  
        {
460  
        {
461  
            if( r.q <= 0 )
461  
            if( r.q <= 0 )
462  
                continue;
462  
                continue;
463  
            auto s = match_media( r, type, subtype );
463  
            auto s = match_media( r, type, subtype );
464  
            if( s < 0 )
464  
            if( s < 0 )
465  
                continue;
465  
                continue;
466  
            priority p{ r.q, s, r.order };
466  
            priority p{ r.q, s, r.order };
467  
            if( ! matched ||
467  
            if( ! matched ||
468  
                p.specificity > pri.specificity ||
468  
                p.specificity > pri.specificity ||
469  
                ( p.specificity == pri.specificity &&
469  
                ( p.specificity == pri.specificity &&
470  
                    p.q > pri.q ) ||
470  
                    p.q > pri.q ) ||
471  
                ( p.specificity == pri.specificity &&
471  
                ( p.specificity == pri.specificity &&
472  
                    p.q == pri.q &&
472  
                    p.q == pri.q &&
473  
                    p.order < pri.order ) )
473  
                    p.order < pri.order ) )
474  
            {
474  
            {
475  
                pri = p;
475  
                pri = p;
476  
                matched = true;
476  
                matched = true;
477  
            }
477  
            }
478  
        }
478  
        }
479  

479  

480  
        if( ! matched || pri.q <= 0 )
480  
        if( ! matched || pri.q <= 0 )
481  
            continue;
481  
            continue;
482  

482  

483  
        if( ! found || is_better( pri, best_pri ) )
483  
        if( ! found || is_better( pri, best_pri ) )
484  
        {
484  
        {
485  
            best_val = o;
485  
            best_val = o;
486  
            best_pri = pri;
486  
            best_pri = pri;
487  
            found = true;
487  
            found = true;
488  
        }
488  
        }
489  
    }
489  
    }
490  

490  

491  
    return found ? best_val : std::string_view{};
491  
    return found ? best_val : std::string_view{};
492  
}
492  
}
493  

493  

494  
std::vector<std::string_view>
494  
std::vector<std::string_view>
495  
accepts::types() const
495  
accepts::types() const
496  
{
496  
{
497  
    auto accept = fields_.value_or(
497  
    auto accept = fields_.value_or(
498  
        field::accept, "" );
498  
        field::accept, "" );
499  
    if( accept.empty() )
499  
    if( accept.empty() )
500  
        return {};
500  
        return {};
501  

501  

502  
    auto ranges = parse_accept( accept );
502  
    auto ranges = parse_accept( accept );
503  

503  

504  
    std::sort( ranges.begin(), ranges.end(),
504  
    std::sort( ranges.begin(), ranges.end(),
505  
        []( media_range const& a,
505  
        []( media_range const& a,
506  
            media_range const& b )
506  
            media_range const& b )
507  
        {
507  
        {
508  
            if( a.q != b.q )
508  
            if( a.q != b.q )
509  
                return a.q > b.q;
509  
                return a.q > b.q;
510  
            return a.order < b.order;
510  
            return a.order < b.order;
511  
        });
511  
        });
512  

512  

513  
    std::vector<std::string_view> result;
513  
    std::vector<std::string_view> result;
514  
    result.reserve( ranges.size() );
514  
    result.reserve( ranges.size() );
515  
    for( auto const& r : ranges )
515  
    for( auto const& r : ranges )
516  
    {
516  
    {
517  
        if( r.q <= 0 )
517  
        if( r.q <= 0 )
518  
            continue;
518  
            continue;
519  
        result.push_back( r.full );
519  
        result.push_back( r.full );
520  
    }
520  
    }
521  
    return result;
521  
    return result;
522  
}
522  
}
523  

523  

524  
std::string_view
524  
std::string_view
525  
accepts::encoding(
525  
accepts::encoding(
526  
    std::initializer_list<
526  
    std::initializer_list<
527  
        std::string_view> offered ) const
527  
        std::string_view> offered ) const
528  
{
528  
{
529  
    if( offered.size() == 0 )
529  
    if( offered.size() == 0 )
530  
        return {};
530  
        return {};
531  

531  

532  
    auto header = fields_.value_or(
532  
    auto header = fields_.value_or(
533  
        field::accept_encoding, "" );
533  
        field::accept_encoding, "" );
534  

534  

535  
    if( header.empty() )
535  
    if( header.empty() )
536  
        return *offered.begin();
536  
        return *offered.begin();
537  

537  

538  
    auto entries = parse_simple( header );
538  
    auto entries = parse_simple( header );
539  
    if( entries.empty() )
539  
    if( entries.empty() )
540  
        return *offered.begin();
540  
        return *offered.begin();
541  

541  

542  
    return negotiate( entries, offered, match_exact );
542  
    return negotiate( entries, offered, match_exact );
543  
}
543  
}
544  

544  

545  
std::vector<std::string_view>
545  
std::vector<std::string_view>
546  
accepts::encodings() const
546  
accepts::encodings() const
547  
{
547  
{
548  
    auto header = fields_.value_or(
548  
    auto header = fields_.value_or(
549  
        field::accept_encoding, "" );
549  
        field::accept_encoding, "" );
550  
    if( header.empty() )
550  
    if( header.empty() )
551  
        return {};
551  
        return {};
552  

552  

553  
    auto entries = parse_simple( header );
553  
    auto entries = parse_simple( header );
554  
    return sorted_values( entries );
554  
    return sorted_values( entries );
555  
}
555  
}
556  

556  

557  
std::string_view
557  
std::string_view
558  
accepts::charset(
558  
accepts::charset(
559  
    std::initializer_list<
559  
    std::initializer_list<
560  
        std::string_view> offered ) const
560  
        std::string_view> offered ) const
561  
{
561  
{
562  
    if( offered.size() == 0 )
562  
    if( offered.size() == 0 )
563  
        return {};
563  
        return {};
564  

564  

565  
    auto header = fields_.value_or(
565  
    auto header = fields_.value_or(
566  
        field::accept_charset, "" );
566  
        field::accept_charset, "" );
567  

567  

568  
    if( header.empty() )
568  
    if( header.empty() )
569  
        return *offered.begin();
569  
        return *offered.begin();
570  

570  

571  
    auto entries = parse_simple( header );
571  
    auto entries = parse_simple( header );
572  
    if( entries.empty() )
572  
    if( entries.empty() )
573  
        return *offered.begin();
573  
        return *offered.begin();
574  

574  

575  
    return negotiate( entries, offered, match_exact );
575  
    return negotiate( entries, offered, match_exact );
576  
}
576  
}
577  

577  

578  
std::vector<std::string_view>
578  
std::vector<std::string_view>
579  
accepts::charsets() const
579  
accepts::charsets() const
580  
{
580  
{
581  
    auto header = fields_.value_or(
581  
    auto header = fields_.value_or(
582  
        field::accept_charset, "" );
582  
        field::accept_charset, "" );
583  
    if( header.empty() )
583  
    if( header.empty() )
584  
        return {};
584  
        return {};
585  

585  

586  
    auto entries = parse_simple( header );
586  
    auto entries = parse_simple( header );
587  
    return sorted_values( entries );
587  
    return sorted_values( entries );
588  
}
588  
}
589  

589  

590  
std::string_view
590  
std::string_view
591  
accepts::language(
591  
accepts::language(
592  
    std::initializer_list<
592  
    std::initializer_list<
593  
        std::string_view> offered ) const
593  
        std::string_view> offered ) const
594  
{
594  
{
595  
    if( offered.size() == 0 )
595  
    if( offered.size() == 0 )
596  
        return {};
596  
        return {};
597  

597  

598  
    auto header = fields_.value_or(
598  
    auto header = fields_.value_or(
599  
        field::accept_language, "" );
599  
        field::accept_language, "" );
600  

600  

601  
    if( header.empty() )
601  
    if( header.empty() )
602  
        return *offered.begin();
602  
        return *offered.begin();
603  

603  

604  
    auto entries = parse_simple( header );
604  
    auto entries = parse_simple( header );
605  
    if( entries.empty() )
605  
    if( entries.empty() )
606  
        return *offered.begin();
606  
        return *offered.begin();
607  

607  

608  
    return negotiate( entries, offered, match_language );
608  
    return negotiate( entries, offered, match_language );
609  
}
609  
}
610  

610  

611  
std::vector<std::string_view>
611  
std::vector<std::string_view>
612  
accepts::languages() const
612  
accepts::languages() const
613  
{
613  
{
614  
    auto header = fields_.value_or(
614  
    auto header = fields_.value_or(
615  
        field::accept_language, "" );
615  
        field::accept_language, "" );
616  
    if( header.empty() )
616  
    if( header.empty() )
617  
        return {};
617  
        return {};
618  

618  

619  
    auto entries = parse_simple( header );
619  
    auto entries = parse_simple( header );
620  
    return sorted_values( entries );
620  
    return sorted_values( entries );
621  
}
621  
}
622  

622  

623  
} // http
623  
} // http
624  
} // boost
624  
} // boost