libs/http/src/server/accepts.cpp
86.1% Lines (272/316)
100.0% Functions (24/24)
71.9% Branches (194/270)
libs/http/src/server/accepts.cpp
| Line | Branch | Hits | Source Code |
|---|---|---|---|
| 1 | // | ||
| 2 | // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) | ||
| 3 | // | ||
| 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) | ||
| 6 | // | ||
| 7 | // Official repository: https://github.com/cppalliance/http | ||
| 8 | // | ||
| 9 | |||
| 10 | #include <boost/http/server/accepts.hpp> | ||
| 11 | #include <boost/http/server/mime_types.hpp> | ||
| 12 | #include <boost/http/field.hpp> | ||
| 13 | #include <algorithm> | ||
| 14 | |||
| 15 | namespace boost { | ||
| 16 | namespace http { | ||
| 17 | |||
| 18 | namespace { | ||
| 19 | |||
| 20 | //---------------------------------------------------------- | ||
| 21 | // Helpers | ||
| 22 | //---------------------------------------------------------- | ||
| 23 | |||
| 24 | std::string_view | ||
| 25 | 142 | trim_ows( std::string_view s ) noexcept | |
| 26 | { | ||
| 27 |
3/4✓ Branch 1 taken 169 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 27 times.
✓ Branch 4 taken 142 times.
|
338 | while( ! s.empty() && |
| 28 |
3/4✓ Branch 1 taken 142 times.
✓ Branch 2 taken 27 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 142 times.
|
169 | ( s.front() == ' ' || s.front() == '\t' ) ) |
| 29 | 27 | s.remove_prefix( 1 ); | |
| 30 |
2/4✓ Branch 1 taken 142 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 142 times.
|
284 | while( ! s.empty() && |
| 31 |
2/4✓ Branch 1 taken 142 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 142 times.
|
142 | ( s.back() == ' ' || s.back() == '\t' ) ) |
| 32 | ✗ | s.remove_suffix( 1 ); | |
| 33 | 142 | return s; | |
| 34 | } | ||
| 35 | |||
| 36 | bool | ||
| 37 | 75 | iequals( | |
| 38 | std::string_view a, | ||
| 39 | std::string_view b ) noexcept | ||
| 40 | { | ||
| 41 |
2/2✓ Branch 2 taken 26 times.
✓ Branch 3 taken 49 times.
|
75 | if( a.size() != b.size() ) |
| 42 | 26 | return false; | |
| 43 |
2/2✓ Branch 1 taken 174 times.
✓ Branch 2 taken 28 times.
|
202 | for( std::size_t i = 0; i < a.size(); ++i ) |
| 44 | { | ||
| 45 | 174 | unsigned char ca = a[i]; | |
| 46 | 174 | unsigned char cb = b[i]; | |
| 47 |
3/4✓ Branch 0 taken 155 times.
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 155 times.
|
174 | if( ca >= 'A' && ca <= 'Z' ) |
| 48 | ✗ | ca += 32; | |
| 49 |
3/4✓ Branch 0 taken 155 times.
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 155 times.
|
174 | if( cb >= 'A' && cb <= 'Z' ) |
| 50 | ✗ | cb += 32; | |
| 51 |
2/2✓ Branch 0 taken 21 times.
✓ Branch 1 taken 153 times.
|
174 | if( ca != cb ) |
| 52 | 21 | return false; | |
| 53 | } | ||
| 54 | 28 | return true; | |
| 55 | } | ||
| 56 | |||
| 57 | // Returns quality as integer 0-1000 | ||
| 58 | int | ||
| 59 | 16 | parse_q( std::string_view s ) noexcept | |
| 60 | { | ||
| 61 | 16 | s = trim_ows( s ); | |
| 62 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
|
16 | if( s.empty() ) |
| 63 | ✗ | return 1000; | |
| 64 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
|
16 | if( s[0] == '1' ) |
| 65 | ✗ | return 1000; | |
| 66 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 15 times.
|
16 | if( s[0] != '0' ) |
| 67 | 1 | return 0; | |
| 68 |
5/6✓ Branch 1 taken 14 times.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 14 times.
|
15 | if( s.size() < 2 || s[1] != '.' ) |
| 69 | 1 | return 0; | |
| 70 | 14 | int result = 0; | |
| 71 | 14 | int mult = 100; | |
| 72 | 14 | for( std::size_t i = 2; | |
| 73 |
5/6✓ Branch 1 taken 14 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
✓ Branch 6 taken 14 times.
|
28 | i < s.size() && i < 5; ++i ) |
| 74 | { | ||
| 75 |
3/6✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 14 times.
|
14 | if( s[i] < '0' || s[i] > '9' ) |
| 76 | ✗ | break; | |
| 77 | 14 | result += ( s[i] - '0' ) * mult; | |
| 78 | 14 | mult /= 10; | |
| 79 | } | ||
| 80 | 14 | return result; | |
| 81 | } | ||
| 82 | |||
| 83 | // Extract q-value from parameters after first semicolon | ||
| 84 | int | ||
| 85 | 16 | extract_q( std::string_view params ) noexcept | |
| 86 | { | ||
| 87 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | while( ! params.empty() ) |
| 88 | { | ||
| 89 | 16 | auto semi = params.find( ';' ); | |
| 90 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | auto param = trim_ows( |
| 91 | semi != std::string_view::npos | ||
| 92 | ✗ | ? params.substr( 0, semi ) | |
| 93 | : params ); | ||
| 94 | 16 | if( param.size() >= 2 && | |
| 95 |
3/8✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 16 times.
✗ Branch 9 not taken.
|
32 | ( param[0] == 'q' || param[0] == 'Q' ) && |
| 96 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | param[1] == '=' ) |
| 97 | { | ||
| 98 | 16 | return parse_q( param.substr( 2 ) ); | |
| 99 | } | ||
| 100 | ✗ | if( semi != std::string_view::npos ) | |
| 101 | ✗ | params.remove_prefix( semi + 1 ); | |
| 102 | else | ||
| 103 | ✗ | break; | |
| 104 | } | ||
| 105 | ✗ | return 1000; | |
| 106 | } | ||
| 107 | |||
| 108 | //---------------------------------------------------------- | ||
| 109 | // Negotiation priority | ||
| 110 | //---------------------------------------------------------- | ||
| 111 | |||
| 112 | struct priority | ||
| 113 | { | ||
| 114 | int q; | ||
| 115 | int specificity; | ||
| 116 | int order; | ||
| 117 | }; | ||
| 118 | |||
| 119 | bool | ||
| 120 | 6 | is_better( | |
| 121 | priority const& a, | ||
| 122 | priority const& b ) noexcept | ||
| 123 | { | ||
| 124 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
|
6 | if( a.q != b.q ) |
| 125 | 4 | return a.q > b.q; | |
| 126 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
|
2 | if( a.specificity != b.specificity ) |
| 127 | 1 | return a.specificity > b.specificity; | |
| 128 | 1 | return a.order < b.order; | |
| 129 | } | ||
| 130 | |||
| 131 | //---------------------------------------------------------- | ||
| 132 | // Media type parsing (Accept header) | ||
| 133 | //---------------------------------------------------------- | ||
| 134 | |||
| 135 | struct media_range | ||
| 136 | { | ||
| 137 | std::string_view type; | ||
| 138 | std::string_view subtype; | ||
| 139 | std::string_view full; | ||
| 140 | int q; | ||
| 141 | int order; | ||
| 142 | }; | ||
| 143 | |||
| 144 | std::vector<media_range> | ||
| 145 | 13 | parse_accept( std::string_view header ) | |
| 146 | { | ||
| 147 | 13 | std::vector<media_range> result; | |
| 148 | 13 | int order = 0; | |
| 149 | |||
| 150 |
2/2✓ Branch 1 taken 22 times.
✓ Branch 2 taken 13 times.
|
35 | while( ! header.empty() ) |
| 151 | { | ||
| 152 | 22 | auto comma = header.find( ',' ); | |
| 153 | auto entry = ( comma != std::string_view::npos ) | ||
| 154 |
3/3✓ Branch 0 taken 9 times.
✓ Branch 1 taken 13 times.
✓ Branch 3 taken 9 times.
|
22 | ? header.substr( 0, comma ) |
| 155 | 13 | : header; | |
| 156 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 13 times.
|
22 | if( comma != std::string_view::npos ) |
| 157 | 9 | header.remove_prefix( comma + 1 ); | |
| 158 | else | ||
| 159 | 13 | header = {}; | |
| 160 | |||
| 161 | 22 | entry = trim_ows( entry ); | |
| 162 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
|
22 | if( entry.empty() ) |
| 163 | ✗ | continue; | |
| 164 | |||
| 165 | 22 | auto semi = entry.find( ';' ); | |
| 166 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
|
26 | auto mime_part = trim_ows( |
| 167 | semi != std::string_view::npos | ||
| 168 |
1/1✓ Branch 1 taken 4 times.
|
4 | ? entry.substr( 0, semi ) |
| 169 | : entry ); | ||
| 170 | |||
| 171 | 22 | auto slash = mime_part.find( '/' ); | |
| 172 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
|
22 | if( slash == std::string_view::npos ) |
| 173 | ✗ | continue; | |
| 174 | |||
| 175 | 22 | media_range mr; | |
| 176 |
1/1✓ Branch 1 taken 22 times.
|
22 | mr.type = mime_part.substr( 0, slash ); |
| 177 |
1/1✓ Branch 1 taken 22 times.
|
22 | mr.subtype = mime_part.substr( slash + 1 ); |
| 178 | 22 | mr.full = mime_part; | |
| 179 | 22 | mr.q = ( semi != std::string_view::npos ) | |
| 180 |
3/3✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
✓ Branch 3 taken 4 times.
|
22 | ? extract_q( entry.substr( semi + 1 ) ) |
| 181 | : 1000; | ||
| 182 | 22 | mr.order = order++; | |
| 183 |
1/1✓ Branch 1 taken 22 times.
|
22 | result.push_back( mr ); |
| 184 | } | ||
| 185 | |||
| 186 | 13 | return result; | |
| 187 | ✗ | } | |
| 188 | |||
| 189 | // Returns specificity (0-6) or -1 for no match | ||
| 190 | int | ||
| 191 | 16 | match_media( | |
| 192 | media_range const& range, | ||
| 193 | std::string_view type, | ||
| 194 | std::string_view subtype ) noexcept | ||
| 195 | { | ||
| 196 | 16 | int s = 0; | |
| 197 | |||
| 198 |
2/2✓ Branch 2 taken 15 times.
✓ Branch 3 taken 1 time.
|
16 | if( range.type == "*" ) |
| 199 | { | ||
| 200 | // wildcard type | ||
| 201 | } | ||
| 202 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 2 taken 8 times.
|
15 | else if( iequals( range.type, type ) ) |
| 203 | { | ||
| 204 | 7 | s |= 4; | |
| 205 | } | ||
| 206 | else | ||
| 207 | { | ||
| 208 | 8 | return -1; | |
| 209 | } | ||
| 210 | |||
| 211 |
2/2✓ Branch 2 taken 5 times.
✓ Branch 3 taken 3 times.
|
8 | if( range.subtype == "*" ) |
| 212 | { | ||
| 213 | // wildcard subtype | ||
| 214 | } | ||
| 215 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | else if( iequals( range.subtype, subtype ) ) |
| 216 | { | ||
| 217 | 5 | s |= 2; | |
| 218 | } | ||
| 219 | else | ||
| 220 | { | ||
| 221 | ✗ | return -1; | |
| 222 | } | ||
| 223 | |||
| 224 | 8 | return s; | |
| 225 | } | ||
| 226 | |||
| 227 | //---------------------------------------------------------- | ||
| 228 | // Simple token parsing (Accept-Encoding/Charset/Language) | ||
| 229 | //---------------------------------------------------------- | ||
| 230 | |||
| 231 | struct simple_entry | ||
| 232 | { | ||
| 233 | std::string_view value; | ||
| 234 | int q; | ||
| 235 | int order; | ||
| 236 | }; | ||
| 237 | |||
| 238 | std::vector<simple_entry> | ||
| 239 | 15 | parse_simple( std::string_view header ) | |
| 240 | { | ||
| 241 | 15 | std::vector<simple_entry> result; | |
| 242 | 15 | int order = 0; | |
| 243 | |||
| 244 |
2/2✓ Branch 1 taken 33 times.
✓ Branch 2 taken 15 times.
|
48 | while( ! header.empty() ) |
| 245 | { | ||
| 246 | 33 | auto comma = header.find( ',' ); | |
| 247 | auto entry = ( comma != std::string_view::npos ) | ||
| 248 |
3/3✓ Branch 0 taken 18 times.
✓ Branch 1 taken 15 times.
✓ Branch 3 taken 18 times.
|
33 | ? header.substr( 0, comma ) |
| 249 | 15 | : header; | |
| 250 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 15 times.
|
33 | if( comma != std::string_view::npos ) |
| 251 | 18 | header.remove_prefix( comma + 1 ); | |
| 252 | else | ||
| 253 | 15 | header = {}; | |
| 254 | |||
| 255 | 33 | entry = trim_ows( entry ); | |
| 256 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
|
33 | if( entry.empty() ) |
| 257 | ✗ | continue; | |
| 258 | |||
| 259 | 33 | auto semi = entry.find( ';' ); | |
| 260 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 21 times.
|
45 | auto value = trim_ows( |
| 261 | semi != std::string_view::npos | ||
| 262 |
1/1✓ Branch 1 taken 12 times.
|
12 | ? entry.substr( 0, semi ) |
| 263 | : entry ); | ||
| 264 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
|
33 | if( value.empty() ) |
| 265 | ✗ | continue; | |
| 266 | |||
| 267 | 33 | simple_entry se; | |
| 268 | 33 | se.value = value; | |
| 269 | 33 | se.q = ( semi != std::string_view::npos ) | |
| 270 |
3/3✓ Branch 0 taken 12 times.
✓ Branch 1 taken 21 times.
✓ Branch 3 taken 12 times.
|
33 | ? extract_q( entry.substr( semi + 1 ) ) |
| 271 | : 1000; | ||
| 272 | 33 | se.order = order++; | |
| 273 |
1/1✓ Branch 1 taken 33 times.
|
33 | result.push_back( se ); |
| 274 | } | ||
| 275 | |||
| 276 | 15 | return result; | |
| 277 | ✗ | } | |
| 278 | |||
| 279 | //---------------------------------------------------------- | ||
| 280 | // Matching helpers | ||
| 281 | //---------------------------------------------------------- | ||
| 282 | |||
| 283 | // Exact or wildcard match (encoding, charset) | ||
| 284 | int | ||
| 285 | 25 | match_exact( | |
| 286 | std::string_view spec, | ||
| 287 | std::string_view offered ) noexcept | ||
| 288 | { | ||
| 289 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 16 times.
|
25 | if( iequals( spec, offered ) ) |
| 290 | 9 | return 1; | |
| 291 |
2/2✓ Branch 2 taken 1 time.
✓ Branch 3 taken 15 times.
|
16 | if( spec == "*" ) |
| 292 | 1 | return 0; | |
| 293 | 15 | return -1; | |
| 294 | } | ||
| 295 | |||
| 296 | // Language prefix: "en-US" -> "en" | ||
| 297 | std::string_view | ||
| 298 | 17 | lang_prefix( std::string_view tag ) noexcept | |
| 299 | { | ||
| 300 | 17 | auto dash = tag.find( '-' ); | |
| 301 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 14 times.
|
17 | if( dash != std::string_view::npos ) |
| 302 | 3 | return tag.substr( 0, dash ); | |
| 303 | 14 | return tag; | |
| 304 | } | ||
| 305 | |||
| 306 | // Language match with prefix support | ||
| 307 | int | ||
| 308 | 13 | match_language( | |
| 309 | std::string_view spec, | ||
| 310 | std::string_view offered ) noexcept | ||
| 311 | { | ||
| 312 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 9 times.
|
13 | if( iequals( spec, offered ) ) |
| 313 | 4 | return 4; | |
| 314 |
2/2✓ Branch 2 taken 1 time.
✓ Branch 3 taken 8 times.
|
9 | if( iequals( lang_prefix( spec ), offered ) ) |
| 315 | 1 | return 2; | |
| 316 |
2/2✓ Branch 2 taken 2 times.
✓ Branch 3 taken 6 times.
|
8 | if( iequals( spec, lang_prefix( offered ) ) ) |
| 317 | 2 | return 1; | |
| 318 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
6 | if( spec == "*" ) |
| 319 | ✗ | return 0; | |
| 320 | 6 | return -1; | |
| 321 | } | ||
| 322 | |||
| 323 | //---------------------------------------------------------- | ||
| 324 | // Generic negotiation for simple headers | ||
| 325 | //---------------------------------------------------------- | ||
| 326 | |||
| 327 | template< class MatchFn > | ||
| 328 | std::string_view | ||
| 329 | 12 | negotiate( | |
| 330 | std::vector<simple_entry> const& entries, | ||
| 331 | std::initializer_list<std::string_view> offered, | ||
| 332 | MatchFn match ) | ||
| 333 | { | ||
| 334 | 12 | std::string_view best_val; | |
| 335 | 12 | priority best_pri{ -1, -1, 0 }; | |
| 336 | 12 | bool found = false; | |
| 337 | |||
| 338 |
2/2✓ Branch 2 taken 18 times.
✓ Branch 3 taken 12 times.
|
30 | for( auto const& o : offered ) |
| 339 | { | ||
| 340 | 18 | priority pri{ -1, -1, 0 }; | |
| 341 | 18 | bool matched = false; | |
| 342 | |||
| 343 |
2/2✓ Branch 5 taken 38 times.
✓ Branch 6 taken 18 times.
|
56 | for( auto const& e : entries ) |
| 344 | { | ||
| 345 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
|
38 | if( e.q <= 0 ) |
| 346 | 21 | continue; | |
| 347 | 38 | auto s = match( e.value, o ); | |
| 348 |
2/2✓ Branch 0 taken 21 times.
✓ Branch 1 taken 17 times.
|
38 | if( s < 0 ) |
| 349 | 21 | continue; | |
| 350 | 17 | priority p{ e.q, s, e.order }; | |
| 351 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | if( ! matched || |
| 352 | ✗ | p.specificity > pri.specificity || | |
| 353 | ✗ | ( p.specificity == pri.specificity && | |
| 354 | ✗ | p.q > pri.q ) || | |
| 355 | ✗ | ( p.specificity == pri.specificity && | |
| 356 | ✗ | p.q == pri.q && | |
| 357 | ✗ | p.order < pri.order ) ) | |
| 358 | { | ||
| 359 | 17 | pri = p; | |
| 360 | 17 | matched = true; | |
| 361 | } | ||
| 362 | } | ||
| 363 | |||
| 364 |
3/4✓ Branch 0 taken 17 times.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
|
18 | if( ! matched || pri.q <= 0 ) |
| 365 | 1 | continue; | |
| 366 | |||
| 367 |
6/6✓ Branch 0 taken 6 times.
✓ Branch 1 taken 11 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 13 times.
✓ Branch 6 taken 4 times.
|
17 | if( ! found || is_better( pri, best_pri ) ) |
| 368 | { | ||
| 369 | 13 | best_val = o; | |
| 370 | 13 | best_pri = pri; | |
| 371 | 13 | found = true; | |
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1 time.
|
12 | return found ? best_val : std::string_view{}; |
| 376 | } | ||
| 377 | |||
| 378 | // Return sorted values from simple entries | ||
| 379 | std::vector<std::string_view> | ||
| 380 | 3 | sorted_values( | |
| 381 | std::vector<simple_entry>& entries ) | ||
| 382 | { | ||
| 383 | 3 | std::sort( entries.begin(), entries.end(), | |
| 384 | 13 | []( simple_entry const& a, | |
| 385 | simple_entry const& b ) | ||
| 386 | { | ||
| 387 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
|
13 | if( a.q != b.q ) |
| 388 | 11 | return a.q > b.q; | |
| 389 | 2 | return a.order < b.order; | |
| 390 | }); | ||
| 391 | |||
| 392 | 3 | std::vector<std::string_view> result; | |
| 393 |
1/1✓ Branch 2 taken 3 times.
|
3 | result.reserve( entries.size() ); |
| 394 |
2/2✓ Branch 5 taken 9 times.
✓ Branch 6 taken 3 times.
|
12 | for( auto const& e : entries ) |
| 395 | { | ||
| 396 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if( e.q <= 0 ) |
| 397 | ✗ | continue; | |
| 398 |
1/1✓ Branch 1 taken 9 times.
|
9 | result.push_back( e.value ); |
| 399 | } | ||
| 400 | 3 | return result; | |
| 401 | ✗ | } | |
| 402 | |||
| 403 | } // (anon) | ||
| 404 | |||
| 405 | //---------------------------------------------------------- | ||
| 406 | |||
| 407 | 26 | accepts::accepts( | |
| 408 | 26 | fields_base const& fields ) noexcept | |
| 409 | 26 | : fields_( fields ) | |
| 410 | { | ||
| 411 | 26 | } | |
| 412 | |||
| 413 | std::string_view | ||
| 414 | 12 | accepts::type( | |
| 415 | std::initializer_list< | ||
| 416 | std::string_view> offered ) const | ||
| 417 | { | ||
| 418 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 11 times.
|
12 | if( offered.size() == 0 ) |
| 419 | 1 | return {}; | |
| 420 | |||
| 421 | 11 | auto accept = fields_.value_or( | |
| 422 | field::accept, "" ); | ||
| 423 | |||
| 424 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 10 times.
|
11 | if( accept.empty() ) |
| 425 | 1 | return *offered.begin(); | |
| 426 | |||
| 427 |
1/1✓ Branch 2 taken 10 times.
|
10 | auto ranges = parse_accept( accept ); |
| 428 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
|
10 | if( ranges.empty() ) |
| 429 | ✗ | return *offered.begin(); | |
| 430 | |||
| 431 | 10 | std::string_view best_val; | |
| 432 | 10 | priority best_pri{ -1, -1, 0 }; | |
| 433 | 10 | bool found = false; | |
| 434 | |||
| 435 |
2/2✓ Branch 2 taken 12 times.
✓ Branch 3 taken 10 times.
|
22 | for( auto const& o : offered ) |
| 436 | { | ||
| 437 | // Convert extension to MIME if needed | ||
| 438 | 12 | std::string_view mime_str = o; | |
| 439 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 3 times.
|
12 | if( o.find( '/' ) == std::string_view::npos ) |
| 440 | { | ||
| 441 | 9 | auto looked = mime_types::lookup( o ); | |
| 442 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1 time.
|
9 | if( ! looked.empty() ) |
| 443 | 8 | mime_str = looked; | |
| 444 | else | ||
| 445 | 1 | continue; | |
| 446 | } | ||
| 447 | |||
| 448 | 11 | auto slash = mime_str.find( '/' ); | |
| 449 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if( slash == std::string_view::npos ) |
| 450 | ✗ | continue; | |
| 451 | |||
| 452 |
1/1✓ Branch 1 taken 11 times.
|
11 | auto type = mime_str.substr( 0, slash ); |
| 453 |
1/1✓ Branch 1 taken 11 times.
|
11 | auto subtype = mime_str.substr( slash + 1 ); |
| 454 | |||
| 455 | // Find best matching range for this type | ||
| 456 | 11 | priority pri{ -1, -1, 0 }; | |
| 457 | 11 | bool matched = false; | |
| 458 | |||
| 459 |
2/2✓ Branch 5 taken 18 times.
✓ Branch 6 taken 11 times.
|
29 | for( auto const& r : ranges ) |
| 460 | { | ||
| 461 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 16 times.
|
18 | if( r.q <= 0 ) |
| 462 | 10 | continue; | |
| 463 | 16 | auto s = match_media( r, type, subtype ); | |
| 464 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
|
16 | if( s < 0 ) |
| 465 | 8 | continue; | |
| 466 | 8 | priority p{ r.q, s, r.order }; | |
| 467 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | if( ! matched || |
| 468 | ✗ | p.specificity > pri.specificity || | |
| 469 | ✗ | ( p.specificity == pri.specificity && | |
| 470 | ✗ | p.q > pri.q ) || | |
| 471 | ✗ | ( p.specificity == pri.specificity && | |
| 472 | ✗ | p.q == pri.q && | |
| 473 | ✗ | p.order < pri.order ) ) | |
| 474 | { | ||
| 475 | 8 | pri = p; | |
| 476 | 8 | matched = true; | |
| 477 | } | ||
| 478 | } | ||
| 479 | |||
| 480 |
3/4✓ Branch 0 taken 8 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
|
11 | if( ! matched || pri.q <= 0 ) |
| 481 | 3 | continue; | |
| 482 | |||
| 483 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
|
8 | if( ! found || is_better( pri, best_pri ) ) |
| 484 | { | ||
| 485 | 8 | best_val = o; | |
| 486 | 8 | best_pri = pri; | |
| 487 | 8 | found = true; | |
| 488 | } | ||
| 489 | } | ||
| 490 | |||
| 491 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
|
10 | return found ? best_val : std::string_view{}; |
| 492 | 10 | } | |
| 493 | |||
| 494 | std::vector<std::string_view> | ||
| 495 | 4 | accepts::types() const | |
| 496 | { | ||
| 497 | 4 | auto accept = fields_.value_or( | |
| 498 | field::accept, "" ); | ||
| 499 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 3 times.
|
4 | if( accept.empty() ) |
| 500 | 1 | return {}; | |
| 501 | |||
| 502 |
1/1✓ Branch 2 taken 3 times.
|
3 | auto ranges = parse_accept( accept ); |
| 503 | |||
| 504 |
1/1✓ Branch 3 taken 3 times.
|
3 | std::sort( ranges.begin(), ranges.end(), |
| 505 | 6 | []( media_range const& a, | |
| 506 | media_range const& b ) | ||
| 507 | { | ||
| 508 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if( a.q != b.q ) |
| 509 | 6 | return a.q > b.q; | |
| 510 | ✗ | return a.order < b.order; | |
| 511 | }); | ||
| 512 | |||
| 513 | 3 | std::vector<std::string_view> result; | |
| 514 |
1/1✓ Branch 2 taken 3 times.
|
3 | result.reserve( ranges.size() ); |
| 515 |
2/2✓ Branch 5 taken 6 times.
✓ Branch 6 taken 3 times.
|
9 | for( auto const& r : ranges ) |
| 516 | { | ||
| 517 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 5 times.
|
6 | if( r.q <= 0 ) |
| 518 | 1 | continue; | |
| 519 |
1/1✓ Branch 1 taken 5 times.
|
5 | result.push_back( r.full ); |
| 520 | } | ||
| 521 | 3 | return result; | |
| 522 | 3 | } | |
| 523 | |||
| 524 | std::string_view | ||
| 525 | 6 | accepts::encoding( | |
| 526 | std::initializer_list< | ||
| 527 | std::string_view> offered ) const | ||
| 528 | { | ||
| 529 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
|
6 | if( offered.size() == 0 ) |
| 530 | ✗ | return {}; | |
| 531 | |||
| 532 | 6 | auto header = fields_.value_or( | |
| 533 | field::accept_encoding, "" ); | ||
| 534 | |||
| 535 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 5 times.
|
6 | if( header.empty() ) |
| 536 | 1 | return *offered.begin(); | |
| 537 | |||
| 538 |
1/1✓ Branch 2 taken 5 times.
|
5 | auto entries = parse_simple( header ); |
| 539 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
|
5 | if( entries.empty() ) |
| 540 | ✗ | return *offered.begin(); | |
| 541 | |||
| 542 | 5 | return negotiate( entries, offered, match_exact ); | |
| 543 | 5 | } | |
| 544 | |||
| 545 | std::vector<std::string_view> | ||
| 546 | 2 | accepts::encodings() const | |
| 547 | { | ||
| 548 | 2 | auto header = fields_.value_or( | |
| 549 | field::accept_encoding, "" ); | ||
| 550 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
|
2 | if( header.empty() ) |
| 551 | 1 | return {}; | |
| 552 | |||
| 553 |
1/1✓ Branch 2 taken 1 time.
|
1 | auto entries = parse_simple( header ); |
| 554 |
1/1✓ Branch 1 taken 1 time.
|
1 | return sorted_values( entries ); |
| 555 | 1 | } | |
| 556 | |||
| 557 | std::string_view | ||
| 558 | 3 | accepts::charset( | |
| 559 | std::initializer_list< | ||
| 560 | std::string_view> offered ) const | ||
| 561 | { | ||
| 562 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | if( offered.size() == 0 ) |
| 563 | ✗ | return {}; | |
| 564 | |||
| 565 | 3 | auto header = fields_.value_or( | |
| 566 | field::accept_charset, "" ); | ||
| 567 | |||
| 568 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
|
3 | if( header.empty() ) |
| 569 | 1 | return *offered.begin(); | |
| 570 | |||
| 571 |
1/1✓ Branch 2 taken 2 times.
|
2 | auto entries = parse_simple( header ); |
| 572 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2 | if( entries.empty() ) |
| 573 | ✗ | return *offered.begin(); | |
| 574 | |||
| 575 | 2 | return negotiate( entries, offered, match_exact ); | |
| 576 | 2 | } | |
| 577 | |||
| 578 | std::vector<std::string_view> | ||
| 579 | 1 | accepts::charsets() const | |
| 580 | { | ||
| 581 | 1 | auto header = fields_.value_or( | |
| 582 | field::accept_charset, "" ); | ||
| 583 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
|
1 | if( header.empty() ) |
| 584 | ✗ | return {}; | |
| 585 | |||
| 586 |
1/1✓ Branch 2 taken 1 time.
|
1 | auto entries = parse_simple( header ); |
| 587 |
1/1✓ Branch 1 taken 1 time.
|
1 | return sorted_values( entries ); |
| 588 | 1 | } | |
| 589 | |||
| 590 | std::string_view | ||
| 591 | 6 | accepts::language( | |
| 592 | std::initializer_list< | ||
| 593 | std::string_view> offered ) const | ||
| 594 | { | ||
| 595 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
|
6 | if( offered.size() == 0 ) |
| 596 | ✗ | return {}; | |
| 597 | |||
| 598 | 6 | auto header = fields_.value_or( | |
| 599 | field::accept_language, "" ); | ||
| 600 | |||
| 601 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 5 times.
|
6 | if( header.empty() ) |
| 602 | 1 | return *offered.begin(); | |
| 603 | |||
| 604 |
1/1✓ Branch 2 taken 5 times.
|
5 | auto entries = parse_simple( header ); |
| 605 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
|
5 | if( entries.empty() ) |
| 606 | ✗ | return *offered.begin(); | |
| 607 | |||
| 608 | 5 | return negotiate( entries, offered, match_language ); | |
| 609 | 5 | } | |
| 610 | |||
| 611 | std::vector<std::string_view> | ||
| 612 | 1 | accepts::languages() const | |
| 613 | { | ||
| 614 | 1 | auto header = fields_.value_or( | |
| 615 | field::accept_language, "" ); | ||
| 616 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
|
1 | if( header.empty() ) |
| 617 | ✗ | return {}; | |
| 618 | |||
| 619 |
1/1✓ Branch 2 taken 1 time.
|
1 | auto entries = parse_simple( header ); |
| 620 |
1/1✓ Branch 1 taken 1 time.
|
1 | return sorted_values( entries ); |
| 621 | 1 | } | |
| 622 | |||
| 623 | } // http | ||
| 624 | } // boost | ||
| 625 |