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