request_handler.cpp 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. //
  2. // request_handler.cpp
  3. // ~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6. //
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #include "request_handler.hpp"
  11. #include <fstream>
  12. #include <sstream>
  13. #include <string>
  14. #include "mime_types.hpp"
  15. #include "reply.hpp"
  16. #include "request.hpp"
  17. namespace http {
  18. namespace server {
  19. request_handler::request_handler(const std::string& doc_root)
  20. : doc_root_(doc_root)
  21. {
  22. }
  23. void request_handler::handle_request(const request& req, reply& rep)
  24. {
  25. // Decode url to path.
  26. std::string request_path;
  27. if (!url_decode(req.uri, request_path))
  28. {
  29. rep = reply::stock_reply(reply::bad_request);
  30. return;
  31. }
  32. // Request path must be absolute and not contain "..".
  33. if (request_path.empty() || request_path[0] != '/'
  34. || request_path.find("..") != std::string::npos)
  35. {
  36. rep = reply::stock_reply(reply::bad_request);
  37. return;
  38. }
  39. // If path ends in slash (i.e. is a directory) then add "index.html".
  40. if (request_path[request_path.size() - 1] == '/')
  41. {
  42. request_path += "index.html";
  43. }
  44. // Determine the file extension.
  45. std::size_t last_slash_pos = request_path.find_last_of("/");
  46. std::size_t last_dot_pos = request_path.find_last_of(".");
  47. std::string extension;
  48. if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos)
  49. {
  50. extension = request_path.substr(last_dot_pos + 1);
  51. }
  52. // Open the file to send back.
  53. std::string full_path = doc_root_ + request_path;
  54. std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
  55. if (!is)
  56. {
  57. rep = reply::stock_reply(reply::not_found);
  58. return;
  59. }
  60. // Fill out the reply to be sent to the client.
  61. rep.status = reply::ok;
  62. char buf[512];
  63. while (is.read(buf, sizeof(buf)).gcount() > 0)
  64. rep.content.append(buf, is.gcount());
  65. rep.headers.resize(2);
  66. rep.headers[0].name = "Content-Length";
  67. rep.headers[0].value = std::to_string(rep.content.size());
  68. rep.headers[1].name = "Content-Type";
  69. rep.headers[1].value = mime_types::extension_to_type(extension);
  70. }
  71. bool request_handler::url_decode(const std::string& in, std::string& out)
  72. {
  73. out.clear();
  74. out.reserve(in.size());
  75. for (std::size_t i = 0; i < in.size(); ++i)
  76. {
  77. if (in[i] == '%')
  78. {
  79. if (i + 3 <= in.size())
  80. {
  81. int value = 0;
  82. std::istringstream is(in.substr(i + 1, 2));
  83. if (is >> std::hex >> value)
  84. {
  85. out += static_cast<char>(value);
  86. i += 2;
  87. }
  88. else
  89. {
  90. return false;
  91. }
  92. }
  93. else
  94. {
  95. return false;
  96. }
  97. }
  98. else if (in[i] == '+')
  99. {
  100. out += ' ';
  101. }
  102. else
  103. {
  104. out += in[i];
  105. }
  106. }
  107. return true;
  108. }
  109. } // namespace server
  110. } // namespace http