Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Fix exception propaation for client errors
We change the cross-thread exception propagation given the asynchronous
client implementation. The previous implementation caused
double-wrapping of exceptions which works with the
std::shared_future<...> implementation, but not with
boost::shared_future<...> -- since Boost.Thread still uses
Boost.Exception for the APIs.

While we're here we make the exception propagation more explicit using
boost::rethrow_exception(...), so we can unwrap the exception that's
transported via an exception_ptr.

Fixes #749.
  • Loading branch information
deanberris committed Apr 7, 2017
commit 95bd07c1c8b56aea2b8b1eebba2595d44e7a2d00
22 changes: 10 additions & 12 deletions boost/network/protocol/http/client/connection/async_normal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,9 @@ struct http_async_connection
request_strand_(resolver.get_io_service()),
delegate_(std::move(delegate)) {}

// This is the main entry point for the connection/request pipeline.
// We're
// overriding async_connection_base<...>::start(...) here which is
// called
// by the client.
// This is the main entry point for the connection/request pipeline. We're
// overriding async_connection_base<...>::start(...) here which is called by
// the client.
virtual response start(request const& request, string_type const& method,
bool get_body, body_callback_function_type callback,
body_generator_function_type generator) {
Expand Down Expand Up @@ -198,13 +196,13 @@ struct http_async_connection
private:
void set_errors(boost::system::error_code const& ec, body_callback_function_type callback) {
boost::system::system_error error(ec);
this->version_promise.set_exception(std::make_exception_ptr(error));
this->status_promise.set_exception(std::make_exception_ptr(error));
this->status_message_promise.set_exception(std::make_exception_ptr(error));
this->headers_promise.set_exception(std::make_exception_ptr(error));
this->source_promise.set_exception(std::make_exception_ptr(error));
this->destination_promise.set_exception(std::make_exception_ptr(error));
this->body_promise.set_exception(std::make_exception_ptr(error));
this->version_promise.set_exception(boost::copy_exception(error));
this->status_promise.set_exception(boost::copy_exception(error));
this->status_message_promise.set_exception(boost::copy_exception(error));
this->headers_promise.set_exception(boost::copy_exception(error));
this->source_promise.set_exception(boost::copy_exception(error));
this->destination_promise.set_exception(boost::copy_exception(error));
this->body_promise.set_exception(boost::copy_exception(error));
if ( callback )
callback( boost::iterator_range<typename std::array<typename char_<Tag>::type, 1024>::const_iterator>(), ec );
this->timer_.cancel();
Expand Down
67 changes: 49 additions & 18 deletions boost/network/protocol/http/message/async_message.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#include <cstdint>
#include <boost/optional.hpp>
#include <cstdint>

// FIXME move this out to a trait
#include <set>
#include <boost/network/detail/wrapper_base.hpp>
#include <boost/thread/future.hpp>
#include <set>

namespace boost {
namespace network {
Expand All @@ -26,12 +26,11 @@ namespace impl {
template <class Tag>
struct ready_wrapper;

} // namespace impl
/* impl */
} // namespace impl
/* impl */

template <class Tag>
struct async_message {

typedef typename string<Tag>::type string_type;
typedef typename headers_container<Tag>::type headers_container_type;
typedef typename headers_container_type::value_type header_type;
Expand All @@ -54,63 +53,95 @@ struct async_message {
headers_(other.headers_),
body_(other.body_) {}

string_type const status_message() const { return status_message_.get(); }
string_type status_message() const {
status_message_.wait();
if (status_message_.has_exception())
boost::rethrow_exception(status_message_.get_exception_ptr());
return status_message_.get();
}

void status_message(boost::shared_future<string_type> const& future) const {
status_message_ = future;
}

string_type const version() const { return version_.get(); }
string_type version() const {
version_.wait();
if (version_.has_exception())
boost::rethrow_exception(version_.get_exception_ptr());
return version_.get();
}

void version(boost::shared_future<string_type> const& future) const {
version_ = future;
}

std::uint16_t status() const { return status_.get(); }
std::uint16_t status() const {
status_.wait();
if (status_.has_exception())
boost::rethrow_exception(status_.get_exception_ptr());
return status_.get();
}

void status(boost::shared_future<uint16_t> const& future) const {
status_ = future;
}

string_type const source() const { return source_.get(); }
string_type source() const {
source_.wait();
if (source_.has_exception())
boost::rethrow_exception(source_.get_exception_ptr());
return source_.get();
}

void source(boost::shared_future<string_type> const& future) const {
source_ = future;
}

string_type const destination() const { return destination_.get(); }
string_type destination() const {
destination_.wait();
if (destination_.has_exception())
boost::rethrow_exception(source_.get_exception_ptr());
return destination_.get();
}

void destination(boost::shared_future<string_type> const& future) const {
destination_ = future;
}

headers_container_type const& headers() const {
if (retrieved_headers_) return *retrieved_headers_;
if (headers_.has_exception())
boost::rethrow_exception(headers_.get_exception_ptr());
headers_container_type raw_headers = headers_.get();
raw_headers.insert(added_headers.begin(), added_headers.end());
for (string_type const & key : removed_headers) {
for (string_type const& key : removed_headers) {
raw_headers.erase(key);
}
retrieved_headers_ = raw_headers;
return *retrieved_headers_;
}

void headers(boost::shared_future<headers_container_type> const& future)
const {
void headers(
boost::shared_future<headers_container_type> const& future) const {
headers_ = future;
}

void add_header(typename headers_container_type::value_type const& pair_)
const {
void add_header(
typename headers_container_type::value_type const& pair_) const {
added_headers.insert(added_headers.end(), pair_);
}

void remove_header(typename headers_container_type::key_type const& key_)
const {
void remove_header(
typename headers_container_type::key_type const& key_) const {
removed_headers.insert(key_);
}

string_type const body() const { return body_.get(); }
string_type body() const {
body_.wait();
if (body_.has_exception())
boost::rethrow_exception(body_.get_exception_ptr());
return body_.get();
}

void body(boost::shared_future<string_type> const& future) const {
body_ = future;
Expand Down
11 changes: 11 additions & 0 deletions libs/network/test/http/client_get_test.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright 2010 Dean Michael Berris.
// Copyright 2017 Google, Inc.
// 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)
Expand Down Expand Up @@ -71,3 +72,13 @@ TYPED_TEST(HTTPClientTest, TemporaryClientObjectTest) {
EXPECT_TRUE(response.status() == 200u ||
(response.status() >= 300 && response.status() < 400));
}

TYPED_TEST(HTTPClientTest, PropagatesResolutionErrorsTest) {
using client = TypeParam;
typename client::request request("http://malformed.google.comq");
typename client::response response;
typename client::options options;
typename client::string_type response_body;
ASSERT_NO_THROW(response = client(options).get(request));
EXPECT_THROW(response_body = body(response), std::exception);
}