Line data Source code
1 : #include "http_response.hpp"
2 : #include "../server/mime_types.hpp"
3 : #include "../data/out_string.hpp"
4 :
5 : #include <algorithm>
6 : #include <memory>
7 : #include "../../util/logger.hpp"
8 :
9 : namespace thinger::http{
10 :
11 : namespace status_strings{
12 :
13 : const std::string ok =
14 : "HTTP/1.1 200 OK";
15 : const std::string created =
16 : "HTTP/1.1 201 Created";
17 : const std::string accepted =
18 : "HTTP/1.1 202 Accepted";
19 : const std::string no_content =
20 : "HTTP/1.1 204 No Content";
21 : const std::string partial_content =
22 : "HTTP/1.1 206 Partial Content";
23 : const std::string multiple_choices =
24 : "HTTP/1.1 300 Multiple Choices";
25 : const std::string moved_permanently =
26 : "HTTP/1.1 301 Moved Permanently";
27 : const std::string moved_temporarily =
28 : "HTTP/1.1 302 Moved Temporarily";
29 : const std::string not_modified =
30 : "HTTP/1.1 304 Not Modified";
31 : const std::string bad_request =
32 : "HTTP/1.1 400 Bad Request";
33 : const std::string unauthorized =
34 : "HTTP/1.1 401 Unauthorized";
35 : const std::string forbidden =
36 : "HTTP/1.1 403 Forbidden";
37 : const std::string not_found =
38 : "HTTP/1.1 404 Not Found";
39 : const std::string not_allowed =
40 : "HTTP/1.1 405 Method Not Allowed";
41 : const std::string timed_out =
42 : "HTTP/1.1 408 Request Timeout";
43 : const std::string internal_server_error =
44 : "HTTP/1.1 500 Internal Server Error";
45 : const std::string not_implemented =
46 : "HTTP/1.1 501 Not Implemented";
47 : const std::string bad_gateway =
48 : "HTTP/1.1 502 Bad Gateway";
49 : const std::string service_unavailable =
50 : "HTTP/1.1 503 Service Unavailable";
51 : const std::string gateway_timeout =
52 : "HTTP/1.1 504 Gateway Timeout";
53 : const std::string switching_protocols =
54 : "HTTP/1.1 101 Switching Protocols";
55 : const std::string too_many_requests =
56 : "HTTP/1.1 429 Too Many Requests";
57 : const std::string temporary_redirect =
58 : "HTTP/1.1 307 Temporary Redirect";
59 : const std::string permanent_redirect =
60 : "HTTP/1.1 308 Permanent Redirect";
61 : const std::string upgrade_required =
62 : "HTTP/1.1 426 Upgrade Required";
63 : const std::string conflict =
64 : "HTTP/1.1 409 Conflict";
65 : const std::string payload_too_large =
66 : "HTTP/1.1 413 Payload Too Large";
67 : const std::string unsupported_media_type =
68 : "HTTP/1.1 415 Unsupported Media Type";
69 : const std::string range_not_satisfiable =
70 : "HTTP/1.1 416 Range Not Satisfiable";
71 : const std::string expectation_failed =
72 : "HTTP/1.1 417 Expectation Failed";
73 : const std::string misdirected_request =
74 : "HTTP/1.1 421 Misdirected Request";
75 : const std::string request_header_fields_too_large =
76 : "HTTP/1.1 431 Request Header Fields Too Large";
77 : const std::string unknown =
78 : "HTTP/1.1 000 Unknown Status";
79 :
80 23736 : const std::string& get_status_string(http_response::status status){
81 23736 : switch(status){
82 16487 : case http_response::status::ok:
83 16487 : return ok;
84 40 : case http_response::status::created:
85 40 : return created;
86 3 : case http_response::status::accepted:
87 3 : return accepted;
88 111 : case http_response::status::no_content:
89 111 : return no_content;
90 41 : case http_response::status::partial_content:
91 41 : return partial_content;
92 3 : case http_response::status::multiple_choices:
93 3 : return multiple_choices;
94 14 : case http_response::status::moved_permanently:
95 14 : return moved_permanently;
96 371 : case http_response::status::moved_temporarily:
97 371 : return moved_temporarily;
98 9 : case http_response::status::temporary_redirect:
99 9 : return temporary_redirect;
100 9 : case http_response::status::permanent_redirect:
101 9 : return permanent_redirect;
102 100 : case http_response::status::not_modified:
103 100 : return not_modified;
104 1122 : case http_response::status::bad_request:
105 1122 : return bad_request;
106 115 : case http_response::status::unauthorized:
107 115 : return unauthorized;
108 128 : case http_response::status::forbidden:
109 128 : return forbidden;
110 298 : case http_response::status::not_found:
111 298 : return not_found;
112 82 : case http_response::status::not_allowed:
113 82 : return not_allowed;
114 129 : case http_response::status::timed_out:
115 129 : return timed_out;
116 77 : case http_response::status::internal_server_error:
117 77 : return internal_server_error;
118 3 : case http_response::status::not_implemented:
119 3 : return not_implemented;
120 81 : case http_response::status::bad_gateway:
121 81 : return bad_gateway;
122 9 : case http_response::status::service_unavailable:
123 9 : return service_unavailable;
124 180 : case http_response::status::gateway_timeout:
125 180 : return gateway_timeout;
126 3703 : case http_response::status::switching_protocols:
127 3703 : return switching_protocols;
128 3 : case http_response::status::too_many_requests:
129 3 : return too_many_requests;
130 39 : case http_response::status::upgrade_required:
131 39 : return upgrade_required;
132 3 : case http_response::status::conflict:
133 3 : return conflict;
134 302 : case http_response::status::payload_too_large:
135 302 : return payload_too_large;
136 45 : case http_response::status::unsupported_media_type:
137 45 : return unsupported_media_type;
138 10 : case http_response::status::range_not_satisfiable:
139 10 : return range_not_satisfiable;
140 39 : case http_response::status::expectation_failed:
141 39 : return expectation_failed;
142 51 : case http_response::status::misdirected_request:
143 51 : return misdirected_request;
144 57 : case http_response::status::request_header_fields_too_large:
145 57 : return request_header_fields_too_large;
146 72 : default:
147 72 : return unknown;
148 : }
149 : }
150 : }
151 :
152 23370 : void http_response::to_buffer(std::vector<boost::asio::const_buffer>& buffer) const{
153 23370 : const auto& status_line = status_strings::get_status_string(status_);
154 23370 : auto code = get_status_code();
155 23370 : if (&status_line == &status_strings::unknown && code >= 100 && code <= 599) {
156 : // any other valid status (e.g. relayed from another server): its code, and its
157 : // reason phrase if it only has printable characters (it ends the line)
158 69 : status_line_ = "HTTP/1.1 " + std::to_string(code) + " ";
159 69 : bool printable = std::all_of(reason_phrase_.begin(), reason_phrase_.end(), [](unsigned char c) {
160 84 : return c == '\t' || (c >= 0x20 && c != 0x7f);
161 : });
162 69 : if (printable) status_line_ += reason_phrase_;
163 69 : buffer.emplace_back(boost::asio::buffer(status_line_));
164 69 : } else {
165 23301 : buffer.emplace_back(boost::asio::buffer(status_line));
166 : }
167 23370 : buffer.emplace_back(boost::asio::buffer(misc_strings::crlf));
168 99230 : for(const auto& t: headers_){
169 75860 : buffer.emplace_back(boost::asio::buffer(t.first));
170 75860 : buffer.emplace_back(boost::asio::buffer(misc_strings::name_value_separator));
171 75860 : buffer.emplace_back(boost::asio::buffer(t.second));
172 75860 : buffer.emplace_back(boost::asio::buffer(misc_strings::crlf));
173 : }
174 23370 : buffer.emplace_back(boost::asio::buffer(misc_strings::crlf));
175 23370 : if(!content_.empty()){
176 16998 : buffer.emplace_back(boost::asio::buffer(content_));
177 : }
178 23370 : }
179 :
180 :
181 31103 : void http_response::log(const char* scope, const body_logging& options) const{
182 31103 : if(!LOG_INFO_ENABLED()) return;
183 348 : LOG_INFO("[{}] {}", scope, status_strings::get_status_string(status_));
184 348 : if(LOG_DEBUG_ENABLED()){
185 336 : LOG_DEBUG("Headers:");
186 336 : log_headers(options);
187 : }
188 348 : log_body(content_, content_.empty() ? get_content_length() : content_.size(), options);
189 : }
190 :
191 : namespace stock_replies{
192 :
193 : static const std::string ok;
194 :
195 : static const std::string created(
196 : "<html>"
197 : "<head><title>Created</title></head>"
198 : "<body><h1>201 Created</h1></body>"
199 : "</html>");
200 :
201 : static const std::string accepted(
202 : "<html>"
203 : "<head><title>Accepted</title></head>"
204 : "<body><h1>202 Accepted</h1></body>"
205 : "</html>");
206 :
207 : static const std::string no_content(
208 : "<html>"
209 : "<head><title>No Content</title></head>"
210 : "<body><h1>204 Content</h1></body>"
211 : "</html>");
212 :
213 : static const std::string multiple_choices(
214 : "<html>"
215 : "<head><title>Multiple Choices</title></head>"
216 : "<body><h1>300 Multiple Choices</h1></body>"
217 : "</html>");
218 :
219 : static const std::string moved_permanently(
220 : "<html>"
221 : "<head><title>Moved Permanently</title></head>"
222 : "<body><h1>301 Moved Permanently</h1></body>"
223 : "</html>");
224 :
225 : static const std::string moved_temporarily(
226 : "<html>"
227 : "<head><title>Moved Temporarily</title></head>"
228 : "<body><h1>302 Moved Temporarily</h1></body>"
229 : "</html>");
230 :
231 : static const std::string temporary_redirect(
232 : "<html>"
233 : "<head><title>Temporary Redirect</title></head>"
234 : "<body><h1>307 Temporary Redirect</h1></body>"
235 : "</html>");
236 :
237 : static const std::string permanent_redirect(
238 : "<html>"
239 : "<head><title>Permanet Redirect</title></head>"
240 : "<body><h1>308 Permanent Redirect</h1></body>"
241 : "</html>");
242 :
243 : static const std::string not_modified(
244 : "<html>"
245 : "<head><title>Not Modified</title></head>"
246 : "<body><h1>304 Not Modified</h1></body>"
247 : "</html>");
248 :
249 : static const std::string bad_request(
250 : "<html>"
251 : "<head><title>Bad Request</title></head>"
252 : "<body><h1>400 Bad Request</h1></body>"
253 : "</html>");
254 :
255 : static const std::string unauthorized(
256 : "<html>"
257 : "<head><title>Unauthorized</title></head>"
258 : "<body><h1>401 Unauthorized</h1></body>"
259 : "</html>");
260 :
261 : static const std::string forbidden(
262 : "<html>"
263 : "<head><title>Forbidden</title></head>"
264 : "<body><h1>403 Forbidden</h1></body>"
265 : "</html>");
266 :
267 : static const std::string not_found(
268 : "<html>"
269 : "<head><title>Not Found</title></head>"
270 : "<body><h1>404 Not Found</h1></body>"
271 : "</html>");
272 :
273 : static const std::string internal_server_error(
274 : "<html>"
275 : "<head><title>Internal Server Error</title></head>"
276 : "<body><h1>500 Internal Server Error</h1></body>"
277 : "</html>");
278 :
279 : static const std::string not_implemented(
280 : "<html>"
281 : "<head><title>Not Implemented</title></head>"
282 : "<body><h1>501 Not Implemented</h1></body>"
283 : "</html>");
284 :
285 : static const std::string bad_gateway(
286 : "<html>"
287 : "<head><title>Bad Gateway</title></head>"
288 : "<body><h1>502 Bad Gateway</h1></body>"
289 : "</html>");
290 :
291 : static const std::string service_unavailable(
292 : "<html>"
293 : "<head><title>Service Unavailable</title></head>"
294 : "<body><h1>503 Service Unavailable</h1></body>"
295 : "</html>");
296 :
297 : static const std::string gateway_timeout(
298 : "<html>"
299 : "<head><title>Gateway Timeout</title></head>"
300 : "<body><h1>504 Gateway Timeout</h1></body>"
301 : "</html>");
302 :
303 : static const std::string too_many_requests(
304 : "<html>"
305 : "<head><title>Too Many Requests</title></head>"
306 : "<body><h1>429 Too Many Requests</h1></body>"
307 : "</html>");
308 :
309 : static const std::string timed_out(
310 : "<html>"
311 : "<head><title>Request Timeout</title></head>"
312 : "<body><h1>408 Request Timeout</h1></body>"
313 : "</html>");
314 :
315 : static const std::string payload_too_large(
316 : "<html>"
317 : "<head><title>Payload Too Large</title></head>"
318 : "<body><h1>413 Payload Too Large</h1></body>"
319 : "</html>");
320 :
321 : static const std::string unsupported_media_type(
322 : "<html>"
323 : "<head><title>Unsupported Media Type</title></head>"
324 : "<body><h1>415 Unsupported Media Type</h1></body>"
325 : "</html>");
326 :
327 : static const std::string expectation_failed(
328 : "<html>"
329 : "<head><title>Expectation Failed</title></head>"
330 : "<body><h1>417 Expectation Failed</h1></body>"
331 : "</html>");
332 :
333 : static const std::string misdirected_request(
334 : "<html>"
335 : "<head><title>Misdirected Request</title></head>"
336 : "<body><h1>421 Misdirected Request</h1></body>"
337 : "</html>");
338 :
339 : static const std::string request_header_fields_too_large(
340 : "<html>"
341 : "<head><title>Request Header Fields Too Large</title></head>"
342 : "<body><h1>431 Request Header Fields Too Large</h1></body>"
343 : "</html>");
344 :
345 480 : const std::string& to_string(http_response::status status){
346 480 : switch(status){
347 3 : case http_response::status::ok:
348 3 : return ok;
349 3 : case http_response::status::created:
350 3 : return created;
351 3 : case http_response::status::accepted:
352 3 : return accepted;
353 3 : case http_response::status::no_content:
354 3 : return no_content;
355 0 : case http_response::status::multiple_choices:
356 0 : return multiple_choices;
357 3 : case http_response::status::moved_permanently:
358 3 : return moved_permanently;
359 3 : case http_response::status::moved_temporarily:
360 3 : return moved_temporarily;
361 3 : case http_response::status::temporary_redirect:
362 3 : return temporary_redirect;
363 3 : case http_response::status::permanent_redirect:
364 3 : return permanent_redirect;
365 3 : case http_response::status::not_modified:
366 3 : return not_modified;
367 324 : case http_response::status::bad_request:
368 324 : return bad_request;
369 3 : case http_response::status::unauthorized:
370 3 : return unauthorized;
371 3 : case http_response::status::forbidden:
372 3 : return forbidden;
373 3 : case http_response::status::not_found:
374 3 : return not_found;
375 42 : case http_response::status::timed_out:
376 42 : return timed_out;
377 3 : case http_response::status::internal_server_error:
378 3 : return internal_server_error;
379 0 : case http_response::status::not_implemented:
380 0 : return not_implemented;
381 3 : case http_response::status::bad_gateway:
382 3 : return bad_gateway;
383 3 : case http_response::status::service_unavailable:
384 3 : return service_unavailable;
385 0 : case http_response::status::gateway_timeout:
386 0 : return gateway_timeout;
387 3 : case http_response::status::too_many_requests:
388 3 : return too_many_requests;
389 3 : case http_response::status::payload_too_large:
390 3 : return payload_too_large;
391 0 : case http_response::status::unsupported_media_type:
392 0 : return unsupported_media_type;
393 3 : case http_response::status::expectation_failed:
394 3 : return expectation_failed;
395 3 : case http_response::status::misdirected_request:
396 3 : return misdirected_request;
397 57 : case http_response::status::request_header_fields_too_large:
398 57 : return request_header_fields_too_large;
399 0 : default:
400 0 : return internal_server_error;
401 : }
402 : }
403 :
404 : } // namespace stock_replies
405 :
406 480 : std::shared_ptr<http_response> http_response::stock_http_reply(http_response::status status){
407 480 : auto response = std::make_shared<http_response>();
408 480 : response->set_status(status);
409 480 : response->set_content(stock_replies::to_string(status), http::mime_types::text_html);
410 480 : return response;
411 0 : }
412 :
413 18 : std::string http_response::get_reason_phrase(http_response::status status){
414 : // status lines are "HTTP/1.1 <code> <reason>"
415 18 : const auto& line = status_strings::get_status_string(status);
416 18 : auto position = line.find(' ', line.find(' ') + 1);
417 18 : return position == std::string::npos ? std::string{} : line.substr(position + 1);
418 : }
419 :
420 333130 : http_response::http_response(){
421 : // room for the usual headers (connection, content length and type, and a few more)
422 333130 : headers_.reserve(8);
423 333130 : }
424 :
425 6596 : bool http_response::is_redirect_response() const{
426 13183 : return status_ == status::temporary_redirect ||
427 6587 : status_ == status::moved_temporarily ||
428 6322 : status_ == status::permanent_redirect ||
429 19487 : status_ == status::moved_permanently ||
430 12900 : get_status_code() == 303; // See Other, followed with a GET
431 : }
432 :
433 8906 : void http_response::set_status(uint16_t status_code){
434 8906 : status_ = (status) status_code;
435 8906 : }
436 :
437 13201 : void http_response::set_status(http_response::status status_code){
438 13201 : status_ = status_code;
439 13201 : }
440 :
441 :
442 12 : bool http_response::is_ok() const{
443 12 : return status_>=status::ok && status_<status::multiple_choices;
444 : }
445 :
446 6 : size_t http_response::get_size(){
447 : // Return the size of the content/payload
448 6 : return content_.size();
449 : }
450 :
451 22424 : void http_response::set_content(std::string content){
452 22424 : content_ = std::move(content);
453 22424 : set_content_length(content_.size());
454 22424 : }
455 :
456 21722 : void http_response::set_content(std::string content, std::string content_type){
457 21722 : set_content(std::move(content));
458 21722 : set_content_type(std::move(content_type));
459 21722 : }
460 :
461 23289 : void http_response::set_content_length(size_t content_length){
462 23289 : content_length_ = content_length;
463 23289 : replace_header(std::string(http::header::content_length), boost::lexical_cast<std::string>(content_length));
464 23289 : }
465 :
466 22286 : void http_response::set_content_type(std::string&& content_type){
467 22286 : set_header(http::header::content_type, std::move(content_type));
468 22286 : }
469 :
470 1635 : void http_response::set_content_type(const std::string& content_type) {
471 1635 : set_header(http::header::content_type, content_type);
472 1635 : }
473 :
474 9616 : void http_response::set_reason_phrase(const std::string& reason) {
475 9616 : reason_phrase_ = reason;
476 9616 : }
477 :
478 : }
|