@@ -670,6 +670,7 @@ struct Request {
670
670
std::function<bool ()> is_connection_closed = []() { return true ; };
671
671
672
672
// for client
673
+ std::vector<std::string> accept_content_types;
673
674
ResponseHandler response_handler;
674
675
ContentReceiverWithProgress content_receiver;
675
676
Progress progress;
@@ -2491,6 +2492,9 @@ bool parse_multipart_boundary(const std::string &content_type,
2491
2492
2492
2493
bool parse_range_header (const std::string &s, Ranges &ranges);
2493
2494
2495
+ bool parse_accept_header (const std::string &s,
2496
+ std::vector<std::string> &content_types);
2497
+
2494
2498
int close_socket (socket_t sock);
2495
2499
2496
2500
ssize_t send_socket (socket_t sock, const void *ptr, size_t size, int flags);
@@ -5026,6 +5030,123 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
5026
5030
} catch (...) { return false ; }
5027
5031
#endif
5028
5032
5033
+ inline bool parse_accept_header (const std::string &s,
5034
+ std::vector<std::string> &content_types) {
5035
+ content_types.clear ();
5036
+
5037
+ // Empty string is considered valid (no preference)
5038
+ if (s.empty ()) { return true ; }
5039
+
5040
+ // Check for invalid patterns: leading/trailing commas or consecutive commas
5041
+ if (s.front () == ' ,' || s.back () == ' ,' ||
5042
+ s.find (" ,," ) != std::string::npos) {
5043
+ return false ;
5044
+ }
5045
+
5046
+ struct AcceptEntry {
5047
+ std::string media_type;
5048
+ double quality;
5049
+ int order; // Original order in header
5050
+ };
5051
+
5052
+ std::vector<AcceptEntry> entries;
5053
+ int order = 0 ;
5054
+ bool has_invalid_entry = false ;
5055
+
5056
+ // Split by comma and parse each entry
5057
+ split (s.data (), s.data () + s.size (), ' ,' , [&](const char *b, const char *e) {
5058
+ std::string entry (b, e);
5059
+ entry = trim_copy (entry);
5060
+
5061
+ if (entry.empty ()) {
5062
+ has_invalid_entry = true ;
5063
+ return ;
5064
+ }
5065
+
5066
+ AcceptEntry accept_entry;
5067
+ accept_entry.quality = 1.0 ; // Default quality
5068
+ accept_entry.order = order++;
5069
+
5070
+ // Find q= parameter
5071
+ auto q_pos = entry.find (" ;q=" );
5072
+ if (q_pos == std::string::npos) { q_pos = entry.find (" ; q=" ); }
5073
+
5074
+ if (q_pos != std::string::npos) {
5075
+ // Extract media type (before q parameter)
5076
+ accept_entry.media_type = trim_copy (entry.substr (0 , q_pos));
5077
+
5078
+ // Extract quality value
5079
+ auto q_start = entry.find (' =' , q_pos) + 1 ;
5080
+ auto q_end = entry.find (' ;' , q_start);
5081
+ if (q_end == std::string::npos) { q_end = entry.length (); }
5082
+
5083
+ std::string quality_str =
5084
+ trim_copy (entry.substr (q_start, q_end - q_start));
5085
+ if (quality_str.empty ()) {
5086
+ has_invalid_entry = true ;
5087
+ return ;
5088
+ }
5089
+
5090
+ try {
5091
+ accept_entry.quality = std::stod (quality_str);
5092
+ // Check if quality is in valid range [0.0, 1.0]
5093
+ if (accept_entry.quality < 0.0 || accept_entry.quality > 1.0 ) {
5094
+ has_invalid_entry = true ;
5095
+ return ;
5096
+ }
5097
+ } catch (...) {
5098
+ has_invalid_entry = true ;
5099
+ return ;
5100
+ }
5101
+ } else {
5102
+ // No quality parameter, use entire entry as media type
5103
+ accept_entry.media_type = entry;
5104
+ }
5105
+
5106
+ // Remove additional parameters from media type
5107
+ auto param_pos = accept_entry.media_type .find (' ;' );
5108
+ if (param_pos != std::string::npos) {
5109
+ accept_entry.media_type =
5110
+ trim_copy (accept_entry.media_type .substr (0 , param_pos));
5111
+ }
5112
+
5113
+ // Basic validation of media type format
5114
+ if (accept_entry.media_type .empty ()) {
5115
+ has_invalid_entry = true ;
5116
+ return ;
5117
+ }
5118
+
5119
+ // Check for basic media type format (should contain '/' or be '*')
5120
+ if (accept_entry.media_type != " *" &&
5121
+ accept_entry.media_type .find (' /' ) == std::string::npos) {
5122
+ has_invalid_entry = true ;
5123
+ return ;
5124
+ }
5125
+
5126
+ entries.push_back (accept_entry);
5127
+ });
5128
+
5129
+ // Return false if any invalid entry was found
5130
+ if (has_invalid_entry) { return false ; }
5131
+
5132
+ // Sort by quality (descending), then by original order (ascending)
5133
+ std::sort (entries.begin (), entries.end (),
5134
+ [](const AcceptEntry &a, const AcceptEntry &b) {
5135
+ if (a.quality != b.quality ) {
5136
+ return a.quality > b.quality ; // Higher quality first
5137
+ }
5138
+ return a.order < b.order ; // Earlier order first for same quality
5139
+ });
5140
+
5141
+ // Extract sorted media types
5142
+ content_types.reserve (entries.size ());
5143
+ for (const auto &entry : entries) {
5144
+ content_types.push_back (entry.media_type );
5145
+ }
5146
+
5147
+ return true ;
5148
+ }
5149
+
5029
5150
class MultipartFormDataParser {
5030
5151
public:
5031
5152
MultipartFormDataParser () = default ;
@@ -7446,6 +7567,14 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
7446
7567
req.set_header (" LOCAL_ADDR" , req.local_addr );
7447
7568
req.set_header (" LOCAL_PORT" , std::to_string (req.local_port ));
7448
7569
7570
+ if (req.has_header (" Accept" )) {
7571
+ const auto &accept_header = req.get_header_value (" Accept" );
7572
+ if (!detail::parse_accept_header (accept_header, req.accept_content_types )) {
7573
+ res.status = StatusCode::BadRequest_400;
7574
+ return write_response (strm, close_connection, req, res);
7575
+ }
7576
+ }
7577
+
7449
7578
if (req.has_header (" Range" )) {
7450
7579
const auto &range_header_value = req.get_header_value (" Range" );
7451
7580
if (!detail::parse_range_header (range_header_value, req.ranges )) {
0 commit comments