summaryrefslogtreecommitdiffstats
path: root/src/lib/http/response.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/http/response.cc')
-rw-r--r--src/lib/http/response.cc233
1 files changed, 233 insertions, 0 deletions
diff --git a/src/lib/http/response.cc b/src/lib/http/response.cc
new file mode 100644
index 0000000..cdc419a
--- /dev/null
+++ b/src/lib/http/response.cc
@@ -0,0 +1,233 @@
+// Copyright (C) 2016-2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <http/date_time.h>
+#include <http/response.h>
+#include <boost/date_time/local_time/local_time.hpp>
+#include <boost/date_time/time_facet.hpp>
+#include <sstream>
+
+using namespace boost::local_time;
+using namespace isc::http;
+
+namespace {
+
+/// @brief A map of status codes to status names.
+const std::map<HttpStatusCode, std::string> status_code_to_description = {
+ { HttpStatusCode::OK, "OK" },
+ { HttpStatusCode::CREATED, "Created" },
+ { HttpStatusCode::ACCEPTED, "Accepted" },
+ { HttpStatusCode::NO_CONTENT, "No Content" },
+ { HttpStatusCode::MULTIPLE_CHOICES, "Multiple Choices" },
+ { HttpStatusCode::MOVED_PERMANENTLY, "Moved Permanently" },
+ { HttpStatusCode::MOVED_TEMPORARILY, "Moved Temporarily" },
+ { HttpStatusCode::NOT_MODIFIED, "Not Modified" },
+ { HttpStatusCode::BAD_REQUEST, "Bad Request" },
+ { HttpStatusCode::UNAUTHORIZED, "Unauthorized" },
+ { HttpStatusCode::FORBIDDEN, "Forbidden" },
+ { HttpStatusCode::NOT_FOUND, "Not Found" },
+ { HttpStatusCode::REQUEST_TIMEOUT, "Request Timeout" },
+ { HttpStatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error" },
+ { HttpStatusCode::NOT_IMPLEMENTED, "Not Implemented" },
+ { HttpStatusCode::BAD_GATEWAY, "Bad Gateway" },
+ { HttpStatusCode::SERVICE_UNAVAILABLE, "Service Unavailable" }
+};
+
+/// @brief New line (CRLF).
+const std::string crlf = "\r\n";
+
+}
+
+namespace isc {
+namespace http {
+
+HttpResponse::HttpResponse()
+ : HttpMessage(INBOUND), context_(new HttpResponseContext()) {
+}
+
+HttpResponse::HttpResponse(const HttpVersion& version,
+ const HttpStatusCode& status_code,
+ const CallSetGenericBody& generic_body)
+ : HttpMessage(OUTBOUND), context_(new HttpResponseContext()) {
+ context_->http_version_major_ = version.major_;
+ context_->http_version_minor_ = version.minor_;
+ context_->status_code_ = static_cast<unsigned int>(status_code);
+
+ if (generic_body.set_) {
+ // This currently does nothing, but it is useful to have it here as
+ // an example how to implement it in the derived classes.
+ setGenericBody(status_code);
+ }
+}
+
+void
+HttpResponse::create() {
+ try {
+ http_version_.major_ = context_->http_version_major_;
+ http_version_.minor_ = context_->http_version_minor_;
+
+ // Check if the HTTP version is allowed for this request.
+ if (!inRequiredSet(http_version_, required_versions_)) {
+ isc_throw(BadValue, "use of HTTP version "
+ << http_version_.major_ << "."
+ << http_version_.minor_
+ << " not allowed");
+ }
+
+ // Copy headers from the context.
+ for (auto header = context_->headers_.begin();
+ header != context_->headers_.end();
+ ++header) {
+ HttpHeaderPtr hdr(new HttpHeader(header->name_, header->value_));
+ headers_[hdr->getLowerCaseName()] = hdr;
+ }
+
+ if (getDirection() == HttpMessage::OUTBOUND) {
+ HttpHeaderPtr length_header(new HttpHeader("Content-Length", boost::lexical_cast<std::string>
+ (context_->body_.length())));
+ headers_["content-length"] = length_header;
+
+ HttpHeaderPtr date_header(new HttpHeader("Date", getDateHeaderValue()));;
+ headers_["date"] = date_header;
+ }
+
+ // Iterate over required headers and check that they exist
+ // in the HTTP response.
+ for (auto req_header = required_headers_.begin();
+ req_header != required_headers_.end();
+ ++req_header) {
+ auto header = headers_.find(req_header->first);
+ if (header == headers_.end()) {
+ isc_throw(BadValue, "required header " << req_header->first
+ << " not found in the HTTP response");
+ } else if (!req_header->second->getValue().empty() &&
+ !header->second->isValueEqual(req_header->second->getValue())) {
+ // If specific value is required for the header, check
+ // that the value in the HTTP response matches it.
+ isc_throw(BadValue, "required header's " << header->first
+ << " value is " << req_header->second->getValue()
+ << ", but " << header->second->getValue() << " was found");
+ }
+ }
+
+ } catch (const std::exception& ex) {
+ // Reset the state of the object if we failed at any point.
+ reset();
+ isc_throw(HttpResponseError, ex.what());
+ }
+
+ // All ok.
+ created_ = true;
+}
+
+void
+HttpResponse::finalize() {
+ if (!created_) {
+ create();
+ }
+
+ finalized_ = true;
+}
+
+void
+HttpResponse::reset() {
+ created_ = false;
+ finalized_ = false;
+ headers_.clear();
+}
+
+HttpStatusCode
+HttpResponse::getStatusCode() const {
+ checkCreated();
+ return (static_cast<HttpStatusCode>(context_->status_code_));
+}
+
+std::string
+HttpResponse::getStatusPhrase() const {
+ checkCreated();
+ return (context_->phrase_);
+}
+
+std::string
+HttpResponse::getBody() const {
+ checkFinalized();
+ return (context_->body_);
+}
+
+bool
+HttpResponse::isClientError(const HttpStatusCode& status_code) {
+ // Client errors have status codes of 4XX.
+ uint16_t c = statusCodeToNumber(status_code);
+ return ((c >= 400) && (c < 500));
+}
+
+bool
+HttpResponse::isServerError(const HttpStatusCode& status_code) {
+ // Server errors have status codes of 5XX.
+ uint16_t c = statusCodeToNumber(status_code);
+ return ((c >= 500) && (c < 600));
+}
+
+std::string
+HttpResponse::statusCodeToString(const HttpStatusCode& status_code) {
+ auto status_code_it = status_code_to_description.find(status_code);
+ if (status_code_it == status_code_to_description.end()) {
+ isc_throw(HttpResponseError, "internal server error: no HTTP status"
+ " description for the given status code "
+ << static_cast<uint16_t>(status_code));
+ }
+ return (status_code_it->second);
+}
+
+uint16_t
+HttpResponse::statusCodeToNumber(const HttpStatusCode& status_code) {
+ return (static_cast<uint16_t>(status_code));
+}
+
+std::string
+HttpResponse::getDateHeaderValue() const {
+ // This returns current time in the recommended format.
+ HttpDateTime date_time;
+ return (date_time.rfc1123Format());
+}
+
+std::string
+HttpResponse::toBriefString() const {
+ checkFinalized();
+
+ std::ostringstream s;
+ // HTTP version number and status code.
+ s << "HTTP/" << http_version_.major_ << "." << http_version_.minor_;
+ s << " " << context_->status_code_;
+ s << " " << statusCodeToString(static_cast<HttpStatusCode>(context_->status_code_));
+ return (s.str());
+}
+
+std::string
+HttpResponse::toString() const {
+
+ std::ostringstream s;
+ // HTTP version number and status code.
+ s << toBriefString() << crlf;
+
+ for (auto header_it = headers_.cbegin(); header_it != headers_.cend();
+ ++header_it) {
+ s << header_it->second->getName() << ": " << header_it->second->getValue()
+ << crlf;
+ }
+
+ s << crlf;
+
+ // Include message body.
+ s << getBody();
+
+ return (s.str());
+}
+
+} // namespace http
+} // namespace isc