Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #476 from glynos/master

Refactored HTTP client v2.
commit 2c4428275e2bd8a7718f1c2376d8660dc5357ca1 2 parents 28c7400 + 42ab79d
Dean Michael Berris deanberris authored
10 contrib/http_examples/simple_wget.cpp
View
@@ -21,11 +21,11 @@
namespace http = network::http;
namespace {
- std::string get_filename(const std::string& path) {
- auto index = path.find_last_of('/');
- auto filename = path.substr(index + 1);
- return filename.empty() ? "index.html" : filename;
- }
+std::string get_filename(const std::string& path) {
+ auto index = path.find_last_of('/');
+ auto filename = path.substr(index + 1);
+ return filename.empty() ? "index.html" : filename;
+}
} // namespace
int main(int argc, char* argv[]) {
489 http/src/http/v2/client/client.cpp
View
@@ -1,10 +1,10 @@
-// Copyright (C) 2013 by Glyn Matthews
+// Copyright (C) 2013, 2014 by Glyn Matthews
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
-#include <future>
#include <boost/asio/strand.hpp>
+#include <boost/asio/deadline_timer.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/range/algorithm/find_first_of.hpp>
@@ -16,14 +16,13 @@
#include <network/http/v2/client/response.hpp>
#include <network/http/v2/client/connection/tcp_resolver.hpp>
#include <network/http/v2/client/connection/normal_connection.hpp>
-#include <network/http/v2/client/connection/ssl_connection.hpp>
namespace network {
namespace http {
namespace v2 {
using boost::asio::ip::tcp;
- struct request_helper {
+ struct request_context {
std::shared_ptr<client_connection::async_connection> connection_;
@@ -35,15 +34,16 @@ namespace network {
boost::asio::streambuf request_buffer_;
boost::asio::streambuf response_buffer_;
- // TODO configure deadline timer for timeouts
-
- request_helper(std::shared_ptr<client_connection::async_connection> connection,
- request request,
- request_options options)
- : connection_(connection)
- , request_(request)
- , options_(options) { }
+ std::uint64_t total_bytes_written_, total_bytes_read_;
+ request_context(
+ std::shared_ptr<client_connection::async_connection> connection,
+ request request, request_options options)
+ : connection_(connection),
+ request_(request),
+ options_(options),
+ total_bytes_written_(0),
+ total_bytes_read_(0) {}
};
struct client::impl {
@@ -51,37 +51,46 @@ namespace network {
explicit impl(client_options options);
impl(std::unique_ptr<client_connection::async_resolver> mock_resolver,
- std::unique_ptr<client_connection::async_connection> mock_connection,
- client_options options);
+ std::unique_ptr<client_connection::async_connection>
+ mock_connection, client_options options);
+
+ ~impl();
- ~impl() noexcept;
+ void set_error(const boost::system::error_code &ec,
+ std::shared_ptr<request_context> context);
- std::future<response> execute(std::shared_ptr<request_helper> helper);
+ std::future<response> execute(std::shared_ptr<request_context> context);
+
+ void timeout(const boost::system::error_code &ec,
+ std::shared_ptr<request_context> context);
void connect(const boost::system::error_code &ec,
tcp::resolver::iterator endpoint_iterator,
- std::shared_ptr<request_helper> helper);
+ std::shared_ptr<request_context> context);
void write_request(const boost::system::error_code &ec,
- std::shared_ptr<request_helper> helper);
+ std::shared_ptr<request_context> context);
+
+ void write_body(const boost::system::error_code &ec,
+ std::size_t bytes_written,
+ std::shared_ptr<request_context> context);
void read_response(const boost::system::error_code &ec,
std::size_t bytes_written,
- std::shared_ptr<request_helper> helper);
+ std::shared_ptr<request_context> context);
void read_response_status(const boost::system::error_code &ec,
std::size_t bytes_written,
- std::shared_ptr<request_helper> helper,
- std::shared_ptr<response> res);
+ std::shared_ptr<request_context> context);
void read_response_headers(const boost::system::error_code &ec,
std::size_t bytes_read,
- std::shared_ptr<request_helper> helper,
+ std::shared_ptr<request_context> context,
std::shared_ptr<response> res);
void read_response_body(const boost::system::error_code &ec,
std::size_t bytes_read,
- std::shared_ptr<request_helper> helper,
+ std::shared_ptr<request_context> context,
std::shared_ptr<response> res);
client_options options_;
@@ -90,143 +99,227 @@ namespace network {
boost::asio::io_service::strand strand_;
std::unique_ptr<client_connection::async_resolver> resolver_;
std::shared_ptr<client_connection::async_connection> mock_connection_;
+ bool timedout_;
+ boost::asio::deadline_timer timer_;
std::thread lifetime_thread_;
};
client::impl::impl(client_options options)
- : options_(options)
- , sentinel_(new boost::asio::io_service::work(io_service_))
- , strand_(io_service_)
- , resolver_(new client_connection::tcp_resolver(io_service_, options_.cache_resolved()))
- , lifetime_thread_([=] () { io_service_.run(); }) {
-
- }
-
- client::impl::impl(std::unique_ptr<client_connection::async_resolver> mock_resolver,
- std::unique_ptr<client_connection::async_connection> mock_connection,
- client_options options)
- : options_(options)
- , sentinel_(new boost::asio::io_service::work(io_service_))
- , strand_(io_service_)
- , resolver_(std::move(mock_resolver))
- , lifetime_thread_([=] () { io_service_.run(); }) {
-
- }
-
- client::impl::~impl() noexcept {
+ : options_(options),
+ sentinel_(new boost::asio::io_service::work(io_service_)),
+ strand_(io_service_),
+ resolver_(new client_connection::tcp_resolver(
+ io_service_, options_.cache_resolved())),
+ timedout_(false),
+ timer_(io_service_),
+ lifetime_thread_([=]() { io_service_.run(); }) {}
+
+ client::impl::impl(
+ std::unique_ptr<client_connection::async_resolver> mock_resolver,
+ std::unique_ptr<client_connection::async_connection> mock_connection,
+ client_options options)
+ : options_(options),
+ sentinel_(new boost::asio::io_service::work(io_service_)),
+ strand_(io_service_),
+ resolver_(std::move(mock_resolver)),
+ timedout_(false),
+ timer_(io_service_),
+ lifetime_thread_([=]() { io_service_.run(); }) {}
+
+ client::impl::~impl() {
sentinel_.reset();
lifetime_thread_.join();
}
- std::future<response> client::impl::execute(std::shared_ptr<request_helper> helper) {
- std::future<response> res = helper->response_promise_.get_future();
- // TODO see linearize.hpp
+ void client::impl::set_error(const boost::system::error_code &ec,
+ std::shared_ptr<request_context> context) {
+ context->response_promise_.set_exception(std::make_exception_ptr(
+ std::system_error(ec.value(), std::system_category())));
+ timer_.cancel();
+ }
+
+ std::future<response> client::impl::execute(
+ std::shared_ptr<request_context> context) {
+ std::future<response> res = context->response_promise_.get_future();
// If there is no user-agent, provide one as a default.
- auto user_agent = helper->request_.header("User-Agent");
+ auto user_agent = context->request_.header("User-Agent");
if (!user_agent) {
- helper->request_.append_header("User-Agent", options_.user_agent());
+ context->request_.append_header("User-Agent", options_.user_agent());
}
- auto url = helper->request_.url();
- auto host = url.host()?
- uri::string_type(std::begin(*url.host()), std::end(*url.host())) : uri::string_type();
- auto port = url.port<std::uint16_t>()? *url.port<std::uint16_t>() : 80;
-
- resolver_->async_resolve(host, port,
- strand_.wrap(
- [=](const boost::system::error_code &ec,
- tcp::resolver::iterator endpoint_iterator) {
- connect(ec, endpoint_iterator, helper);
- }));
+ // Get the host and port from the request and resolve
+ auto url = context->request_.url();
+ auto host = url.host() ? uri::string_type(std::begin(*url.host()),
+ std::end(*url.host()))
+ : uri::string_type();
+ auto port = url.port<std::uint16_t>() ? *url.port<std::uint16_t>() : 80;
+
+ resolver_->async_resolve(
+ host, port,
+ strand_.wrap([=](const boost::system::error_code &ec,
+ tcp::resolver::iterator endpoint_iterator) {
+ connect(ec, endpoint_iterator, context);
+ }));
+
+ if (options_.timeout() > std::chrono::milliseconds(0)) {
+ timer_.expires_from_now(boost::posix_time::milliseconds(options_.timeout().count()));
+ timer_.async_wait(strand_.wrap([=](const boost::system::error_code &ec) {
+ timeout(ec, context);
+ }));
+ }
return res;
}
+ void client::impl::timeout(const boost::system::error_code &ec,
+ std::shared_ptr<request_context> context) {
+ if (!ec) {
+ context->connection_->disconnect();
+ }
+ timedout_ = true;
+ }
+
void client::impl::connect(const boost::system::error_code &ec,
tcp::resolver::iterator endpoint_iterator,
- std::shared_ptr<request_helper> helper) {
+ std::shared_ptr<request_context> context) {
if (ec) {
- helper->response_promise_.set_exception(
- std::make_exception_ptr(std::system_error(ec.value(), std::system_category())));
+ set_error(ec, context);
return;
}
- auto host = helper->request_.url().host();
+ // make a connection to an endpoint
+ auto host = context->request_.url().host();
tcp::endpoint endpoint(*endpoint_iterator);
- helper->connection_->async_connect(endpoint,
- std::string(std::begin(*host), std::end(*host)),
- strand_.wrap(
- [=] (const boost::system::error_code &ec) {
- if (ec && endpoint_iterator != tcp::resolver::iterator()) {
- // copy iterator because it is const after the lambda
- // capture
- auto it = endpoint_iterator;
- boost::system::error_code ignore;
- connect(ignore, ++it, helper);
- return;
- }
-
- write_request(ec, helper);
- }));
+ context->connection_->async_connect(
+ endpoint, std::string(std::begin(*host), std::end(*host)),
+ strand_.wrap([=](const boost::system::error_code &ec) {
+ // If there is no connection, try again on another endpoint
+ if (ec && endpoint_iterator != tcp::resolver::iterator()) {
+ // copy iterator because it is const after the lambda
+ // capture
+ auto it = endpoint_iterator;
+ boost::system::error_code ignore;
+ connect(ignore, ++it, context);
+ return;
+ }
+
+ write_request(ec, context);
+ }));
}
- void client::impl::write_request(const boost::system::error_code &ec,
- std::shared_ptr<request_helper> helper) {
+ void client::impl::write_request(
+ const boost::system::error_code &ec,
+ std::shared_ptr<request_context> context) {
+ if (timedout_) {
+ set_error(boost::asio::error::timed_out, context);
+ return;
+ }
+
if (ec) {
- helper->response_promise_.set_exception(
- std::make_exception_ptr(std::system_error(ec.value(), std::system_category())));
+ set_error(ec, context);
return;
}
- std::ostream request_stream(&helper->request_buffer_);
- request_stream << helper->request_;
+ // write the request to an I/O stream.
+ std::ostream request_stream(&context->request_buffer_);
+ request_stream << context->request_;
if (!request_stream) {
- helper->response_promise_.set_exception(
- std::make_exception_ptr(client_exception(client_error::invalid_request)));
+ context->response_promise_.set_exception(std::make_exception_ptr(
+ client_exception(client_error::invalid_request)));
+ timer_.cancel();
+ }
+
+ context->connection_->async_write(
+ context->request_buffer_,
+ strand_.wrap([=](const boost::system::error_code &ec,
+ std::size_t bytes_written) {
+ write_body(ec, bytes_written, context);
+ }));
+ }
+
+ void client::impl::write_body(const boost::system::error_code &ec,
+ std::size_t bytes_written,
+ std::shared_ptr<request_context> context) {
+ if (timedout_) {
+ set_error(boost::asio::error::timed_out, context);
+ return;
+ }
+
+ if (ec) {
+ set_error(ec, context);
+ return;
+ }
+
+ // update progress
+ context->total_bytes_written_ += bytes_written;
+ if (auto progress = context->options_.progress()) {
+ progress(client_message::transfer_direction::bytes_written,
+ context->total_bytes_written_);
}
+ // write the body to an I/O stream
+ std::ostream request_stream(&context->request_buffer_);
// TODO write payload to request_buffer_
+ if (!request_stream) {
+ context->response_promise_.set_exception(std::make_exception_ptr(
+ client_exception(client_error::invalid_request)));
+ }
- helper->connection_->async_write(helper->request_buffer_,
- strand_.wrap(
- [=] (const boost::system::error_code &ec,
- std::size_t bytes_written) {
- read_response(ec, bytes_written, helper);
- }));
+ context->connection_->async_write(
+ context->request_buffer_,
+ strand_.wrap([=](const boost::system::error_code &ec,
+ std::size_t bytes_written) {
+ read_response(ec, bytes_written, context);
+ }));
}
- void client::impl::read_response(const boost::system::error_code &ec, std::size_t,
- std::shared_ptr<request_helper> helper) {
+ void client::impl::read_response(
+ const boost::system::error_code &ec, std::size_t bytes_written,
+ std::shared_ptr<request_context> context) {
+ if (timedout_) {
+ set_error(boost::asio::error::timed_out, context);
+ return;
+ }
+
if (ec) {
- helper->response_promise_.set_exception(
- std::make_exception_ptr(std::system_error(ec.value(), std::system_category())));
+ set_error(ec, context);
return;
}
- std::shared_ptr<response> res(new response{});
- helper->connection_->async_read_until(helper->response_buffer_,
- "\r\n",
- strand_.wrap(
- [=] (const boost::system::error_code &ec,
- std::size_t bytes_read) {
- read_response_status(ec, bytes_read, helper, res);
- }));
+ // update progress.
+ context->total_bytes_written_ += bytes_written;
+ if (auto progress = context->options_.progress()) {
+ progress(client_message::transfer_direction::bytes_written,
+ context->total_bytes_written_);
+ }
+
+ // Create a response object and fill it with the status from the server.
+ context->connection_->async_read_until(
+ context->response_buffer_, "\r\n",
+ strand_.wrap([=](const boost::system::error_code &ec,
+ std::size_t bytes_read) {
+ read_response_status(ec, bytes_read, context);
+ }));
}
- void client::impl::read_response_status(const boost::system::error_code &ec,
- std::size_t,
- std::shared_ptr<request_helper> helper,
- std::shared_ptr<response> res) {
+ void client::impl::read_response_status(
+ const boost::system::error_code &ec, std::size_t,
+ std::shared_ptr<request_context> context) {
+ if (timedout_) {
+ set_error(boost::asio::error::timed_out, context);
+ return;
+ }
+
if (ec) {
- helper->response_promise_.set_exception(
- std::make_exception_ptr(std::system_error(ec.value(), std::system_category())));
+ set_error(ec, context);
return;
}
- std::istream is(&helper->response_buffer_);
+ // Update the reponse status.
+ std::istream is(&context->response_buffer_);
string_type version;
is >> version;
unsigned int status;
@@ -234,50 +327,66 @@ namespace network {
string_type message;
std::getline(is, message);
+ // if options_.follow_redirects()
+ // and if status in range 300 - 307
+ // then take the request and reset the URL from the 'Location' header
+ // restart connection
+ // Not that the 'Location' header can be a *relative* URI
+
+ std::shared_ptr<response> res(new response{});
res->set_version(version);
- res->set_status(network::http::v2::status::code(status));
+ res->set_status(network::http::status::code(status));
res->set_status_message(boost::trim_copy(message));
- helper->connection_->async_read_until(helper->response_buffer_,
- "\r\n\r\n",
- strand_.wrap(
- [=] (const boost::system::error_code &ec,
- std::size_t bytes_read) {
- read_response_headers(ec, bytes_read, helper, res);
- }));
+ // Read the response headers.
+ context->connection_->async_read_until(
+ context->response_buffer_, "\r\n\r\n",
+ strand_.wrap([=](const boost::system::error_code &ec,
+ std::size_t bytes_read) {
+ read_response_headers(ec, bytes_read, context, res);
+ }));
}
- void client::impl::read_response_headers(const boost::system::error_code &ec,
- std::size_t,
- std::shared_ptr<request_helper> helper,
- std::shared_ptr<response> res) {
+ void client::impl::read_response_headers(
+ const boost::system::error_code &ec, std::size_t,
+ std::shared_ptr<request_context> context,
+ std::shared_ptr<response> res) {
+ if (timedout_) {
+ set_error(boost::asio::error::timed_out, context);
+ return;
+ }
+
if (ec) {
- helper->response_promise_.set_exception(
- std::make_exception_ptr(std::system_error(ec.value(), std::system_category())));
+ set_error(ec, context);
return;
}
// fill headers
- std::istream is(&helper->response_buffer_);
+ std::istream is(&context->response_buffer_);
string_type header;
while (std::getline(is, header) && (header != "\r")) {
auto delim = boost::find_first_of(header, ":");
string_type key(std::begin(header), delim);
- while (*++delim == ' ') { }
+ while (*++delim == ' ') {
+ }
string_type value(delim, std::end(header));
res->add_header(key, value);
}
- helper->connection_->async_read(helper->response_buffer_,
- strand_.wrap(
- [=] (const boost::system::error_code &ec,
- std::size_t bytes_read) {
- read_response_body(ec, bytes_read, helper, res);
- }));
+ // read the response body.
+ context->connection_->async_read(
+ context->response_buffer_,
+ strand_.wrap([=](const boost::system::error_code &ec,
+ std::size_t bytes_read) {
+ read_response_body(ec, bytes_read, context, res);
+ }));
}
namespace {
- std::istream &getline_with_newline(std::istream &is, std::string &line) {
+ // I don't want to to delimit with newlines when using the input
+ // stream operator, so that's why I wrote this function.
+ std::istream &getline_with_newline(std::istream &is,
+ std::string &line) {
line.clear();
std::istream::sentry se(is, true);
@@ -286,71 +395,85 @@ namespace network {
while (true) {
int c = sb->sbumpc();
switch (c) {
- case EOF:
- if (line.empty()) {
- is.setstate(std::ios::eofbit);
- }
- return is;
- default:
- line += static_cast<char>(c);
+ case EOF:
+ if (line.empty()) {
+ is.setstate(std::ios::eofbit);
+ }
+ return is;
+ default:
+ line += static_cast<char>(c);
}
}
}
- } // namespace
+ } // namespace
+
+ void client::impl::read_response_body(
+ const boost::system::error_code &ec, std::size_t bytes_read,
+ std::shared_ptr<request_context> context,
+ std::shared_ptr<response> res) {
+ if (timedout_) {
+ set_error(boost::asio::error::timed_out, context);
+ return;
+ }
+
+ if (ec && ec != boost::asio::error::eof) {
+ set_error(ec, context);
+ return;
+ }
- void client::impl::read_response_body(const boost::system::error_code &ec,
- std::size_t bytes_read,
- std::shared_ptr<request_helper> helper,
- std::shared_ptr<response> res) {
+ // update progress.
+ context->total_bytes_read_ += bytes_read;
+ if (auto progress = context->options_.progress()) {
+ progress(client_message::transfer_direction::bytes_read,
+ context->total_bytes_read_);
+ }
+
+ // If there's no data else to read, then set the response and exit.
if (bytes_read == 0) {
- helper->response_promise_.set_value(*res);
+ context->response_promise_.set_value(*res);
+ timer_.cancel();
return;
}
- std::istream is(&helper->response_buffer_);
+ std::istream is(&context->response_buffer_);
string_type line;
+ line.reserve(bytes_read);
while (!getline_with_newline(is, line).eof()) {
- res->append_body(line);
+ res->append_body(std::move(line));
}
- helper->connection_->async_read(helper->response_buffer_,
- strand_.wrap(
- [=] (const boost::system::error_code &ec,
- std::size_t bytes_read) {
- read_response_body(ec, bytes_read, helper, res);
- }));
+ // Keep reading the response body until we have nothing else to read.
+ context->connection_->async_read(
+ context->response_buffer_,
+ strand_.wrap([=](const boost::system::error_code &ec,
+ std::size_t bytes_read) {
+ read_response_body(ec, bytes_read, context, res);
+ }));
}
- client::client(client_options options)
- : pimpl_(new impl(options)) {
-
- }
+ client::client(client_options options) : pimpl_(new impl(options)) {}
- client::client(std::unique_ptr<client_connection::async_resolver> mock_resolver,
- std::unique_ptr<client_connection::async_connection> mock_connection,
- client_options options)
- : pimpl_(new impl(std::move(mock_resolver), std::move(mock_connection), options)) { }
+ client::client(
+ std::unique_ptr<client_connection::async_resolver> mock_resolver,
+ std::unique_ptr<client_connection::async_connection> mock_connection,
+ client_options options)
+ : pimpl_(new impl(std::move(mock_resolver),
+ std::move(mock_connection), options)) {}
- client::~client() noexcept {
- delete pimpl_;
- }
+ client::~client() { }
- std::future<response> client::execute(request req, request_options options) {
+ std::future<response> client::execute(request req,
+ request_options options) {
std::shared_ptr<client_connection::async_connection> connection;
if (pimpl_->mock_connection_) {
connection = pimpl_->mock_connection_;
- }
- else {
+ } else {
// TODO factory based on HTTP or HTTPS
- if (req.is_https()) {
- connection = std::make_shared<client_connection::ssl_connection>(pimpl_->io_service_,
- pimpl_->options_);
- }
- else {
- connection = std::make_shared<client_connection::normal_connection>(pimpl_->io_service_);
- }
+ connection = std::make_shared<client_connection::normal_connection>(
+ pimpl_->io_service_);
}
- return pimpl_->execute(std::make_shared<request_helper>(connection, req, options));
+ return pimpl_->execute(
+ std::make_shared<request_context>(connection, req, options));
}
std::future<response> client::get(request req, request_options options) {
@@ -368,7 +491,8 @@ namespace network {
return execute(req, options);
}
- std::future<response> client::delete_(request req, request_options options) {
+ std::future<response> client::delete_(request req,
+ request_options options) {
req.method(method::delete_);
return execute(req, options);
}
@@ -378,10 +502,11 @@ namespace network {
return execute(req, options);
}
- std::future<response> client::options(request req, request_options options) {
+ std::future<response> client::options(request req,
+ request_options options) {
req.method(method::options);
return execute(req, options);
}
- } // namespace v2
- } // namespace http
-} // namespace network
+ } // namespace v2
+ } // namespace http
+} // namespace network
74 http/src/http/v2/client/client_errors.cpp
View
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 by Glyn Matthews
+// Copyright (C) 2013, 2014 by Glyn Matthews
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
@@ -9,71 +9,57 @@
namespace network {
namespace http {
- namespace v2 {
-
+ inline namespace v2 {
class client_category_impl : public std::error_category {
- public:
-
- client_category_impl() = default;
+ public:
+ client_category_impl() = default;
- virtual ~client_category_impl() noexcept;
+ virtual ~client_category_impl() noexcept override;
- virtual const char *name() const noexcept;
-
- virtual std::string message(int ev) const;
+ virtual const char *name() const noexcept override;
+ virtual std::string message(int ev) const override;
};
- client_category_impl::~client_category_impl() noexcept {
-
- }
+ client_category_impl::~client_category_impl() noexcept {}
const char *client_category_impl::name() const noexcept {
- static const char name[] = "client_error";
- return name;
+ static const char name[] = "client_error";
+ return name;
}
std::string client_category_impl::message(int ev) const {
- switch (client_error(ev)) {
-
- case client_error::invalid_request:
- return "Invalid HTTP request.";
- case client_error::invalid_response:
- return "Invalid HTTP response.";
- default:
- break;
- }
- return "Unknown client error.";
+ switch (client_error(ev)) {
+
+ case client_error::invalid_request:
+ return "Invalid HTTP request.";
+ case client_error::invalid_response:
+ return "Invalid HTTP response.";
+ default:
+ break;
+ }
+ return "Unknown client error.";
}
const std::error_category &client_category() {
- static client_category_impl client_category;
- return client_category;
+ static client_category_impl client_category;
+ return client_category;
}
std::error_code make_error_code(client_error e) {
- return std::error_code(static_cast<int>(e), client_category());
+ return std::error_code(static_cast<int>(e), client_category());
}
invalid_url::invalid_url()
- : std::invalid_argument("Requires HTTP or HTTPS URL.") {
-
- }
+ : std::invalid_argument("Requires HTTP or HTTPS URL.") {}
- invalid_url::~invalid_url() noexcept {
-
- }
+ invalid_url::~invalid_url() noexcept {}
client_exception::client_exception(client_error error)
- : std::system_error(make_error_code(error)) {
-
- }
-
- client_exception::~client_exception() noexcept {
-
- }
+ : std::system_error(make_error_code(error)) {}
- } // namespace v2
- } // namespace network
-} // namespace network
+ client_exception::~client_exception() noexcept {}
+ } // namespace v2
+ } // namespace http
+} // namespace network
682 http/src/network/http/v2/client/client.hpp
View
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 by Glyn Matthews
+// Copyright (C) 2013, 2014 by Glyn Matthews
// Copyright Dean Michael Berris 2012.
// Copyright Google, Inc. 2012.
// Distributed under the Boost Software License, Version 1.0.
@@ -20,7 +20,6 @@
#include <string>
#include <vector>
#include <chrono>
-#include <boost/asio/io_service.hpp>
#include <boost/optional.hpp>
#include <network/config.hpp>
#include <network/version.hpp>
@@ -28,353 +27,338 @@
#include <network/http/v2/client/response.hpp>
namespace network {
- namespace http {
- namespace v2 {
- namespace client_connection {
- class async_resolver;
- class async_connection;
- } // namespace client_connection
-
- /**
- * \ingroup http_client
- * \class client_options network/http/v2/client/client.hpp network/http/v2/client.hpp
- * \brief A set of options to configure an HTTP client.
- */
- class client_options {
-
- public:
-
- /**
- * \brief Constructor.
- */
- client_options()
- : io_service_(boost::none)
- , follow_redirects_(false)
- , cache_resolved_(false)
- , use_proxy_(false)
- , always_verify_peer_(false)
- , user_agent_(std::string("cpp-netlib/") + NETLIB_VERSION)
- , timeout_(30000) { }
-
- /**
- * \brief Copy constructor.
- */
- client_options(const client_options &other)
- : io_service_(other.io_service_)
- , follow_redirects_(other.follow_redirects_)
- , cache_resolved_(other.cache_resolved_)
- , use_proxy_(other.use_proxy_)
- , always_verify_peer_(other.always_verify_peer_)
- , user_agent_(other.user_agent_)
- , timeout_(other.timeout_)
- , openssl_certificate_paths_(other.openssl_certificate_paths_)
- , openssl_verify_paths_(other.openssl_verify_paths_) { }
-
- /**
- * \brief Move constructor.
- */
- client_options(client_options &&other)
- : io_service_(std::move(other.io_service_))
- , follow_redirects_(std::move(other.follow_redirects_))
- , cache_resolved_(std::move(other.cache_resolved_))
- , use_proxy_(std::move(other.use_proxy_))
- , always_verify_peer_(std::move(other.always_verify_peer_))
- , user_agent_(std::move(other.user_agent_))
- , timeout_(std::move(other.timeout_))
- , openssl_certificate_paths_(std::move(other.openssl_certificate_paths_))
- , openssl_verify_paths_(std::move(other.openssl_verify_paths_)) { }
-
- /**
- * \brief Assignment operator.
- */
- client_options &operator = (client_options other) {
- other.swap(*this);
- return *this;
- }
-
- /**
- * \brief Destructor.
- */
- ~client_options() noexcept {
-
- }
-
- /**
- * \brief Swap.
- */
- void swap(client_options &other) noexcept {
- using std::swap;
- std::swap(io_service_, other.io_service_);
- swap(follow_redirects_, other.follow_redirects_);
- swap(cache_resolved_, other.cache_resolved_);
- swap(use_proxy_, other.use_proxy_);
- swap(always_verify_peer_, other.always_verify_peer_);
- swap(user_agent_, other.user_agent_);
- swap(timeout_, other.timeout_);
- swap(openssl_certificate_paths_, other.openssl_certificate_paths_);
- swap(openssl_verify_paths_, other.openssl_verify_paths_);
- }
-
- /**
- * \brief Overrides the client's I/O service.
- * \param io_service The new io_service object to use.
- */
- client_options &io_service(boost::asio::io_service &io_service) {
- io_service_ = io_service;
- return *this;
- }
-
- /**
- * \brief Gets the overridden I/O service.
- * \returns An optional io_service object.
- */
- boost::optional<boost::asio::io_service &> io_service() const {
- return io_service_;
- }
-
- /**
- * \brief Tells the client to follow redirects.
- * \param follow_redirects If \c true, then the client must
- * follow redirects, if \c false it doesn't.
- * \returns \c *this
- */
- client_options &follow_redirects(bool follow_redirects) {
- follow_redirects_ = follow_redirects;
- return *this;
- }
-
- /**
- * \brief Tests if the client follows redirects.
- * \returns \c true if the client follows redirects, \c false
- * otherwise.
- */
- bool follow_redirects() const {
- return follow_redirects_;
- }
-
- /**
- * \brief Tells the client to cache resolved connections.
- * \param cache_resolved If \c true, then the client must
- * cache resolved connections, if \c false it
- * doesn't.
- * \returns \c *this
- */
- client_options &cache_resolved(bool cache_resolved) {
- cache_resolved_ = cache_resolved;
- return *this;
- }
-
- /**
- * \brief Tests if the client caches resolved connections.
- * \returns \c true if the client caches resolved connections,
- * \c false otherwise.
- */
- bool cache_resolved() const {
- return cache_resolved_;
- }
-
- /**
- * \brief Tells the client to use a proxy.
- * \param use_proxy If \c true, then the client must use a
- * proxy, if \c false it doesn't.
- */
- client_options &use_proxy(bool use_proxy) {
- use_proxy_ = use_proxy;
- return *this;
- }
-
- /**
- * \brief Tests if the client uses a proxy.
- * \returns \c true if the client uses a proxy, \c false
- * otherwise.
- */
- bool use_proxy() const {
- return use_proxy_;
- }
-
- /**
- * \brief Sets the client timeout in milliseconds.
- * \param timeout The timeout value in milliseconds.
- */
- client_options &timeout(std::chrono::milliseconds timeout) {
- timeout_ = timeout;
- return *this;
- }
-
- /**
- * \brief Gets the current timeout value.
- * \returns The timeout value in milliseconds.
- */
- std::chrono::milliseconds timeout() const {
- return timeout_;
- }
-
- /**
- * \brief Adds an OpenSSL certificate path.
- * \param path The certificate path.
- */
- client_options &openssl_certificate_path(std::string path) {
- openssl_certificate_paths_.emplace_back(std::move(path));
- return *this;
- }
-
- /**
- * \brief Returns a list of OpenSSL certificate paths.
- * \returns A list of OpenSSL certificate paths.
- */
- std::vector<std::string> openssl_certificate_paths() const {
- return openssl_certificate_paths_;
- }
-
- /**
- * \brief Adds an OpenSSL verify path.
- * \param path The verify path.
- */
- client_options &openssl_verify_path(std::string path) {
- openssl_verify_paths_.emplace_back(std::move(path));
- return *this;
- }
-
- /**
- * \brief Returns a list of OpenSSL verify paths.
- * \returns A list of OpenSSL verify paths.
- */
- std::vector<std::string> openssl_verify_paths() const {
- return openssl_verify_paths_;
- }
-
- client_options &always_verify_peer(bool always_verify_peer) {
- always_verify_peer_ = always_verify_peer;
- return *this;
- }
-
- bool always_verify_peer() const {
- return always_verify_peer_;
- }
-
- client_options &user_agent(const std::string &user_agent) {
- user_agent_ = user_agent;
- return *this;
- }
-
- std::string user_agent() const {
- return user_agent_;
- }
-
- private:
-
- boost::optional<boost::asio::io_service &> io_service_;
- bool follow_redirects_;
- bool cache_resolved_;
- bool use_proxy_;
- bool always_verify_peer_;
- std::string user_agent_;
- std::chrono::milliseconds timeout_;
- std::vector<std::string> openssl_certificate_paths_;
- std::vector<std::string> openssl_verify_paths_;
-
- };
-
- inline
- void swap(client_options &lhs, client_options &rhs) noexcept {
- lhs.swap(rhs);
- }
-
- typedef client_message::request_options request_options;
- typedef client_message::request request;
- typedef client_message::response response;
-
- /**
- * \ingroup http_client
- * \class client network/http/v2/client/client.hpp network/http/v2/client.hpp
- * \brief A class that encapsulates the operations and methods
- * for communicating with an HTTP server.
- */
- class client {
-
- client(const client&) = delete;
- client& operator=(const client&) = delete;
-
- public:
-
- /**
- * \typedef string_type
- * \brief The client string_type.
- */
- typedef request::string_type string_type;
-
- /**
- * \brief Constructor.
- * \param options Client options.
- */
- explicit client(client_options options = client_options());
-
- client(std::unique_ptr<client_connection::async_resolver> mock_resolver,
- std::unique_ptr<client_connection::async_connection> mock_connection,
- client_options options = client_options());
-
- /**
- * \brief Destructor.
- */
- ~client() noexcept;
-
- /**
- * \brief Executes an HTTP request.
- * \param req The request object.
- * \param options The request options.
- */
- std::future<response> execute(request req, request_options options = request_options());
-
- /**
- * \brief Executes an HTTP GET request.
- * \param req The request object.
- * \param options The request options.
- */
- std::future<response> get(request req, request_options options = request_options());
-
- /**
- * \brief Executes an HTTP POST request.
- * \param req The request object.
- * \param options The request options.
- */
- std::future<response> post(request req, request_options options = request_options());
-
- /**
- * \brief Executes an HTTP PUT request.
- * \param req The request object.
- * \param options The request options.
- */
- std::future<response> put(request req, request_options options = request_options());
-
- /**
- * \brief Executes an HTTP DELETE request.
- * \param req The request object.
- * \param options The request options.
- */
- std::future<response> delete_(request req, request_options options = request_options());
-
- /**
- * \brief Executes an HTTP HEAD request.
- * \param req The request object.
- * \param options The request options.
- */
- std::future<response> head(request req, request_options options = request_options());
-
- /**
- * \brief Executes an HTTP OPTIONS request.
- * \param req The request object.
- * \param options The request options.
- */
- std::future<response> options(request req, request_options options = request_options());
-
- private:
-
- struct impl;
- impl *pimpl_;
-
- };
- } // namespace v2
- } // namespace http
+namespace http {
+inline namespace v2 {
+namespace client_connection {
+class async_resolver;
+class async_connection;
+} // namespace client_connection
+
+/**
+ * \ingroup http_client
+ * \class client_options network/http/v2/client/client.hpp network/http/v2/client.hpp
+ * \brief A set of options to configure an HTTP client.
+ */
+class client_options {
+
+public:
+
+ /**
+ * \brief Constructor.
+ */
+ client_options()
+ : follow_redirects_(false)
+ , cache_resolved_(false)
+ , use_proxy_(false)
+ , always_verify_peer_(false)
+ , user_agent_(std::string("cpp-netlib/") + NETLIB_VERSION)
+ , timeout_(30000) { }
+
+ /**
+ * \brief Copy constructor.
+ */
+ client_options(const client_options &other) = default;
+
+ /**
+ * \brief Move constructor.
+ */
+ client_options(client_options &&other) = default;
+
+ /**
+ * \brief Assignment operator.
+ */
+ client_options &operator = (client_options other) {
+ other.swap(*this);
+ return *this;
+ }
+
+ /**
+ * \brief Destructor.
+ */
+ ~client_options() = default;
+
+ /**
+ * \brief Swap.
+ */
+ void swap(client_options &other) noexcept {
+ using std::swap;
+ swap(follow_redirects_, other.follow_redirects_);
+ swap(cache_resolved_, other.cache_resolved_);
+ swap(use_proxy_, other.use_proxy_);
+ swap(always_verify_peer_, other.always_verify_peer_);
+ swap(user_agent_, other.user_agent_);
+ swap(timeout_, other.timeout_);
+ swap(openssl_certificate_paths_, other.openssl_certificate_paths_);
+ swap(openssl_verify_paths_, other.openssl_verify_paths_);
+ }
+
+ /**
+ * \brief Tells the client to follow redirects.
+ * \param follow_redirects If \c true, then the client must
+ * follow redirects, if \c false it doesn't.
+ * \returns \c *this
+ */
+ client_options &follow_redirects(bool follow_redirects) {
+ follow_redirects_ = follow_redirects;
+ return *this;
+ }
+
+ /**
+ * \brief Tests if the client follows redirects.
+ * \returns \c true if the client follows redirects, \c false
+ * otherwise.
+ */
+ bool follow_redirects() const {
+ return follow_redirects_;
+ }
+
+ /**
+ * \brief Tells the client to cache resolved connections.
+ * \param cache_resolved If \c true, then the client must
+ * cache resolved connections, if \c false it
+ * doesn't.
+ * \returns \c *this
+ */
+ client_options &cache_resolved(bool cache_resolved) {
+ cache_resolved_ = cache_resolved;
+ return *this;
+ }
+
+ /**
+ * \brief Tests if the client caches resolved connections.
+ * \returns \c true if the client caches resolved connections,
+ * \c false otherwise.
+ */
+ bool cache_resolved() const {
+ return cache_resolved_;
+ }
+
+ /**
+ * \brief Tells the client to use a proxy.
+ * \param use_proxy If \c true, then the client must use a
+ * proxy, if \c false it doesn't.
+ * \returns \c *this
+ */
+ client_options &use_proxy(bool use_proxy) {
+ use_proxy_ = use_proxy;
+ return *this;
+ }
+
+ /**
+ * \brief Tests if the client uses a proxy.
+ * \returns \c true if the client uses a proxy, \c false
+ * otherwise.
+ */
+ bool use_proxy() const {
+ return use_proxy_;
+ }
+
+ /**
+ * \brief Sets the client timeout in milliseconds.
+ * \param timeout The timeout value in milliseconds.
+ * \returns \c *this
+ */
+ client_options &timeout(std::chrono::milliseconds timeout) {
+ timeout_ = timeout;
+ return *this;
+ }
+
+ /**
+ * \brief Gets the current timeout value.
+ * \returns The timeout value in milliseconds.
+ */
+ std::chrono::milliseconds timeout() const {
+ return timeout_;
+ }
+
+ /**
+ * \brief Adds an OpenSSL certificate path.
+ * \param path The certificate path.
+ * \returns \c *this
+ */
+ client_options &openssl_certificate_path(std::string path) {
+ openssl_certificate_paths_.emplace_back(std::move(path));
+ return *this;
+ }
+
+ /**
+ * \brief Returns a list of OpenSSL certificate paths.
+ * \returns A list of OpenSSL certificate paths.
+ */
+ std::vector<std::string> openssl_certificate_paths() const {
+ return openssl_certificate_paths_;
+ }
+
+ /**
+ * \brief Adds an OpenSSL verify path.
+ * \param path The verify path.
+ * \returns \c *this
+ */
+ client_options &openssl_verify_path(std::string path) {
+ openssl_verify_paths_.emplace_back(std::move(path));
+ return *this;
+ }
+
+ /**
+ * \brief Returns a list of OpenSSL verify paths.
+ * \returns A list of OpenSSL verify paths.
+ */
+ std::vector<std::string> openssl_verify_paths() const {
+ return openssl_verify_paths_;
+ }
+
+ /**
+ * \brief
+ * \returns \c *this
+ */
+ client_options &always_verify_peer(bool always_verify_peer) {
+ always_verify_peer_ = always_verify_peer;
+ return *this;
+ }
+
+ /**
+ * \brief
+ * \returns
+ */
+ bool always_verify_peer() const {
+ return always_verify_peer_;
+ }
+
+ /**
+ * \brief
+ * \returns \c *this
+ */
+ client_options &user_agent(const std::string &user_agent) {
+ user_agent_ = user_agent;
+ return *this;
+ }
+
+ /**
+ * \brief
+ * \returns
+ */
+ std::string user_agent() const {
+ return user_agent_;
+ }
+
+private:
+
+ bool follow_redirects_;
+ bool cache_resolved_;
+ bool use_proxy_;
+ bool always_verify_peer_;
+ std::string user_agent_;
+ std::chrono::milliseconds timeout_;
+ std::vector<std::string> openssl_certificate_paths_;
+ std::vector<std::string> openssl_verify_paths_;
+
+};
+
+/**
+ * \brief
+ * \param lhs
+ * \param rhs
+ */
+inline
+void swap(client_options &lhs, client_options &rhs) noexcept {
+ lhs.swap(rhs);
+}
+
+typedef client_message::request_options request_options;
+typedef client_message::request request;
+typedef client_message::response response;
+
+/**
+ * \ingroup http_client
+ * \class client network/http/v2/client/client.hpp network/http/v2/client.hpp
+ * \brief A class that encapsulates the operations and methods
+ * for communicating with an HTTP server.
+ */
+class client {
+
+ client(const client&) = delete;
+ client& operator=(const client&) = delete;
+
+public:
+
+ /**
+ * \typedef string_type
+ * \brief The client string_type.
+ */
+ typedef request::string_type string_type;
+
+ /**
+ * \brief Constructor.
+ * \param options Client options.
+ */
+ explicit client(client_options options = client_options());
+
+ client(std::unique_ptr<client_connection::async_resolver> mock_resolver,
+ std::unique_ptr<client_connection::async_connection> mock_connection,
+ client_options options = client_options());
+
+ /**
+ * \brief Destructor.
+ */
+ ~client();
+
+ /**
+ * \brief Executes an HTTP request.
+ * \param req The request object.
+ * \param options The request options.
+ */
+ std::future<response> execute(request req, request_options options = request_options());
+
+ /**
+ * \brief Executes an HTTP GET request.
+ * \param req The request object.
+ * \param options The request options.
+ */
+ std::future<response> get(request req, request_options options = request_options());
+
+ /**
+ * \brief Executes an HTTP POST request.
+ * \param req The request object.
+ * \param options The request options.
+ */
+ std::future<response> post(request req, request_options options = request_options());
+
+ /**
+ * \brief Executes an HTTP PUT request.
+ * \param req The request object.
+ * \param options The request options.
+ */
+ std::future<response> put(request req, request_options options = request_options());
+
+ /**
+ * \brief Executes an HTTP DELETE request.
+ * \param req The request object.
+ * \param options The request options.
+ */
+ std::future<response> delete_(request req, request_options options = request_options());
+
+ /**
+ * \brief Executes an HTTP HEAD request.
+ * \param req The request object.
+ * \param options The request options.
+ */
+ std::future<response> head(request req, request_options options = request_options());
+
+ /**
+ * \brief Executes an HTTP OPTIONS request.
+ * \param req The request object.
+ * \param options The request options.
+ */
+ std::future<response> options(request req, request_options options = request_options());
+
+private:
+
+ struct impl;
+ std::unique_ptr<impl> pimpl_;
+
+};
+} // namespace v2
+} // namespace http
} // namespace network
#endif // NETWORK_HTTP_V2_CLIENT_CLIENT_INC
138 http/src/network/http/v2/client/client_errors.hpp
View
@@ -17,75 +17,75 @@
#include <network/config.hpp>
namespace network {
- namespace http {
- namespace v2 {
- /**
- * \ingroup http_client
- * \enum client_error network/http/v2/client/client_errors.hpp network/http/v2/client.hpp
- * \brief An enumeration of all types of client error.
- */
- enum class client_error {
- // request
- invalid_request,
-
- // response
- invalid_response,
- };
-
- /**
- * \brief Gets the error category for HTTP client errors.
- */
- const std::error_category &client_category();
-
- /**
- * \brief Makes an error code object from a client_error enum.
- */
- std::error_code make_error_code(client_error e);
-
- /**
- * \ingroup http_client
- * \class invalid_url network/http/v2/client/client_errors.hpp network/http/v2/client.hpp
- * \brief An exception thrown if the URL provides is invalid.
- */
- class invalid_url : public std::invalid_argument {
-
- public:
-
- /**
- * \brief Constructor.
- */
- explicit invalid_url();
-
- /**
- * \brief Destructor.
- */
- virtual ~invalid_url() noexcept;
-
- };
-
- /**
- * \ingroup http_client
- * \class client_exception network/http/v2/client/client_errors.hpp network/http/v2/client.hpp
- * \brief An exception thrown when there is a client error.
- */
- class client_exception : public std::system_error {
-
- public:
-
- /**
- * \brief Constructor.
- */
- explicit client_exception(client_error error);
-
- /**
- * \brief Destructor.
- */
- virtual ~client_exception() noexcept;
-
- };
-
- } // namespace v2
- } // namespace http
+namespace http {
+inline namespace v2 {
+/**
+ * \ingroup http_client
+ * \enum client_error network/http/v2/client/client_errors.hpp network/http/v2/client.hpp
+ * \brief An enumeration of all types of client error.
+ */
+enum class client_error {
+ // request
+ invalid_request,
+
+ // response
+ invalid_response,
+};
+
+/**
+ * \brief Gets the error category for HTTP client errors.
+ */
+const std::error_category &client_category();
+
+/**
+ * \brief Makes an error code object from a client_error enum.
+ */
+std::error_code make_error_code(client_error e);
+
+/**
+ * \ingroup http_client
+ * \class invalid_url network/http/v2/client/client_errors.hpp network/http/v2/client.hpp
+ * \brief An exception thrown if the URL provides is invalid.
+ */
+class invalid_url : public std::invalid_argument {
+
+public:
+
+ /**
+ * \brief Constructor.
+ */
+ explicit invalid_url();
+
+ /**
+ * \brief Destructor.
+ */
+ virtual ~invalid_url() noexcept;
+
+};
+
+/**
+ * \ingroup http_client
+ * \class client_exception network/http/v2/client/client_errors.hpp network/http/v2/client.hpp
+ * \brief An exception thrown when there is a client error.
+ */
+class client_exception : public std::system_error {
+
+public:
+
+ /**
+ * \brief Constructor.
+ */
+ explicit client_exception(client_error error);
+
+ /**
+ * \brief Destructor.
+ */
+ virtual ~client_exception() noexcept;
+
+};
+
+} // namespace v2
+} // namespace http
} // namespace network
#endif // NETWORK_HTTP_V2_CLIENT_CLIENT_ERRORS_INC
7 http/src/network/http/v2/client/connection/async_connection.hpp
View
@@ -20,7 +20,7 @@
namespace network {
namespace http {
- namespace v2 {
+ inline namespace v2 {
namespace client_connection {
/**
* \class async_connection network/http/v2/client/connection/async_connection.hpp
@@ -95,6 +95,11 @@ namespace network {
read_callback callback) = 0;
/**
+ * \brief Breaks the connection.
+ */
+ virtual void disconnect() = 0;
+
+ /**
* \brief Cancels an operation on a connection.
*/
virtual void cancel() = 0;
2  http/src/network/http/v2/client/connection/async_resolver.hpp
View
@@ -18,7 +18,7 @@
namespace network {
namespace http {
- namespace v2 {
+ inline namespace v2 {
namespace client_connection {
/**
* \class async_resolver network/http/v2/client/connection/async_resolver.hpp
116 http/src/network/http/v2/client/connection/endpoint_cache.hpp
View
@@ -18,52 +18,80 @@
namespace network {
namespace http {
- namespace v2 {
+ inline namespace v2 {
namespace client_connection {
- class endpoint_cache {
-
- typedef boost::asio::ip::tcp::resolver resolver;
-
- typedef resolver::iterator resolver_iterator;
-
- typedef std::unordered_map<std::string,
- resolver_iterator> cache_type;
-
- public:
-
- typedef cache_type::iterator iterator;
-
- cache_type::iterator begin() {
- return endpoints_.begin();
- }
-
- cache_type::iterator end() {
- return endpoints_.end();
- }
-
- void insert(const std::string &host,
- resolver_iterator endpoint_iterator) {
- endpoints_.insert(std::make_pair(host, endpoint_iterator));
- }
-
- iterator find(const std::string &host) {
- return endpoints_.find(boost::to_lower_copy(host));
- }
-
- void clear() {
- endpoint_cache().swap(*this);
- }
-
- void swap(endpoint_cache &other) noexcept {
- endpoints_.swap(other.endpoints_);
- }
-
- private:
-
- cache_type endpoints_;
-
- };
+ /**
+ * \class endpoint_cache network/http/v2/client/connection/endpoint_cache.hpp
+ * \brief
+ */
+ class endpoint_cache {
+
+ /**
+ * \brief
+ */
+ typedef boost::asio::ip::tcp::resolver resolver;
+
+ typedef resolver::iterator resolver_iterator;
+
+ typedef std::unordered_map<std::string,
+ resolver_iterator> cache_type;
+
+ public:
+
+ /**
+ * \brief
+ */
+ typedef cache_type::iterator iterator;
+
+ /**
+ * \brief
+ */
+ cache_type::iterator begin() {
+ return endpoints_.begin();
+ }
+
+ /**
+ * \brief
+ */
+ cache_type::iterator end() {
+ return endpoints_.end();
+ }
+
+ /**
+ * \brief
+ */
+ void insert(const std::string &host,
+ resolver_iterator endpoint_iterator) {
+ endpoints_.insert(std::make_pair(host, endpoint_iterator));
+ }
+
+ /**
+ * \brief
+ */
+ iterator find(const std::string &host) {
+ return endpoints_.find(boost::to_lower_copy(host));
+ }
+
+ /**
+ * \brief
+ */
+ void clear() {
+ endpoint_cache().swap(*this);
+ }
+
+ /**
+ * \brief
+ */
+ void swap(endpoint_cache &other) noexcept {
+ endpoints_.swap(other.endpoints_);
+ }
+
+ private:
+
+ cache_type endpoints_;
+
+ };
} // namespace client_connection
} // namespace v2
} // namespace http
113 http/src/network/http/v2/client/connection/normal_connection.hpp
View
@@ -21,59 +21,80 @@
namespace network {
namespace http {
- namespace v2 {
+ inline namespace v2 {
namespace client_connection {
- class normal_connection : public async_connection {
- normal_connection(const normal_connection &) = delete;
- normal_connection &operator = (const normal_connection &) = delete;
-
- public:
-
- explicit normal_connection(boost::asio::io_service &io_service)
- : io_service_(io_service) {
-
- }
-
- virtual ~normal_connection() noexcept {
-
- }
-
- virtual void async_connect(const boost::asio::ip::tcp::endpoint &endpoint,
- const std::string &host,
- connect_callback callback) {
- using boost::asio::ip::tcp;
- socket_.reset(new tcp::socket{io_service_});
- socket_->async_connect(endpoint, callback);
+ /**
+ * \class
+ * \brief
+ */
+ class normal_connection : public async_connection {
+
+ normal_connection(const normal_connection &) = delete;
+ normal_connection &operator = (const normal_connection &) = delete;
+
+ public:
+
+ /**
+ * \brief
+ */
+ explicit normal_connection(boost::asio::io_service &io_service)
+ : io_service_(io_service) {
+
+ }
+
+ /**
+ * \brief Destructor.
+ */
+ virtual ~normal_connection() noexcept {
+
+ }
+
+ virtual void async_connect(const boost::asio::ip::tcp::endpoint &endpoint,
+ const std::string &host,
+ connect_callback callback) {
+ using boost::asio::ip::tcp;
+ socket_.reset(new tcp::socket{io_service_});
+ socket_->async_connect(endpoint, callback);
+ }
+
+ virtual void async_write(boost::asio::streambuf &command_streambuf,
+ write_callback callback) {
+ boost::asio::async_write(*socket_, command_streambuf, callback);
+ }
+
+ virtual void async_read_until(boost::asio::streambuf &command_streambuf,
+ const std::string &delim,
+ read_callback callback) {
+ boost::asio::async_read_until(*socket_, command_streambuf, delim, callback);
+ }
+
+ virtual void async_read(boost::asio::streambuf &command_streambuf,
+ read_callback callback) {
+ boost::asio::async_read(*socket_, command_streambuf,
+ boost::asio::transfer_at_least(1), callback);
+ }
+
+ virtual void disconnect() {
+ if (socket_ && socket_->is_open()) {
+ boost::system::error_code ec;
+ socket_->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
+ if (!ec) {
+ socket_->close(ec);
+ }
}
+ }
- virtual void async_write(boost::asio::streambuf &command_streambuf,
- write_callback callback) {
- boost::asio::async_write(*socket_, command_streambuf, callback);
- }
-
- virtual void async_read_until(boost::asio::streambuf &command_streambuf,
- const std::string &delim,
- read_callback callback) {
- boost::asio::async_read_until(*socket_, command_streambuf, delim, callback);
- }
-
- virtual void async_read(boost::asio::streambuf &command_streambuf,
- read_callback callback) {
- boost::asio::async_read(*socket_, command_streambuf,
- boost::asio::transfer_at_least(1), callback);
- }
-
- virtual void cancel() {
- socket_->cancel();
- }
+ virtual void cancel() {
+ socket_->cancel();
+ }
- private:
+ private:
- boost::asio::io_service &io_service_;
- std::unique_ptr<boost::asio::ip::tcp::socket> socket_;
+ boost::asio::io_service &io_service_;
+ std::unique_ptr<boost::asio::ip::tcp::socket> socket_;
- };
+ };
} // namespace client_connection
} // namespace v2
} // namespace http
182 http/src/network/http/v2/client/connection/ssl_connection.hpp
View
@@ -23,103 +23,123 @@
namespace network {
namespace http {
- namespace v2 {
+ inline namespace v2 {
namespace client_connection {
- class ssl_connection : public async_connection {
-
- ssl_connection(const ssl_connection &) = delete;
- ssl_connection &operator = (const ssl_connection &) = delete;
-
- public:
-
- ssl_connection(boost::asio::io_service &io_service, const client_options &options)
- : io_service_(io_service)
- , options_(options) {
-
- }
-
- virtual ~ssl_connection() noexcept {
-
- }
-
- virtual void async_connect(const boost::asio::ip::tcp::endpoint &endpoint,
- const std::string &host,
- connect_callback callback) {
- context_.reset(new boost::asio::ssl::context(boost::asio::ssl::context::sslv23));
- auto certificate_paths = options_.openssl_certificate_paths();
- auto verifier_paths = options_.openssl_verify_paths();
- bool use_default_verification = certificate_paths.empty() && verifier_paths.empty();
- if (!use_default_verification) {
- for (auto path : certificate_paths) {
- context_->load_verify_file(path);
- }
- for (auto path : verifier_paths) {
- context_->add_verify_path(path);
- }
- context_->set_verify_mode(boost::asio::ssl::context::verify_peer);
- context_->set_verify_callback(boost::asio::ssl::rfc2818_verification(host));
+ /**
+ * \class
+ * \brief
+ */
+ class ssl_connection : public async_connection {
+
+ ssl_connection(const ssl_connection &) = delete;
+ ssl_connection &operator = (const ssl_connection &) = delete;
+
+ public:
+
+ /**
+ * \brief
+ */
+ ssl_connection(boost::asio::io_service &io_service, const client_options &options)
+ : io_service_(io_service)
+ , options_(options) {
+
+ }
+
+ /**
+ * \brief Destructor.
+ */
+ virtual ~ssl_connection() noexcept {
+
+ }
+
+ virtual void async_connect(const boost::asio::ip::tcp::endpoint &endpoint,
+ const std::string &host,
+ connect_callback callback) {
+ context_.reset(new boost::asio::ssl::context(boost::asio::ssl::context::sslv23));
+ auto certificate_paths = options_.openssl_certificate_paths();
+ auto verifier_paths = options_.openssl_verify_paths();
+ bool use_default_verification = certificate_paths.empty() && verifier_paths.empty();
+ if (!use_default_verification) {
+ for (auto path : certificate_paths) {
+ context_->load_verify_file(path);
}
- else {
- context_->set_default_verify_paths();
- context_->set_verify_mode(boost::asio::ssl::context::verify_none);
+ for (auto path : verifier_paths) {
+ context_->add_verify_path(path);
}
- socket_.reset(new boost::asio::ssl::stream<
- boost::asio::ip::tcp::socket>(io_service_, *context_));
-
- using namespace std::placeholders;
- socket_->lowest_layer().async_connect(endpoint,
- [=] (const boost::system::error_code &ec) {
- handle_connected(ec, callback);
- });
+ context_->set_verify_mode(boost::asio::ssl::context::verify_peer);
+ context_->set_verify_callback(boost::asio::ssl::rfc2818_verification(host));
}
-
- virtual void async_write(boost::asio::streambuf &command_streambuf,
- write_callback callback) {
- boost::asio::async_write(*socket_, command_streambuf, callback);
- }
-
- virtual void async_read_until(boost::asio::streambuf &command_streambuf,
- const std::string &delim,
- read_callback callback) {
- boost::asio::async_read_until(*socket_, command_streambuf, delim, callback);
+ else {
+ context_->set_default_verify_paths();
+ context_->set_verify_mode(boost::asio::ssl::context::verify_none);
}
-
- virtual void async_read(boost::asio::streambuf &command_streambuf,
- read_callback callback) {
- boost::asio::async_read(*socket_, command_streambuf,
- boost::asio::transfer_at_least(1), callback);
+ socket_.reset(new boost::asio::ssl::stream<
+ boost::asio::ip::tcp::socket>(io_service_, *context_));
+
+ using namespace std::placeholders;
+ socket_->lowest_layer().async_connect(endpoint,
+ [=] (const boost::system::error_code &ec) {
+ handle_connected(ec, callback);
+ });
+ }
+
+ virtual void async_write(boost::asio::streambuf &command_streambuf,
+ write_callback callback) {
+ boost::asio::async_write(*socket_, command_streambuf, callback);
+ }
+
+ virtual void async_read_until(boost::asio::streambuf &command_streambuf,
+ const std::string &delim,
+ read_callback callback) {
+ boost::asio::async_read_until(*socket_, command_streambuf, delim, callback);
+ }
+
+ virtual void async_read(boost::asio::streambuf &command_streambuf,
+ read_callback callback) {
+ boost::asio::async_read(*socket_, command_streambuf,
+ boost::asio::transfer_at_least(1), callback);
+ }
+
+ virtual void disconnect() {
+ if (socket_ && socket_->lowest_layer().is_open()) {
+ boost::system::error_code ec;
+ socket_->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
+ if (!ec) {
+ socket_->lowest_layer().close(ec);
+ }
}
+ }
- virtual void cancel() {
- //socket_->cancel();
- }
+ virtual void cancel() {
+ socket_->lowest_layer().cancel();
+ }
- private:
+ private:
- void handle_connected(const boost::system::error_code &ec, connect_callback callback) {
- if (!ec) {
- auto existing_session = SSL_get1_session(socket_->native_handle());
- if (existing_session) {
- socket_->async_handshake(boost::asio::ssl::stream_base::client, callback);
- }
- else {
- SSL_set_session(socket_->native_handle(), existing_session);
- SSL_connect(socket_->native_handle());
- callback(ec);
- }
+ void handle_connected(const boost::system::error_code &ec, connect_callback callback) {
+ if (!ec) {
+ auto existing_session = SSL_get1_session(socket_->native_handle());
+ if (existing_session) {
+ socket_->async_handshake(boost::asio::ssl::stream_base::client, callback);
}
else {
+ SSL_set_session(socket_->native_handle(), existing_session);
+ SSL_connect(socket_->native_handle());
callback(ec);
}
}
+ else {
+ callback(ec);
+ }
+ }
- boost::asio::io_service &io_service_;
- client_options options_;
- std::unique_ptr<boost::asio::ssl::context> context_;
- std::unique_ptr<
- boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> socket_;
+ boost::asio::io_service &io_service_;
+ client_options options_;
+ std::unique_ptr<boost::asio::ssl::context> context_;
+ std::unique_ptr<
+ boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> socket_;
- };
+ };
} // namespace client_connection
} // namespace v2
} // namespace http
150 http/src/network/http/v2/client/connection/tcp_resolver.hpp
View
@@ -24,81 +24,81 @@
#include <network/http/v2/client/connection/endpoint_cache.hpp>
namespace network {
- namespace http {
- namespace v2 {
- namespace client_connection {
- /**
- * \class tcp_resolver network/http/v2/client/connection/tcp_resolver.hpp
- * \brief Resolves and maintains a cache of hosts.
- */
- class tcp_resolver : public async_resolver {
-
- tcp_resolver(const tcp_resolver &) = delete;
- tcp_resolver &operator = (const tcp_resolver &) = delete;
-
- public:
-
- using async_resolver::resolver;
- using async_resolver::resolver_iterator;
- using async_resolver::resolve_callback;
-
- /**
- * \brief Constructor.
- */
- tcp_resolver(boost::asio::io_service &service, bool cache_resolved = false)
- : resolver_(service)
- , cache_resolved_(cache_resolved) {
-
- }
-
- /**
- * \brief Destructor.
- */
- virtual ~tcp_resolver() noexcept {
-
- }
-
- virtual void async_resolve(const std::string &host, std::uint16_t port,
- resolve_callback handler) {
- if (cache_resolved_) {
- auto it = endpoint_cache_.find(host);
- if (it != endpoint_cache_.end()) {
- boost::system::error_code ec;
- handler(ec, it->second);
- return;
- }
- }
-
- resolver::query query(host, std::to_string(port));
- resolver_.async_resolve(query,
- [host, handler, this](const boost::system::error_code &ec,
- resolver_iterator endpoint_iterator) {
- if (ec) {
- handler(ec, resolver_iterator());
- }
- else {
- if (cache_resolved_) {
- endpoint_cache_.insert(host, endpoint_iterator);
- }
- handler(ec, endpoint_iterator);
- }
- });
- }
-
- virtual void clear_resolved_cache() {
- endpoint_cache_.clear();
- }
-
- private:
-
- resolver resolver_;
- bool cache_resolved_;
- endpoint_cache endpoint_cache_;
-
- };
- } // namespace client_connection
- } // namespace v2
- } // namespace http
+namespace http {
+inline namespace v2 {
+namespace client_connection {
+/**
+ * \class tcp_resolver network/http/v2/client/connection/tcp_resolver.hpp
+ * \brief Resolves and maintains a cache of hosts.
+ */
+class tcp_resolver : public async_resolver {
+
+ tcp_resolver(const tcp_resolver &) = delete;
+ tcp_resolver &operator = (const tcp_resolver &) = delete;
+
+public:
+
+ using async_resolver::resolver;
+ using async_resolver::resolver_iterator;
+ using async_resolver::resolve_callback;
+
+ /**
+ * \brief Constructor.
+ */
+ tcp_resolver(boost::asio::io_service &service, bool cache_resolved = false)
+ : resolver_(service)
+ , cache_resolved_(cache_resolved) {
+
+ }
+
+ /**
+ * \brief Destructor.
+ */
+ virtual ~tcp_resolver() noexcept {
+
+ }
+
+ virtual void async_resolve(const std::string &host, std::uint16_t port,
+ resolve_callback handler) {
+ if (cache_resolved_) {
+ auto it = endpoint_cache_.find(host);
+ if (it != endpoint_cache_.end()) {
+ boost::system::error_code ec;
+ handler(ec, it->second);
+ return;
+ }
+ }
+
+ resolver::query query(host, std::to_string(port));
+ resolver_.async_resolve(query,
+ [host, handler, this](const boost::system::error_code &ec,
+ resolver_iterator endpoint_iterator) {
+ if (ec) {
+ handler(ec, resolver_iterator());
+ }
+ else {
+ if (cache_resolved_) {
+ endpoint_cache_.insert(host, endpoint_iterator);
+ }
+ handler(ec, endpoint_iterator);
+ }
+ });
+ }
+
+ virtual void clear_resolved_cache() {
+ endpoint_cache_.clear();
+ }
+
+private:
+
+ resolver resolver_;
+ bool cache_resolved_;
+ endpoint_cache endpoint_cache_;
+
+};
+} // namespace client_connection
+} // namespace v2
+} // namespace http
} // namespace network
#endif // NETWORK_HTTP_V2_CLIENT_CONNECTION_TCP_RESOLVER_INC
1,020 http/src/network/http/v2/client/request.hpp
View
@@ -20,6 +20,7 @@
#include <utility>
#include <cstdint>
#include <algorithm>
+#include <functional>
#include <sstream>
#include <iterator>
#include <boost/range/iterator_range.hpp>
@@ -33,485 +34,546 @@
#include <network/uri.hpp>
namespace network {
- namespace http {
- namespace v2 {
- namespace client_message {
- /**
- * \ingroup http_client
- * \class request_options network/http/v2/client/request.hpp network/http/v2/client.hpp
- * \brief A class used to configure an HTTP request.
- */
- class request_options {
-
- public:
-
- /**
- * \brief Constructor.
- */
- request_options()
- : resolve_timeout_(30000)
- , read_timeout_(30000)
- , total_timeout_(30000)
- , max_redirects_(10) { }
-
- /**
- * \brief Copy constructor.
- */
- request_options(request_options const &other)
- : resolve_timeout_(other.resolve_timeout_)
- , read_timeout_(other.read_timeout_)
- , total_timeout_(other.total_timeout_)
- , max_redirects_(other.max_redirects_) { }
-
- /**
- * \brief Move constructor.
- */
- request_options(request_options &&other)
- : resolve_timeout_(std::move(other.resolve_timeout_))
- , read_timeout_(std::move(other.read_timeout_))
- , total_timeout_(std::move(other.total_timeout_))
- , max_redirects_(std::move(other.max_redirects_)) { }
-
- /**
- * \brief Assignment operator.
- */
- request_options &operator = (request_options other) {
- other.swap(*this);
- return *this;
- }
-
- /**
- * \brief Destructor.
- */
- ~request_options() noexcept {
-
- }
-
- void swap(request_options &other) noexcept {
- using std::swap;
- swap(resolve_timeout_, other.resolve_timeout_);
- swap(read_timeout_, other.read_timeout_);
- swap(total_timeout_, other.total_timeout_);
- }
-
- request_options &resolve_timeout(std::uint64_t resolve_timeout) {
- resolve_timeout_ = resolve_timeout;
- return *this;
- }
-
- std::uint64_t resolve_timeout() const {
- return resolve_timeout_;
- }
-
- request_options &read_timeout(std::uint64_t read_timeout) {
- read_timeout_ = read_timeout;
- return *this;
- }
-
- std::uint64_t read_timeout() const {
- return read_timeout_;
- }
-
- request_options &total_timeout(std::uint64_t total_timeout) {
- total_timeout_ = total_timeout;
- return *this;
- }
-
- std::uint64_t total_timeout() const {
- return total_timeout_;
- }
-
- request_options &max_redirects(int max_redirects) {
- max_redirects_ = max_redirects;
- return *this;
- }
-
- int max_redirects() const {
- return max_redirects_;
- }
-
- private:
-
- std::uint64_t resolve_timeout_;
- std::uint64_t read_timeout_;
- std::uint64_t total_timeout_;
- int max_redirects_;
-
- };
-
- inline
- void swap(request_options &lhs, request_options &rhs) noexcept {
- lhs.swap(rhs);
- }
-
- /**
- * \ingroup http_client
- * \class byte_source network/http/v2/client/request.hpp network/http/v2/client.hpp
- * \brief An abstract class that allows a request object to read
- * data from any source.
- */
- class byte_source {
-
- public:
-
- /**
- * \typedef string_type
- * \brief The byte_source string type.
- */
- typedef std::string string_type;
-
- /**
- * \typedef size_type
- * \brief The byte_source size type.
- */
- typedef std::size_t size_type;
-
- /**
- * \brief Destructor.
- */
- virtual ~byte_source() noexcept {}
-
- /**
- * \brief Allows the request to read the data into a local
- * copy of its source string.
- */
- virtual size_type read(string_type &source, size_type length) = 0;
-
- };
-
- /**
- * \ingroup http_client
- * \class string_byte_source network/http/v2/client/request.hpp network/http/v2/client.hpp
- * \brief A class that wraps a string as a byte source.
- */
- class string_byte_source : public byte_source {
-
- public:
-
- /**
- * \brief Constructor.
- */
- explicit string_byte_source(string_type source);
-
- /**
- * \brief Destructor.
- */
- virtual ~string_byte_source() {}
-
- virtual size_type read(string_type &source, size_type length);
-
- private:
-
- string_type source_;
-
- };
-
- /**
- * \ingroup http_client
- * \class request network/http/v2/client/request.hpp network/http/v2/client.hpp
- * \brief A class that models an HTTP request.
- */
- class request {
-
- public:
-
- /**
- * \typedef string_type
- * \brief The request string type.
- */
- typedef byte_source::string_type string_type;
-
- /**
- * \typedef size_type
- * \brief The request size type.
- */
- typedef byte_source::size_type size_type;
-
- /**
- * \typedef headers_type
- * \brief The request headers type.
- */
- typedef std::vector<std::pair<string_type, string_type>> headers_type;
-
- /**
- * \typedef headers_iterator
- * \brief The request headers iterator.
- */
- typedef headers_type::iterator headers_iterator;
-
- /**
- * \typedef const_headers_iterator
- * \brief The request headers const_iterator.
- */
- typedef headers_type::const_iterator const_headers_iterator;
-
- /**
- * \brief Constructor.
- */
- request()
- : byte_source_(nullptr) { }
-
- /**
- * \brief Constructor.
- */
- explicit request(uri url)
- : url_(url) {
- if (auto scheme = url.scheme()) {
- if ((!boost::equal(*scheme, boost::as_literal("http"))) &&
- (!boost::equal(*scheme, boost::as_literal("https")))) {
- throw invalid_url();
- }
-
- if (auto path = url.path()) {
- std::copy(std::begin(*path), std::end(*path),
- std::back_inserter(path_));
- }
-
- if (auto query = url.query()) {
- path_.push_back('?');
- std::copy(std::begin(*query), std::end(*query),
- std::back_inserter(path_));
- }
-
- if (auto fragment = url.fragment()) {
- path_.push_back('#');
- std::copy(std::begin(*fragment), std::end(*fragment),
- std::back_inserter(path_));
- }
-
- std::ostringstream oss;
- std::copy(std::begin(*url.host()), std::end(*url.host()),
- std::ostream_iterator<char>(oss));
- if (url.port()) {
- oss << ":";
- std::copy(std::begin(*url.port()), std::end(*url.port()),
- std::ostream_iterator<char>(oss));
- }
- append_header("Host", oss.str());
- }
- else {
- throw invalid_url();
- }
- }
-
- /**
- * \brief Copy constructor.
- */
- request(const request &other)
- : url_(other.url_)
- , method_(other.method_)
- , path_(other.path_)
- , version_(other.version_)
- , headers_(other.headers_)
- , byte_source_(other.byte_source_) { }
-
- /**
- * \brief Move constructor.
- */
- request(request &&other) noexcept
- : url_(std::move(other.url_))
- , method_(std::move(other.method_))
- , path_(std::move(other.path_))
- , version_(std::move(other.version_))
- , headers_(std::move(other.headers_))
- , byte_source_(std::move(other.byte_source_)) { }
-
- /**
- * \brief Assignment operator.
- */
- request &operator = (request other) {
- other.swap(*this);
- return *this;
- }
-
- /**
- * \brief Destructor.
- */
- ~request() noexcept {
-
- }
-
- /**
- * \brief Swaps one request object with another.
- * \param other The other request object.
- */
- void swap(request &other) noexcept {
- using std::swap;
- swap(url_, other.url_);
- swap(method_, other.method_);
- swap(path_, other.path_);
- swap(version_, other.version_);
- swap(headers_, other.headers_);
- swap(byte_source_, other.byte_source_);
- }
-
- request &url(const uri &url) {
- // throw invalid_url
- url_ = url;
- return *this;
- }
-
- uri url() const {
- return url_;
- }
-
- /**
- * \brief Checks whether this is an HTTPS request.
- * \returns \c true if it is HTTPS, \c false otherwise.
- */
- bool is_https() const {
- return url_.scheme() && boost::equal(*url_.scheme(), boost::as_literal("https"));
- }
-
- /**
- * \brief Sets the HTTP request method.
- * \param method The HTTP request method.
- * \returns *this
- */
- request &method(network::http::v2::method method) {
- method_ = method;
- return *this;
<