@@ -450,6 +450,10 @@ struct hash {
450450 }
451451};
452452
453+ template <typename T>
454+ using unordered_set = std::unordered_set<T, detail::case_ignore::hash,
455+ detail::case_ignore::equal_to>;
456+
453457} // namespace case_ignore
454458
455459// This is based on
@@ -710,6 +714,7 @@ struct Request {
710714 std::string matched_route;
711715 Params params;
712716 Headers headers;
717+ Headers trailers;
713718 std::string body;
714719
715720 std::string remote_addr;
@@ -744,6 +749,10 @@ struct Request {
744749 size_t get_header_value_count (const std::string &key) const ;
745750 void set_header (const std::string &key, const std::string &val);
746751
752+ bool has_trailer (const std::string &key) const ;
753+ std::string get_trailer_value (const std::string &key, size_t id = 0 ) const ;
754+ size_t get_trailer_value_count (const std::string &key) const ;
755+
747756 bool has_param (const std::string &key) const ;
748757 std::string get_param_value (const std::string &key, size_t id = 0 ) const ;
749758 size_t get_param_value_count (const std::string &key) const ;
@@ -765,6 +774,7 @@ struct Response {
765774 int status = -1 ;
766775 std::string reason;
767776 Headers headers;
777+ Headers trailers;
768778 std::string body;
769779 std::string location; // Redirect location
770780
@@ -776,6 +786,10 @@ struct Response {
776786 size_t get_header_value_count (const std::string &key) const ;
777787 void set_header (const std::string &key, const std::string &val);
778788
789+ bool has_trailer (const std::string &key) const ;
790+ std::string get_trailer_value (const std::string &key, size_t id = 0 ) const ;
791+ size_t get_trailer_value_count (const std::string &key) const ;
792+
779793 void set_redirect (const std::string &url, int status = StatusCode::Found_302);
780794 void set_content (const char *s, size_t n, const std::string &content_type);
781795 void set_content (const std::string &s, const std::string &content_type);
@@ -4727,6 +4741,42 @@ inline ReadContentResult read_content_chunked(Stream &strm, T &x,
47274741 // chunked transfer coding data without the final CRLF.
47284742 if (!line_reader.getline ()) { return ReadContentResult::Success; }
47294743
4744+ // RFC 7230 Section 4.1.2 - Headers prohibited in trailers
4745+ thread_local case_ignore::unordered_set<std::string> prohibited_trailers = {
4746+ // Message framing
4747+ " transfer-encoding" , " content-length" ,
4748+
4749+ // Routing
4750+ " host" ,
4751+
4752+ // Authentication
4753+ " authorization" , " www-authenticate" , " proxy-authenticate" ,
4754+ " proxy-authorization" , " cookie" , " set-cookie" ,
4755+
4756+ // Request modifiers
4757+ " cache-control" , " expect" , " max-forwards" , " pragma" , " range" , " te" ,
4758+
4759+ // Response control
4760+ " age" , " expires" , " date" , " location" , " retry-after" , " vary" , " warning" ,
4761+
4762+ // Payload processing
4763+ " content-encoding" , " content-type" , " content-range" , " trailer" };
4764+
4765+ // Parse declared trailer headers once for performance
4766+ case_ignore::unordered_set<std::string> declared_trailers;
4767+ if (has_header (x.headers , " Trailer" )) {
4768+ auto trailer_header = get_header_value (x.headers , " Trailer" , " " , 0 );
4769+ auto len = std::strlen (trailer_header);
4770+
4771+ split (trailer_header, trailer_header + len, ' ,' ,
4772+ [&](const char *b, const char *e) {
4773+ std::string key (b, e);
4774+ if (prohibited_trailers.find (key) == prohibited_trailers.end ()) {
4775+ declared_trailers.insert (key);
4776+ }
4777+ });
4778+ }
4779+
47304780 size_t trailer_header_count = 0 ;
47314781 while (strcmp (line_reader.ptr (), " \r\n " ) != 0 ) {
47324782 if (line_reader.size () > CPPHTTPLIB_HEADER_MAX_LENGTH) {
@@ -4744,11 +4794,12 @@ inline ReadContentResult read_content_chunked(Stream &strm, T &x,
47444794
47454795 parse_header (line_reader.ptr (), end,
47464796 [&](const std::string &key, const std::string &val) {
4747- x.headers .emplace (key, val);
4797+ if (declared_trailers.find (key) != declared_trailers.end ()) {
4798+ x.trailers .emplace (key, val);
4799+ trailer_header_count++;
4800+ }
47484801 });
47494802
4750- trailer_header_count++;
4751-
47524803 if (!line_reader.getline ()) { return ReadContentResult::Error; }
47534804 }
47544805
@@ -6468,6 +6519,24 @@ inline void Request::set_header(const std::string &key,
64686519 }
64696520}
64706521
6522+ inline bool Request::has_trailer (const std::string &key) const {
6523+ return trailers.find (key) != trailers.end ();
6524+ }
6525+
6526+ inline std::string Request::get_trailer_value (const std::string &key,
6527+ size_t id) const {
6528+ auto rng = trailers.equal_range (key);
6529+ auto it = rng.first ;
6530+ std::advance (it, static_cast <ssize_t >(id));
6531+ if (it != rng.second ) { return it->second ; }
6532+ return std::string ();
6533+ }
6534+
6535+ inline size_t Request::get_trailer_value_count (const std::string &key) const {
6536+ auto r = trailers.equal_range (key);
6537+ return static_cast <size_t >(std::distance (r.first , r.second ));
6538+ }
6539+
64716540inline bool Request::has_param (const std::string &key) const {
64726541 return params.find (key) != params.end ();
64736542}
@@ -6571,6 +6640,23 @@ inline void Response::set_header(const std::string &key,
65716640 headers.emplace (key, val);
65726641 }
65736642}
6643+ inline bool Response::has_trailer (const std::string &key) const {
6644+ return trailers.find (key) != trailers.end ();
6645+ }
6646+
6647+ inline std::string Response::get_trailer_value (const std::string &key,
6648+ size_t id) const {
6649+ auto rng = trailers.equal_range (key);
6650+ auto it = rng.first ;
6651+ std::advance (it, static_cast <ssize_t >(id));
6652+ if (it != rng.second ) { return it->second ; }
6653+ return std::string ();
6654+ }
6655+
6656+ inline size_t Response::get_trailer_value_count (const std::string &key) const {
6657+ auto r = trailers.equal_range (key);
6658+ return static_cast <size_t >(std::distance (r.first , r.second ));
6659+ }
65746660
65756661inline void Response::set_redirect (const std::string &url, int stat) {
65766662 if (detail::fields::is_field_value (url)) {
0 commit comments