Line data Source code
1 : #include "headers.hpp"
2 : #include <charconv>
3 : #include <regex>
4 : #include "../../util/logger.hpp"
5 :
6 : namespace thinger::http{
7 :
8 5342 : void headers::process_header(std::string key, std::string value){
9 5342 : if(boost::indeterminate(keep_alive_) && is_header(key, header::connection)){
10 : /*
11 : * Firefox send both keep-alive and upgrade values in Connection header, i.e., when opening a WebSocket,
12 : * so it is necessary to test values separately.
13 : */
14 1041 : std::vector<std::string> strs;
15 1041 : boost::split(strs, value, boost::is_any_of(","));
16 2085 : for(auto& str:strs){
17 1044 : boost::algorithm::trim(str);
18 1044 : if(is_header(str, connection::keep_alive)){
19 961 : keep_alive_ = true;
20 : }
21 83 : else if (is_header(str, connection::close)){
22 39 : keep_alive_ = false;
23 : }
24 44 : else if(is_header(str, connection::upgrade)){
25 44 : upgrade_ = true;
26 : }
27 : }
28 1041 : }
29 :
30 5342 : if(is_header(key, header::accept)){
31 27 : stream_ = boost::iequals(value, accept::event_stream);
32 5315 : }else if(is_header(key, header::content_length)){
33 1010 : auto [ptr, ec] = std::from_chars(value.data(), value.data() + value.size(), content_length_);
34 1010 : if (ec != std::errc{}) {
35 3 : content_length_ = 0;
36 : }
37 : }
38 :
39 5342 : headers_.emplace_back(std::move(key), std::move(value));
40 5342 : }
41 :
42 8150 : void headers::add_header(std::string key, std::string value){
43 8150 : if(key.empty()) return;
44 8150 : headers_.emplace_back(std::move(key), std::move(value));
45 : }
46 :
47 21 : void headers::add_proxy(std::string key, std::string value){
48 21 : if(key.empty()) return;
49 18 : proxy_headers_.emplace_back(std::move(key), std::move(value));
50 : }
51 :
52 7056 : void headers::set_header(std::string key, std::string value){
53 11510 : for(auto & header : headers_)
54 : {
55 5851 : if(is_header(header.first, key)){
56 1397 : header.second = std::move(value);
57 1397 : return;
58 : }
59 : }
60 5659 : add_header(std::move(key), std::move(value));
61 : }
62 :
63 6 : void headers::set_proxy(std::string key, std::string value){
64 6 : for(auto & header : proxy_headers_)
65 : {
66 3 : if(is_header(header.first, key)){
67 3 : header.second = std::move(value);
68 3 : return;
69 : }
70 : }
71 3 : add_proxy(std::move(key), std::move(value));
72 : }
73 :
74 12 : bool headers::upgrade() const{
75 12 : return upgrade_;
76 : }
77 :
78 9 : bool headers::stream() const{
79 9 : return stream_;
80 : }
81 :
82 3484 : bool headers::has_header(std::string_view key) const{
83 11139 : for(const auto & header : headers_)
84 : {
85 7946 : if(is_header(header.first, key)){
86 291 : return true;
87 : }
88 : }
89 3193 : return false;
90 : }
91 :
92 637 : const std::string& headers::get_header(std::string_view key) const
93 : {
94 2039 : for(const auto & header : headers_)
95 : {
96 1972 : if(is_header(header.first, key)){
97 570 : return header.second;
98 : }
99 : }
100 67 : static std::string emtpy;
101 67 : return emtpy;
102 : }
103 :
104 52 : std::vector<std::string> headers::get_headers_with_key(std::string_view key) const{
105 52 : std::vector<std::string> headers;
106 156 : for(const auto & header : headers_)
107 : {
108 104 : if(is_header(header.first, key)){
109 12 : headers.push_back(header.second);
110 : }
111 : }
112 52 : return headers;
113 0 : }
114 :
115 0 : const std::vector<headers::http_header>& headers::get_headers() const{
116 0 : return headers_;
117 : }
118 :
119 80 : std::vector<headers::http_header>& headers::get_headers(){
120 80 : return headers_;
121 : }
122 :
123 57 : bool headers::remove_header(std::string_view key)
124 : {
125 192 : for(auto it=headers_.begin(); it!=headers_.end(); ++it){
126 189 : if(is_header(it->first, key)){
127 54 : headers_.erase(it);
128 54 : return true;
129 : }
130 : }
131 3 : return false;
132 : }
133 :
134 6 : const std::string& headers::get_authorization() const
135 : {
136 6 : return get_header(header::authorization);
137 : }
138 :
139 6 : const std::string& headers::get_cookie() const
140 : {
141 6 : return get_header(header::cookie);
142 : }
143 :
144 6 : const std::string& headers::get_user_agent() const{
145 6 : return get_header(header::user_agent);
146 : }
147 :
148 73 : const std::string& headers::get_content_type() const
149 : {
150 73 : return get_header(header::content_type);
151 : }
152 :
153 9 : bool headers::is_content_type(const std::string& value) const
154 : {
155 9 : return boost::istarts_with(get_header(header::content_type), value);
156 : }
157 :
158 6378 : bool headers::empty_headers() const{
159 6378 : return headers_.empty();
160 : }
161 :
162 3 : void headers::debug_headers(std::ostream& os) const{
163 9 : for(const std::pair<std::string, std::string>& t: headers_){
164 6 : os << "\t> " << t.first << ": " << t.second << std::endl;
165 : }
166 3 : }
167 :
168 0 : void headers::log(const char* scope, int level) const{
169 0 : LOG_DEBUG("[{}] Headers:", scope);
170 0 : for(const auto& t: headers_){
171 0 : LOG_DEBUG(" {}: {}", t.first, t.second);
172 : }
173 0 : for(const auto& t: proxy_headers_){
174 0 : LOG_DEBUG(" (PROXY REPLACE) {}: {}", t.first, t.second);
175 : }
176 0 : }
177 :
178 4123 : size_t headers::get_content_length() const{
179 4123 : return content_length_;
180 : }
181 :
182 3021 : bool headers::keep_alive() const
183 : {
184 3021 : if(boost::indeterminate(keep_alive_)){
185 1950 : return http_version_major_>=1 && http_version_minor_>=1;
186 : }else{
187 1071 : return (bool) keep_alive_;
188 : }
189 : }
190 :
191 1026 : void headers::set_keep_alive(bool keep_alive){
192 1026 : keep_alive_ = keep_alive;
193 1026 : set_header(http::header::connection, (keep_alive ? "Keep-Alive" : "Close"));
194 1026 : }
195 :
196 1992 : void headers::set_http_version_major(uint8_t http_version_major) {
197 1992 : http_version_major_ = http_version_major;
198 1992 : }
199 :
200 1992 : void headers::set_http_version_minor(uint8_t http_version_minor) {
201 1992 : http_version_minor_ = http_version_minor;
202 1992 : }
203 :
204 6 : int headers::get_http_version_major() const {
205 6 : return http_version_major_;
206 : }
207 :
208 6 : int headers::get_http_version_minor() const {
209 6 : return http_version_minor_;
210 : }
211 :
212 24 : std::string headers::get_parameter(const std::string& header_value, std::string_view name) {
213 :
214 24 : auto start = header_value.begin();
215 24 : auto end = header_value.end();
216 :
217 30 : if(start==end) return "";
218 :
219 21 : static std::regex cookie_regex("([^;^\\s]+)=\"?([^;^\"]*)\"?");
220 21 : std::smatch what;
221 :
222 36 : while (std::regex_search(start, end, what, cookie_regex))
223 : {
224 66 : std::string key(what[1].first, what[1].second);
225 33 : std::string value(what[2].first, what[2].second);
226 :
227 33 : if(key==name) return value;
228 :
229 15 : start = what[0].second;
230 51 : }
231 :
232 6 : return "";
233 21 : }
234 :
235 31317 : bool inline headers::is_header(std::string_view key, std::string_view header) const{
236 31317 : return boost::iequals(key, header);
237 : }
238 : }
|