request.cc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. // Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <http/request.h>
  7. #include <boost/algorithm/string.hpp>
  8. #include <boost/lexical_cast.hpp>
  9. namespace isc {
  10. namespace http {
  11. HttpRequest::HttpRequest()
  12. : required_methods_(),required_versions_(), required_headers_(),
  13. created_(false), finalized_(false), method_(Method::HTTP_METHOD_UNKNOWN),
  14. headers_(), context_(new HttpRequestContext()) {
  15. }
  16. HttpRequest::~HttpRequest() {
  17. }
  18. void
  19. HttpRequest::requireHttpMethod(const HttpRequest::Method& method) {
  20. required_methods_.insert(method);
  21. }
  22. void
  23. HttpRequest::requireHttpVersion(const HttpVersion& version) {
  24. required_versions_.insert(version);
  25. }
  26. void
  27. HttpRequest::requireHeader(const std::string& header_name) {
  28. // Empty value denotes that the header is required but no specific
  29. // value is expected.
  30. required_headers_[header_name] = "";
  31. }
  32. void
  33. HttpRequest::requireHeaderValue(const std::string& header_name,
  34. const std::string& header_value) {
  35. required_headers_[header_name] = header_value;
  36. }
  37. bool
  38. HttpRequest::requiresBody() const {
  39. // If Content-Length is required the body must exist too. There may
  40. // be probably some cases when Content-Length is not provided but
  41. // the body is provided. But, probably not in our use cases.
  42. return (required_headers_.find("Content-Length") != required_headers_.end());
  43. }
  44. void
  45. HttpRequest::create() {
  46. try {
  47. // The RequestParser doesn't validate the method name. Thus, this
  48. // may throw an exception. But, we're fine with lower case names,
  49. // e.g. get, post etc.
  50. method_ = methodFromString(context_->method_);
  51. // Check if the method is allowed for this request.
  52. if (!inRequiredSet(method_, required_methods_)) {
  53. isc_throw(BadValue, "use of HTTP " << methodToString(method_)
  54. << " not allowed");
  55. }
  56. // Check if the HTTP version is allowed for this request.
  57. if (!inRequiredSet(HttpVersion(context_->http_version_major_,
  58. context_->http_version_minor_),
  59. required_versions_)) {
  60. isc_throw(BadValue, "use of HTTP version "
  61. << context_->http_version_major_ << "."
  62. << context_->http_version_minor_
  63. << " not allowed");
  64. }
  65. // Copy headers from the context.
  66. for (auto header = context_->headers_.begin();
  67. header != context_->headers_.end();
  68. ++header) {
  69. headers_[header->name_] = header->value_;
  70. }
  71. // Iterate over required headers and check that they exist
  72. // in the HTTP request.
  73. for (auto req_header = required_headers_.begin();
  74. req_header != required_headers_.end();
  75. ++req_header) {
  76. auto header = headers_.find(req_header->first);
  77. if (header == headers_.end()) {
  78. isc_throw(BadValue, "required header " << req_header->first
  79. << " not found in the HTTP request");
  80. } else if (!req_header->second.empty() &&
  81. header->second != req_header->second) {
  82. // If specific value is required for the header, check
  83. // that the value in the HTTP request matches it.
  84. isc_throw(BadValue, "required header's " << header->first
  85. << " value is " << req_header->second
  86. << ", but " << header->second << " was found");
  87. }
  88. }
  89. } catch (const std::exception& ex) {
  90. // Reset the state of the object if we failed at any point.
  91. reset();
  92. isc_throw(HttpRequestError, ex.what());
  93. }
  94. // All ok.
  95. created_ = true;
  96. }
  97. void
  98. HttpRequest::finalize() {
  99. if (!created_) {
  100. create();
  101. }
  102. // In this specific case, we don't need to do anything because the
  103. // body is retrieved from the context object directly. We also don't
  104. // know what type of body we have received. Derived classes should
  105. // override this method and handle various types of bodies.
  106. finalized_ = true;
  107. }
  108. void
  109. HttpRequest::reset() {
  110. created_ = false;
  111. finalized_ = false;
  112. method_ = HttpRequest::Method::HTTP_METHOD_UNKNOWN;
  113. headers_.clear();
  114. }
  115. HttpRequest::Method
  116. HttpRequest::getMethod() const {
  117. checkCreated();
  118. return (method_);
  119. }
  120. std::string
  121. HttpRequest::getUri() const {
  122. checkCreated();
  123. return (context_->uri_);
  124. }
  125. HttpVersion
  126. HttpRequest::getHttpVersion() const {
  127. checkCreated();
  128. return (HttpVersion(context_->http_version_major_,
  129. context_->http_version_minor_));
  130. }
  131. std::string
  132. HttpRequest::getHeaderValue(const std::string& header) const {
  133. checkCreated();
  134. auto header_it = headers_.find(header);
  135. if (header_it != headers_.end()) {
  136. return (header_it->second);
  137. }
  138. // No such header.
  139. isc_throw(HttpRequestNonExistingHeader, header << " HTTP header"
  140. " not found in the request");
  141. }
  142. uint64_t
  143. HttpRequest::getHeaderValueAsUint64(const std::string& header) const {
  144. // This will throw an exception if the header doesn't exist.
  145. std::string header_value = getHeaderValue(header);
  146. try {
  147. return (boost::lexical_cast<uint64_t>(header_value));
  148. } catch (const boost::bad_lexical_cast& ex) {
  149. // The specified header does exist, but the value is not a number.
  150. isc_throw(HttpRequestError, header << " HTTP header value "
  151. << header_value << " is not a valid number");
  152. }
  153. }
  154. std::string
  155. HttpRequest::getBody() const {
  156. checkFinalized();
  157. return (context_->body_);
  158. }
  159. void
  160. HttpRequest::checkCreated() const {
  161. if (!created_) {
  162. isc_throw(HttpRequestError, "unable to retrieve values of HTTP"
  163. " request because the HttpRequest::create() must be"
  164. " called first. This is a programmatic error");
  165. }
  166. }
  167. void
  168. HttpRequest::checkFinalized() const {
  169. if (!finalized_) {
  170. isc_throw(HttpRequestError, "unable to retrieve body of HTTP"
  171. " request because the HttpRequest::finalize() must be"
  172. " called first. This is a programmatic error");
  173. }
  174. }
  175. template<typename T>
  176. bool
  177. HttpRequest::inRequiredSet(const T& element,
  178. const std::set<T>& element_set) const {
  179. return (element_set.empty() || element_set.count(element) > 0);
  180. }
  181. HttpRequest::Method
  182. HttpRequest::methodFromString(std::string method) const {
  183. boost::to_upper(method);
  184. if (method == "GET") {
  185. return (Method::HTTP_GET);
  186. } else if (method == "POST") {
  187. return (Method::HTTP_POST);
  188. } else if (method == "HEAD") {
  189. return (Method::HTTP_HEAD);
  190. } else if (method == "PUT") {
  191. return (Method::HTTP_PUT);
  192. } else if (method == "DELETE") {
  193. return (Method::HTTP_DELETE);
  194. } else if (method == "OPTIONS") {
  195. return (Method::HTTP_OPTIONS);
  196. } else if (method == "CONNECT") {
  197. return (Method::HTTP_CONNECT);
  198. } else {
  199. isc_throw(HttpRequestError, "unknown HTTP method " << method);
  200. }
  201. }
  202. std::string
  203. HttpRequest::methodToString(const HttpRequest::Method& method) const {
  204. switch (method) {
  205. case Method::HTTP_GET:
  206. return ("GET");
  207. case Method::HTTP_POST:
  208. return ("POST");
  209. case Method::HTTP_HEAD:
  210. return ("HEAD");
  211. case Method::HTTP_PUT:
  212. return ("PUT");
  213. case Method::HTTP_DELETE:
  214. return ("DELETE");
  215. case Method::HTTP_OPTIONS:
  216. return ("OPTIONS");
  217. case Method::HTTP_CONNECT:
  218. return ("CONNECT");
  219. default:
  220. return ("unknown HTTP method");
  221. }
  222. }
  223. }
  224. }