Line data Source code
1 : #include "http_response.hpp"
2 : #include "../server/mime_types.hpp"
3 : #include "../data/out_string.hpp"
4 :
5 : #include <memory>
6 : #include "../../util/logger.hpp"
7 :
8 : namespace thinger::http{
9 :
10 : namespace status_strings{
11 :
12 : const std::string ok =
13 : "HTTP/1.1 200 OK";
14 : const std::string created =
15 : "HTTP/1.1 201 Created";
16 : const std::string accepted =
17 : "HTTP/1.1 202 Accepted";
18 : const std::string no_content =
19 : "HTTP/1.1 204 No Content";
20 : const std::string multiple_choices =
21 : "HTTP/1.1 300 Multiple Choices";
22 : const std::string moved_permanently =
23 : "HTTP/1.1 301 Moved Permanently";
24 : const std::string moved_temporarily =
25 : "HTTP/1.1 302 Moved Temporarily";
26 : const std::string not_modified =
27 : "HTTP/1.1 304 Not Modified";
28 : const std::string bad_request =
29 : "HTTP/1.1 400 Bad Request";
30 : const std::string unauthorized =
31 : "HTTP/1.1 401 Unauthorized";
32 : const std::string forbidden =
33 : "HTTP/1.1 403 Forbidden";
34 : const std::string not_found =
35 : "HTTP/1.1 404 Not Found";
36 : const std::string not_allowed =
37 : "HTTP/1.1 405 Method Not Allowed";
38 : const std::string timed_out =
39 : "HTTP/1.1 408 Request Timeout";
40 : const std::string internal_server_error =
41 : "HTTP/1.1 500 Internal Server Error";
42 : const std::string not_implemented =
43 : "HTTP/1.1 501 Not Implemented";
44 : const std::string bad_gateway =
45 : "HTTP/1.1 502 Bad Gateway";
46 : const std::string service_unavailable =
47 : "HTTP/1.1 503 Service Unavailable";
48 : const std::string switching_protocols =
49 : "HTTP/1.1 101 Switching Protocols";
50 : const std::string too_many_requests =
51 : "HTTP/1.1 429 Too Many Requests";
52 : const std::string temporary_redirect =
53 : "HTTP/1.1 307 Temporary Redirect";
54 : const std::string permanent_redirect =
55 : "HTTP/1.1 308 Permanent Redirect";
56 : const std::string upgrade_required =
57 : "HTTP/1.1 426 Upgrade Required";
58 : const std::string conflict =
59 : "HTTP/1.1 409 Conflict";
60 : const std::string payload_too_large =
61 : "HTTP/1.1 413 Payload Too Large";
62 : const std::string unknown =
63 : "HTTP/1.1 000 Unknown Status";
64 :
65 2648 : const std::string& get_status_string(http_response::status status){
66 2648 : switch(status){
67 2056 : case http_response::status::ok:
68 2056 : return ok;
69 24 : case http_response::status::created:
70 24 : return created;
71 3 : case http_response::status::accepted:
72 3 : return accepted;
73 6 : case http_response::status::no_content:
74 6 : return no_content;
75 3 : case http_response::status::multiple_choices:
76 3 : return multiple_choices;
77 9 : case http_response::status::moved_permanently:
78 9 : return moved_permanently;
79 198 : case http_response::status::moved_temporarily:
80 198 : return moved_temporarily;
81 3 : case http_response::status::temporary_redirect:
82 3 : return temporary_redirect;
83 3 : case http_response::status::permanent_redirect:
84 3 : return permanent_redirect;
85 3 : case http_response::status::not_modified:
86 3 : return not_modified;
87 18 : case http_response::status::bad_request:
88 18 : return bad_request;
89 15 : case http_response::status::unauthorized:
90 15 : return unauthorized;
91 9 : case http_response::status::forbidden:
92 9 : return forbidden;
93 81 : case http_response::status::not_found:
94 81 : return not_found;
95 27 : case http_response::status::not_allowed:
96 27 : return not_allowed;
97 3 : case http_response::status::timed_out:
98 3 : return timed_out;
99 24 : case http_response::status::internal_server_error:
100 24 : return internal_server_error;
101 3 : case http_response::status::not_implemented:
102 3 : return not_implemented;
103 3 : case http_response::status::bad_gateway:
104 3 : return bad_gateway;
105 12 : case http_response::status::service_unavailable:
106 12 : return service_unavailable;
107 117 : case http_response::status::switching_protocols:
108 117 : return switching_protocols;
109 3 : case http_response::status::too_many_requests:
110 3 : return too_many_requests;
111 3 : case http_response::status::upgrade_required:
112 3 : return upgrade_required;
113 3 : case http_response::status::conflict:
114 3 : return conflict;
115 16 : case http_response::status::payload_too_large:
116 16 : return payload_too_large;
117 3 : default:
118 3 : return unknown;
119 : }
120 : }
121 : }
122 :
123 949 : void http_response::to_buffer(std::vector<boost::asio::const_buffer>& buffer) const{
124 949 : buffer.emplace_back(boost::asio::buffer(status_strings::get_status_string(status_)));
125 949 : buffer.emplace_back(boost::asio::buffer(misc_strings::crlf));
126 3623 : for(const auto& t: headers_){
127 2674 : buffer.emplace_back(boost::asio::buffer(t.first));
128 2674 : buffer.emplace_back(boost::asio::buffer(misc_strings::name_value_separator));
129 2674 : buffer.emplace_back(boost::asio::buffer(t.second));
130 2674 : buffer.emplace_back(boost::asio::buffer(misc_strings::crlf));
131 : }
132 949 : buffer.emplace_back(boost::asio::buffer(misc_strings::crlf));
133 949 : if(!content_.empty()){
134 733 : buffer.emplace_back(boost::asio::buffer(content_));
135 : }
136 949 : }
137 :
138 :
139 1699 : void http_response::log(const char* scope, int level) const{
140 : // Log the response with context
141 1699 : LOG_INFO("[{}] {}", scope, status_strings::get_status_string(status_));
142 :
143 : // Log headers at debug level
144 1699 : LOG_DEBUG("Headers:");
145 6888 : for(const auto& t: headers_){
146 5189 : LOG_DEBUG(" {}: {}", t.first, t.second);
147 : }
148 1702 : for(const auto& t: proxy_headers_){
149 3 : LOG_DEBUG(" (PROXY) {}: {}", t.first, t.second);
150 : }
151 :
152 : // Log body if present (at trace level)
153 1699 : if(!content_.empty()){
154 1415 : LOG_TRACE("Body: {} bytes", content_.size());
155 : // Limit body output to avoid flooding logs
156 1415 : if(content_.size() <= 500) {
157 1394 : LOG_TRACE(" {}", content_);
158 : } else {
159 21 : LOG_TRACE(" {} (truncated)", content_.substr(0, 500));
160 : }
161 : }
162 1699 : }
163 :
164 : namespace stock_replies{
165 :
166 : static const std::string ok;
167 :
168 : static const std::string created(
169 : "<html>"
170 : "<head><title>Created</title></head>"
171 : "<body><h1>201 Created</h1></body>"
172 : "</html>");
173 :
174 : static const std::string accepted(
175 : "<html>"
176 : "<head><title>Accepted</title></head>"
177 : "<body><h1>202 Accepted</h1></body>"
178 : "</html>");
179 :
180 : static const std::string no_content(
181 : "<html>"
182 : "<head><title>No Content</title></head>"
183 : "<body><h1>204 Content</h1></body>"
184 : "</html>");
185 :
186 : static const std::string multiple_choices(
187 : "<html>"
188 : "<head><title>Multiple Choices</title></head>"
189 : "<body><h1>300 Multiple Choices</h1></body>"
190 : "</html>");
191 :
192 : static const std::string moved_permanently(
193 : "<html>"
194 : "<head><title>Moved Permanently</title></head>"
195 : "<body><h1>301 Moved Permanently</h1></body>"
196 : "</html>");
197 :
198 : static const std::string moved_temporarily(
199 : "<html>"
200 : "<head><title>Moved Temporarily</title></head>"
201 : "<body><h1>302 Moved Temporarily</h1></body>"
202 : "</html>");
203 :
204 : static const std::string temporary_redirect(
205 : "<html>"
206 : "<head><title>Temporary Redirect</title></head>"
207 : "<body><h1>307 Temporary Redirect</h1></body>"
208 : "</html>");
209 :
210 : static const std::string permanent_redirect(
211 : "<html>"
212 : "<head><title>Permanet Redirect</title></head>"
213 : "<body><h1>308 Permanent Redirect</h1></body>"
214 : "</html>");
215 :
216 : static const std::string not_modified(
217 : "<html>"
218 : "<head><title>Not Modified</title></head>"
219 : "<body><h1>304 Not Modified</h1></body>"
220 : "</html>");
221 :
222 : static const std::string bad_request(
223 : "<html>"
224 : "<head><title>Bad Request</title></head>"
225 : "<body><h1>400 Bad Request</h1></body>"
226 : "</html>");
227 :
228 : static const std::string unauthorized(
229 : "<html>"
230 : "<head><title>Unauthorized</title></head>"
231 : "<body><h1>401 Unauthorized</h1></body>"
232 : "</html>");
233 :
234 : static const std::string forbidden(
235 : "<html>"
236 : "<head><title>Forbidden</title></head>"
237 : "<body><h1>403 Forbidden</h1></body>"
238 : "</html>");
239 :
240 : static const std::string not_found(
241 : "<html>"
242 : "<head><title>Not Found</title></head>"
243 : "<body><h1>404 Not Found</h1></body>"
244 : "</html>");
245 :
246 : static const std::string internal_server_error(
247 : "<html>"
248 : "<head><title>Internal Server Error</title></head>"
249 : "<body><h1>500 Internal Server Error</h1></body>"
250 : "</html>");
251 :
252 : static const std::string not_implemented(
253 : "<html>"
254 : "<head><title>Not Implemented</title></head>"
255 : "<body><h1>501 Not Implemented</h1></body>"
256 : "</html>");
257 :
258 : static const std::string bad_gateway(
259 : "<html>"
260 : "<head><title>Bad Gateway</title></head>"
261 : "<body><h1>502 Bad Gateway</h1></body>"
262 : "</html>");
263 :
264 : static const std::string service_unavailable(
265 : "<html>"
266 : "<head><title>Service Unavailable</title></head>"
267 : "<body><h1>503 Service Unavailable</h1></body>"
268 : "</html>");
269 :
270 : static const std::string too_many_requests(
271 : "<html>"
272 : "<head><title>Too Many Requests</title></head>"
273 : "<body><h1>429 Too Many Requests</h1></body>"
274 : "</html>");
275 :
276 : static const std::string timed_out(
277 : "<html>"
278 : "<head><title>Request Timeout</title></head>"
279 : "<body><h1>408 Request Timeout</h1></body>"
280 : "</html>");
281 :
282 : static const std::string payload_too_large(
283 : "<html>"
284 : "<head><title>Payload Too Large</title></head>"
285 : "<body><h1>413 Payload Too Large</h1></body>"
286 : "</html>");
287 :
288 54 : const std::string& to_string(http_response::status status){
289 54 : switch(status){
290 3 : case http_response::status::ok:
291 3 : return ok;
292 3 : case http_response::status::created:
293 3 : return created;
294 3 : case http_response::status::accepted:
295 3 : return accepted;
296 3 : case http_response::status::no_content:
297 3 : return no_content;
298 0 : case http_response::status::multiple_choices:
299 0 : return multiple_choices;
300 3 : case http_response::status::moved_permanently:
301 3 : return moved_permanently;
302 3 : case http_response::status::moved_temporarily:
303 3 : return moved_temporarily;
304 3 : case http_response::status::temporary_redirect:
305 3 : return temporary_redirect;
306 3 : case http_response::status::permanent_redirect:
307 3 : return permanent_redirect;
308 3 : case http_response::status::not_modified:
309 3 : return not_modified;
310 3 : case http_response::status::bad_request:
311 3 : return bad_request;
312 3 : case http_response::status::unauthorized:
313 3 : return unauthorized;
314 3 : case http_response::status::forbidden:
315 3 : return forbidden;
316 3 : case http_response::status::not_found:
317 3 : return not_found;
318 3 : case http_response::status::timed_out:
319 3 : return timed_out;
320 3 : case http_response::status::internal_server_error:
321 3 : return internal_server_error;
322 0 : case http_response::status::not_implemented:
323 0 : return not_implemented;
324 0 : case http_response::status::bad_gateway:
325 0 : return bad_gateway;
326 3 : case http_response::status::service_unavailable:
327 3 : return service_unavailable;
328 3 : case http_response::status::too_many_requests:
329 3 : return too_many_requests;
330 3 : case http_response::status::payload_too_large:
331 3 : return payload_too_large;
332 0 : default:
333 0 : return internal_server_error;
334 : }
335 : }
336 :
337 : } // namespace stock_replies
338 :
339 54 : std::shared_ptr<http_response> http_response::stock_http_reply(http_response::status status){
340 54 : auto response = std::make_shared<http_response>();
341 54 : response->set_status(status);
342 54 : response->set_content(stock_replies::to_string(status), http::mime_types::text_html);
343 54 : return response;
344 0 : }
345 :
346 1891 : http_response::http_response()= default;
347 :
348 806 : bool http_response::is_redirect_response() const{
349 1609 : return status_ == status::temporary_redirect ||
350 803 : status_ == status::moved_temporarily ||
351 2336 : status_ == status::permanent_redirect ||
352 1533 : status_ == status::moved_permanently;
353 : }
354 :
355 825 : void http_response::set_status(uint16_t status_code){
356 825 : status_ = (status) status_code;
357 825 : }
358 :
359 1043 : void http_response::set_status(http_response::status status_code){
360 1043 : status_ = status_code;
361 1043 : }
362 :
363 :
364 1587 : http_response::status http_response::get_status() const{
365 1587 : return status_;
366 : }
367 :
368 97 : int http_response::get_status_code() const{
369 97 : return static_cast<int>(status_);
370 : }
371 :
372 12 : bool http_response::is_ok() const{
373 12 : return status_>=status::ok && status_<status::multiple_choices;
374 : }
375 :
376 6 : size_t http_response::get_size(){
377 : // Return the size of the content/payload
378 6 : return content_.size();
379 : }
380 :
381 0 : const std::string& http_response::get_content() const{
382 0 : return content_;
383 : }
384 :
385 45698 : std::string& http_response::get_content(){
386 45698 : return content_;
387 : }
388 :
389 12 : size_t http_response::get_content_size() const{
390 12 : return content_.size();
391 : }
392 :
393 933 : void http_response::set_content(std::string content){
394 933 : content_ = std::move(content);
395 933 : set_content_length(content_.size());
396 933 : }
397 :
398 828 : void http_response::set_content(std::string content, std::string content_type){
399 828 : set_content(std::move(content));
400 828 : set_content_type(std::move(content_type));
401 828 : }
402 :
403 936 : void http_response::set_content_length(size_t content_length){
404 936 : content_length_ = content_length;
405 936 : set_header(http::header::content_length, boost::lexical_cast<std::string>(content_length));
406 936 : }
407 :
408 831 : void http_response::set_content_type(std::string&& content_type){
409 831 : set_header(http::header::content_type, std::move(content_type));
410 831 : }
411 :
412 11 : void http_response::set_content_type(const std::string& content_type) {
413 11 : set_header(http::header::content_type, content_type);
414 11 : }
415 :
416 822 : void http_response::set_reason_phrase(const std::string& reason) {
417 822 : reason_phrase_ = reason;
418 822 : }
419 :
420 : }
|