Commit 715d18f9 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac5088'

parents 76451257 3bcb33f2
......@@ -128,6 +128,14 @@ for retry in "none" "--std=c++11" "--std=c++0x" "--std=c++1x" "fail"; do
AC_MSG_WARN([unsupported C++11 feature])
AC_MSG_NOTICE([retrying by adding $retry to $CXX])
CXX="$CXX_SAVED $retry"
AC_MSG_CHECKING($retry support)
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[],
[int myincr = 1;])],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])
continue])
fi
AC_MSG_CHECKING(std::unique_ptr support)
......@@ -140,6 +148,35 @@ for retry in "none" "--std=c++11" "--std=c++0x" "--std=c++1x" "fail"; do
[AC_MSG_RESULT([no])
continue])
AC_MSG_CHECKING(cbegin/cend support)
feature="cbegin/cend"
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[#include <string>],
[const std::string& s = "abcd";
unsigned count = 0;
for (std::string::const_iterator i = s.cbegin();
i != s.cend(); ++i)
if (*i == 'b')
++count;])],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])
continue])
AC_MSG_CHECKING(final method support)
feature="final method"
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[],
[class Foo {
public:
virtual ~Foo() {};
virtual void bar() final;
}])],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])
continue])
AC_MSG_CHECKING(aggregate initialization support)
feature="aggregate initialization"
AC_COMPILE_IFELSE(
......@@ -155,7 +192,7 @@ for retry in "none" "--std=c++11" "--std=c++0x" "--std=c++1x" "fail"; do
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[],
[auto incr = [[]](int x) { return x + 1; };])],
[auto myincr = [[]](int x) { return x + 1; };])],
[AC_MSG_RESULT([yes])
break],
[AC_MSG_RESULT([no])
......@@ -1280,7 +1317,7 @@ if test "x$enable_gtest" = "xyes" ; then
[AC_MSG_ERROR([no gtest source at $GTEST_SOURCE])])
fi
have_gtest_source=yes
GTEST_LDFLAGS="\$(top_builddir)/ext/gtest/libgtest.a"
GTEST_LDADD="\$(top_builddir)/ext/gtest/libgtest.a"
DISTCHECK_GTEST_CONFIGURE_FLAG="--with-gtest-source=$GTEST_SOURCE"
GTEST_INCLUDES="-I$GTEST_SOURCE -I$GTEST_SOURCE/include"
fi
......
......@@ -22,13 +22,18 @@ EXTRA_DIST = http_messages.mes
CLEANFILES = *.gcno *.gcda http_messages.h http_messages.cc s-messages
lib_LTLIBRARIES = libkea-http.la
libkea_http_la_SOURCES = http_log.cc http_log.h
libkea_http_la_SOURCES = date_time.cc date_time.h
libkea_http_la_SOURCES += http_log.cc http_log.h
libkea_http_la_SOURCES += header_context.h
libkea_http_la_SOURCES += http_types.h
libkea_http_la_SOURCES += post_request.cc post_request.h
libkea_http_la_SOURCES += post_request_json.cc post_request_json.h
libkea_http_la_SOURCES += request.cc request.h
libkea_http_la_SOURCES += request_context.h
libkea_http_la_SOURCES += request_parser.cc request_parser.h
libkea_http_la_SOURCES += response.cc response.h
libkea_http_la_SOURCES += response_creator.cc response_creator.h
libkea_http_la_SOURCES += response_json.cc response_json.h
nodist_libkea_http_la_SOURCES = http_messages.cc http_messages.h
......
// Copyright (C) 2016-2017 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 <http/date_time.h>
#include <boost/date_time/time_facet.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <locale>
#include <sstream>
using namespace boost::local_time;
using namespace boost::posix_time;
namespace isc {
namespace http {
HttpDateTime::HttpDateTime()
: time_(boost::posix_time::microsec_clock::universal_time()) {
}
HttpDateTime::HttpDateTime(const boost::posix_time::ptime& t)
: time_(t) {
}
std::string
HttpDateTime::rfc1123Format() const {
return (toString("%a, %d %b %Y %H:%M:%S GMT", "RFC 1123"));
}
std::string
HttpDateTime::rfc850Format() const {
return (toString("%A, %d-%b-%y %H:%M:%S GMT", "RFC 850"));
}
std::string
HttpDateTime::asctimeFormat() const {
return (toString("%a %b %e %H:%M:%S %Y", "asctime"));
}
HttpDateTime
HttpDateTime::fromRfc1123(const std::string& time_string) {
return (HttpDateTime(fromString(time_string,
"%a, %d %b %Y %H:%M:%S %ZP",
"RFC 1123")));
}
HttpDateTime
HttpDateTime::fromRfc850(const std::string& time_string) {
return (HttpDateTime(fromString(time_string,
"%A, %d-%b-%y %H:%M:%S %ZP",
"RFC 850")));
}
HttpDateTime
HttpDateTime::fromAsctime(const std::string& time_string) {
// The asctime() puts space instead of leading 0 in days of
// month. The %e # formatter of time_input_facet doesn't deal
// with this. To deal with this, we make a copy of the string
// holding formatted time and replace a space preceding day
// number with 0. Thanks to this workaround we can use the
// %d formatter which seems to work fine. This has a side
// effect of accepting timestamps such as Sun Nov 06 08:49:37 1994,
// but it should be ok to be liberal in this case.
std::string time_string_copy(time_string);
boost::replace_all(time_string_copy, " ", " 0");
return (HttpDateTime(fromString(time_string_copy,
"%a %b %d %H:%M:%S %Y",
"asctime",
false)));
}
HttpDateTime
HttpDateTime::fromAny(const std::string& time_string) {
HttpDateTime date_time;
// Try to parse as a timestamp specified in RFC 1123 format.
try {
date_time = fromRfc1123(time_string);
return (date_time);
} catch (...) {
// Ignore errors, simply try different format.
}
// Try to parse as a timestamp specified in RFC 850 format.
try {
date_time = fromRfc850(time_string);
return (date_time);
} catch (...) {
// Ignore errors, simply try different format.
}
// Try to parse as a timestamp output by asctime() function.
try {
date_time = fromAsctime(time_string);
} catch (...) {
isc_throw(HttpTimeConversionError,
"unsupported time format of the '" << time_string
<< "'");
}
return (date_time);
}
std::string
HttpDateTime::toString(const std::string& format,
const std::string& method_name) const {
std::ostringstream s;
// Create raw pointer. The output stream will take responsibility for
// deleting the object.
time_facet* df(new time_facet(format.c_str()));
s.imbue(std::locale(std::locale::classic(), df));
// Convert time value to a string.
s << time_;
if (s.fail()) {
isc_throw(HttpTimeConversionError, "unable to convert "
<< "time value of '" << time_ << "'"
<< " to " << method_name << " format");
}
return (s.str());
}
ptime
HttpDateTime::fromString(const std::string& time_string,
const std::string& format,
const std::string& method_name,
const bool zone_check) {
std::istringstream s(time_string);
// Create raw pointer. The input stream will take responsibility for
// deleting the object.
time_input_facet* tif(new time_input_facet(format));
s.imbue(std::locale(std::locale::classic(), tif));
time_zone_ptr zone(new posix_time_zone("GMT"));
local_date_time ldt = local_microsec_clock::local_time(zone);
// Parse the time value. The stream will not automatically detect whether
// the zone is GMT. We need to check it on our own.
s >> ldt;
if (s.fail() ||
(zone_check && (!ldt.zone() ||
ldt.zone()->std_zone_abbrev() != "GMT"))) {
isc_throw(HttpTimeConversionError, "unable to parse "
<< method_name << " time value of '"
<< time_string << "'");
}
return (ldt.local_time());
}
} // namespace http
} // namespace isc
// Copyright (C) 2016 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/.
#ifndef HTTP_DATE_TIME_H
#define HTTP_DATE_TIME_H
#include <exceptions/exceptions.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <string>
namespace isc {
namespace http {
/// @brief Exception thrown when there is an error during time conversion.
///
/// The most common reason for this exception is that the unsupported time
/// format was used as an input to the time parsing functions.
class HttpTimeConversionError : public Exception {
public:
HttpTimeConversionError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief This class parses and generates time values used in HTTP.
///
/// The HTTP protocol have historically allowed 3 different date/time formats
/// (see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html). These are:
/// - Sun, 06 Nov 1994 08:49:37 GMT
/// - Sunday, 06-Nov-94 08:49:37 GMT
/// - Sun Nov 6 08:49:37 1994
///
/// The first format is preferred but implementations must also support
/// remaining two obsolete formats for compatibility. This class implements
/// parsers and generators for all three formats. It uses @c boost::posix_time
/// to represent time and date. It uses @c boost::date_time::time_facet
/// and @c boost::date_time::time_input_facet to generate and parse the
/// timestamps.
class HttpDateTime {
public:
/// @brief Default constructor.
///
/// Sets current universal time as time value.
HttpDateTime();
/// @brief Construct from @c boost::posix_time::ptime object.
///
/// @param t time value to be set.
explicit HttpDateTime(const boost::posix_time::ptime& t);
/// @brief Returns time encapsulated by this class.
///
/// @return @c boost::posix_time::ptime value encapsulated by the instance
/// of this class.
boost::posix_time::ptime getPtime() const {
return (time_);
}
/// @brief Returns time value formatted as specified in RFC 1123.
///
/// @return A string containing time value formatted as
/// Sun, 06 Nov 1994 08:49:37 GMT.
std::string rfc1123Format() const;
/// @brief Returns time value formatted as specified in RFC 850.
///
/// @return A string containing time value formatted as
/// Sunday, 06-Nov-94 08:49:37 GMT.
std::string rfc850Format() const;
/// @brief Returns time value formatted as output of ANSI C's
/// asctime().
///
/// @return A string containing time value formatted as
/// Sun Nov 6 08:49:37 1994.
std::string asctimeFormat() const;
/// @brief Creates an instance from a string containing time value
/// formatted as specified in RFC 1123.
///
/// @param time_string Input string holding formatted time value.
/// @return Instance of @ref HttpDateTime.
/// @throw HttpTimeConversionError if provided timestamp has invalid
/// format.
static HttpDateTime fromRfc1123(const std::string& time_string);
/// @brief Creates an instance from a string containing time value
/// formatted as specified in RFC 850.
///
/// @param time_string Input string holding formatted time value.
/// @return Instance of @ref HttpDateTime.
/// @throw HttpTimeConversionError if provided timestamp has invalid
/// format.
static HttpDateTime fromRfc850(const std::string& time_string);
/// @brief Creates an instance from a string containing time value
/// formatted as output from asctime() function.
///
/// @param time_string Input string holding formatted time value.
/// @return Instance of @ref HttpDateTime.
/// @throw HttpTimeConversionError if provided timestamp has invalid
/// format.
static HttpDateTime fromAsctime(const std::string& time_string);
/// @brief Creates an instance from a string containing time value
/// formatted in one of the supported formats.
///
/// This method will detect the format of the time value and parse it.
/// It tries parsing the value in the following order:
/// - a format specified in RFC 1123,
/// - a format specified in RFC 850,
/// - a format of asctime output.
///
/// @param time_string Input string holding formatted time value.
/// @return Instance of @ref HttpDateTime.
/// @throw HttpTimeConversionError if provided value doesn't match any
/// of the supported formats.
static HttpDateTime fromAny(const std::string& time_string);
private:
/// @brief Generic method formatting a time value to a specified format.
////
/// @param format Time format as accepted by the
/// @c boost::date_time::time_facet.
std::string toString(const std::string& format,
const std::string& method_name) const;
/// @brief Generic method parsing time value and converting it to the
/// instance of @c boost::posix_time::ptime.
///
/// @param time_string Input string holding formatted time value.
/// @param format Time format as accepted by the
/// @c boost::date_time::time_input_facet.
/// @param method_name Name of the expected format to appear in the error
/// message if parsing fails, e.g. RFC 1123, RFC 850 or asctime.
/// @param zone_check Indicates if the time zone name should be validated
/// during parsing. This should be set to false for the formats which
/// lack time zones (e.g. asctime).
///
/// @return Instance of the @ref boost::posix_time::ptime created from the
/// input string.
/// @throw HttpTimeConversionError if provided value doesn't match the
/// specified format.
static boost::posix_time::ptime
fromString(const std::string& time_string, const std::string& format,
const std::string& method_name, const bool zone_check = true);
/// @brief Time value encapsulated by this class instance.
boost::posix_time::ptime time_;
};
} // namespace http
} // namespace isc
#endif
// Copyright (C) 2016-2017 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/.
#ifndef HTTP_TYPES_H
#define HTTP_TYPES_H
/// @brief HTTP protocol version.
struct HttpVersion {
unsigned major_; ///< Major HTTP version.
unsigned minor_; ///< Minor HTTP version.
/// @brief Constructor.
///
/// @param major Major HTTP version.
/// @param minor Minor HTTP version.
explicit HttpVersion(const unsigned major, const unsigned minor)
: major_(major), minor_(minor) {
}
/// @brief Operator less.
///
/// @param rhs Version to compare to.
bool operator<(const HttpVersion& rhs) const {
return ((major_ < rhs.major_) ||
((major_ == rhs.major_) && (minor_ < rhs.minor_)));
}
/// @brief Operator equal.
///
/// @param rhs Version to compare to.
bool operator==(const HttpVersion& rhs) const {
return ((major_ == rhs.major_) && (minor_ == rhs.minor_));
}
/// @brief Operator not equal.
///
/// @param rhs Version to compare to.
bool operator!=(const HttpVersion& rhs) const {
return (!operator==(rhs));
}
};
#endif
// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2016-2017 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
......@@ -66,8 +66,8 @@ HttpRequest::create() {
}
// Check if the HTTP version is allowed for this request.
if (!inRequiredSet(std::make_pair(context_->http_version_major_,
context_->http_version_minor_),
if (!inRequiredSet(HttpVersion(context_->http_version_major_,
context_->http_version_minor_),
required_versions_)) {
isc_throw(BadValue, "use of HTTP version "
<< context_->http_version_major_ << "."
......@@ -144,11 +144,11 @@ HttpRequest::getUri() const {
return (context_->uri_);
}
HttpRequest::HttpVersion
HttpVersion
HttpRequest::getHttpVersion() const {
checkCreated();
return (std::make_pair(context_->http_version_major_,
context_->http_version_minor_));
return (HttpVersion(context_->http_version_major_,
context_->http_version_minor_));
}
std::string
......
// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2016-2017 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
......@@ -8,7 +8,9 @@
#define HTTP_REQUEST_H
#include <exceptions/exceptions.h>
#include <http/http_types.h>
#include <http/request_context.h>
#include <boost/shared_ptr.hpp>
#include <map>
#include <set>
#include <stdint.h>
......@@ -34,6 +36,14 @@ public:
HttpRequestError(file, line, what) { };
};
class HttpRequest;
/// @brief Pointer to the @ref HttpRequest object.
typedef boost::shared_ptr<HttpRequest> HttpRequestPtr;
/// @brief Pointer to the const @ref HttpRequest object.
typedef boost::shared_ptr<const HttpRequest> ConstHttpRequestPtr;
/// @brief Represents HTTP request message.
///
/// This object represents parsed HTTP message. The @ref HttpRequestContext
......@@ -51,9 +61,6 @@ public:
class HttpRequest {
public:
/// @brief Type of HTTP version, including major and minor version number.
typedef std::pair<unsigned int, unsigned int> HttpVersion;
/// @brief HTTP methods.
enum class Method {
HTTP_GET,
......@@ -190,6 +197,16 @@ public:
/// @brief Returns HTTP message body as string.
std::string getBody() const;
/// @brief Checks if the request has been successfully finalized.
///
/// The request is gets finalized on successfull call to
/// @ref HttpRequest::finalize.
///
/// @return true if the request has been finalized, false otherwise.
bool isFinalized() const {
return (finalized_);
}
//@}
protected:
......
// Copyright (C) 2016-2017 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 <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::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(const HttpVersion& version,
const HttpStatusCode& status_code,
const CallSetGenericBody& generic_body)
: http_version_(version), status_code_(status_code), headers_(),
body_() {
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::setBody(const std::string& body) {
body_ = 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);
}