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