@@ -450,6 +450,10 @@ struct hash {
450
450
}
451
451
};
452
452
453
+ template <typename T>
454
+ using unordered_set = std::unordered_set<T, detail::case_ignore::hash,
455
+ detail::case_ignore::equal_to>;
456
+
453
457
} // namespace case_ignore
454
458
455
459
// This is based on
@@ -710,6 +714,7 @@ struct Request {
710
714
std::string matched_route;
711
715
Params params;
712
716
Headers headers;
717
+ Headers trailers;
713
718
std::string body;
714
719
715
720
std::string remote_addr;
@@ -744,6 +749,10 @@ struct Request {
744
749
size_t get_header_value_count (const std::string &key) const ;
745
750
void set_header (const std::string &key, const std::string &val);
746
751
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
+
747
756
bool has_param (const std::string &key) const ;
748
757
std::string get_param_value (const std::string &key, size_t id = 0 ) const ;
749
758
size_t get_param_value_count (const std::string &key) const ;
@@ -765,6 +774,7 @@ struct Response {
765
774
int status = -1 ;
766
775
std::string reason;
767
776
Headers headers;
777
+ Headers trailers;
768
778
std::string body;
769
779
std::string location; // Redirect location
770
780
@@ -776,6 +786,10 @@ struct Response {
776
786
size_t get_header_value_count (const std::string &key) const ;
777
787
void set_header (const std::string &key, const std::string &val);
778
788
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
+
779
793
void set_redirect (const std::string &url, int status = StatusCode::Found_302);
780
794
void set_content (const char *s, size_t n, const std::string &content_type);
781
795
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,
4727
4741
// chunked transfer coding data without the final CRLF.
4728
4742
if (!line_reader.getline ()) { return ReadContentResult::Success; }
4729
4743
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
+
4730
4780
size_t trailer_header_count = 0 ;
4731
4781
while (strcmp (line_reader.ptr (), " \r\n " ) != 0 ) {
4732
4782
if (line_reader.size () > CPPHTTPLIB_HEADER_MAX_LENGTH) {
@@ -4744,11 +4794,12 @@ inline ReadContentResult read_content_chunked(Stream &strm, T &x,
4744
4794
4745
4795
parse_header (line_reader.ptr (), end,
4746
4796
[&](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
+ }
4748
4801
});
4749
4802
4750
- trailer_header_count++;
4751
-
4752
4803
if (!line_reader.getline ()) { return ReadContentResult::Error; }
4753
4804
}
4754
4805
@@ -6468,6 +6519,24 @@ inline void Request::set_header(const std::string &key,
6468
6519
}
6469
6520
}
6470
6521
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
+
6471
6540
inline bool Request::has_param (const std::string &key) const {
6472
6541
return params.find (key) != params.end ();
6473
6542
}
@@ -6571,6 +6640,23 @@ inline void Response::set_header(const std::string &key,
6571
6640
headers.emplace (key, val);
6572
6641
}
6573
6642
}
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
+ }
6574
6660
6575
6661
inline void Response::set_redirect (const std::string &url, int stat) {
6576
6662
if (detail::fields::is_field_value (url)) {
0 commit comments