Skip to content

Commit

Permalink
Squashed Asio SSL FUBAR Issue
Browse files Browse the repository at this point in the history
So, this affects the HTTPS implementation in 0.9.3 as well, where Asio
is not exactly being smart about how it deals with SSL errors. We've
had to do some acrobatics just to determine whether the error code
returned by an operation performed on an SSL stream resulted in a short
read. The change is simple, but tracking it down is so much of a PITA.

We probably should file a bug in Asio to be able to better detect the
situation with short reads on SSL streams, and somehow also file a bug
for it. At worst the short read should just use an asio::error::eof so
that it's not too hard to deal with this issue from the user side.

Also, this commit has some header-fudging to reduce dependencies and
incremental compile-times. A more extensive effort should be done to
reduce dependencies across header-files library-wide.
  • Loading branch information
deanberris committed Mar 17, 2012
1 parent 7ff00ac commit b26c440
Show file tree
Hide file tree
Showing 11 changed files with 121 additions and 52 deletions.
19 changes: 14 additions & 5 deletions boost/network/protocol/http/client/connection/async_normal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,24 @@

#include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/network/protocol/http/request.hpp>
#include <boost/network/protocol/http/response.hpp>
#include <boost/network/protocol/http/client/client_connection.hpp>
#include <boost/network/protocol/http/client/connection/resolver_delegate.hpp>
#include <boost/network/protocol/http/client/connection/connection_delegate.hpp>
#include <boost/enable_shared_from_this.hpp>

namespace boost { namespace asio {

class io_service;

} // namespace asio

} // namespace boost

namespace boost { namespace network { namespace http {

struct request;
struct response;
struct resolver_delegate;
struct connection_delegate;

struct http_async_connection_pimpl;

struct http_async_connection : client_connection
Expand Down
53 changes: 39 additions & 14 deletions boost/network/protocol/http/client/connection/async_normal.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@
#include <boost/asio/placeholders.hpp>
#include <boost/network/protocol/http/client/connection/async_normal.hpp>
#include <boost/network/protocol/http/request.hpp>
#include <boost/network/protocol/http/response.hpp>
#include <boost/network/protocol/http/client/connection/connection_delegate.hpp>
#include <boost/network/protocol/http/client/connection/resolver_delegate.hpp>
#include <boost/network/protocol/http/algorithms/linearize.hpp>
#include <boost/network/protocol/http/impl/access.hpp>
#include <boost/asio/strand.hpp>
#include <boost/network/detail/debug.hpp>
#ifdef BOOST_NETWORK_ENABLE_HTTPS
#include <boost/asio/ssl/error.hpp>
#endif

namespace boost { namespace network { namespace http {

Expand Down Expand Up @@ -59,8 +65,9 @@ struct http_async_connection_pimpl : boost::enable_shared_from_this<http_async_c
BOOST_NETWORK_MESSAGE("method: " << this->method);
boost::uint16_t port_ = port(request);
BOOST_NETWORK_MESSAGE("port: " << port_);
this->host_ = host(request);
resolver_delegate_->resolve(
host(request),
this->host_,
port_,
request_strand_.wrap(
boost::bind(
Expand Down Expand Up @@ -131,17 +138,19 @@ struct http_async_connection_pimpl : boost::enable_shared_from_this<http_async_c
BOOST_NETWORK_MESSAGE("trying connection to: "
<< iter->endpoint().address() << ":" << port);
asio::ip::tcp::endpoint endpoint(iter->endpoint().address(), port);
connection_delegate_->connect(endpoint,
request_strand_.wrap(
boost::bind(
&this_type::handle_connected,
this_type::shared_from_this(),
port,
get_body,
callback,
std::make_pair(++iter,
resolver_iterator()),
placeholders::error)));
connection_delegate_->connect(
endpoint,
this->host_,
request_strand_.wrap(
boost::bind(
&this_type::handle_connected,
this_type::shared_from_this(),
port,
get_body,
callback,
std::make_pair(++iter,
resolver_iterator()),
placeholders::error)));
} else {
BOOST_NETWORK_MESSAGE("error encountered while resolving.");
set_errors(ec ? ec : boost::asio::error::host_not_found);
Expand Down Expand Up @@ -174,6 +183,7 @@ struct http_async_connection_pimpl : boost::enable_shared_from_this<http_async_c
BOOST_NETWORK_MESSAGE("trying: " << iter->endpoint().address() << ":" << port);
asio::ip::tcp::endpoint endpoint(iter->endpoint().address(), port);
connection_delegate_->connect(endpoint,
this->host_,
request_strand_.wrap(
boost::bind(
&this_type::handle_connected,
Expand Down Expand Up @@ -218,7 +228,20 @@ struct http_async_connection_pimpl : boost::enable_shared_from_this<http_async_c

void handle_received_data(state_t state, bool get_body, body_callback_function_type callback, boost::system::error_code const & ec, std::size_t bytes_transferred) {
BOOST_NETWORK_MESSAGE("http_async_connection_pimpl::handle_received_data(...)");
if (!ec || ec == boost::asio::error::eof) {
// Okay, there's some weirdness with Boost.Asio's handling of SSL errors
// so we need to do some acrobatics to make sure that we're handling the
// short-read errors correctly. This is such a PITA that we have to deal
// with this here.
static long short_read_error = 335544539;
bool is_short_read_error =
#ifdef BOOST_NETWORK_ENABLE_HTTPS
ec.category() == asio::error::ssl_category &&
ec.value() == short_read_error
#else
false
#endif
;
if (!ec || ec == boost::asio::error::eof || is_short_read_error) {
BOOST_NETWORK_MESSAGE("processing data chunk, no error encountered so far...");
logic::tribool parsed_ok;
size_t remainder;
Expand Down Expand Up @@ -347,7 +370,7 @@ struct http_async_connection_pimpl : boost::enable_shared_from_this<http_async_c
return;
case body:
BOOST_NETWORK_MESSAGE("parsing body...");
if (ec == boost::asio::error::eof) {
if (ec == boost::asio::error::eof || is_short_read_error) {
BOOST_NETWORK_MESSAGE("end of the line.");
// Here we're handling the case when the connection has been
// closed from the server side, or at least that the end of file
Expand Down Expand Up @@ -429,6 +452,7 @@ struct http_async_connection_pimpl : boost::enable_shared_from_this<http_async_c
}
} else {
boost::system::system_error error(ec);
BOOST_NETWORK_MESSAGE("error encountered: " << error.what() << " (" << ec << ")");
this->source_promise.set_exception(boost::copy_exception(error));
this->destination_promise.set_exception(boost::copy_exception(error));
switch (state) {
Expand Down Expand Up @@ -773,6 +797,7 @@ struct http_async_connection_pimpl : boost::enable_shared_from_this<http_async_c
buffer_type part;
buffer_type::const_iterator part_begin;
std::string partial_parsed;
std::string host_;
};

// END OF PIMPL DEFINITION
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace boost { namespace network { namespace http {

struct connection_delegate {
virtual void connect(asio::ip::tcp::endpoint & endpoint,
std::string const & host,
function<void(system::error_code const &)> handler) = 0;
virtual void write(asio::streambuf & command_streambuf,
function<void(system::error_code const &, size_t)> handler) = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ struct normal_delegate : connection_delegate {
normal_delegate(asio::io_service & service);

virtual void connect(asio::ip::tcp::endpoint & endpoint,
std::string const &host,
function<void(system::error_code const &)> handler);
virtual void write(asio::streambuf & command_streambuf,
function<void(system::error_code const &, size_t)> handler);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ boost::network::http::normal_delegate::normal_delegate(asio::io_service & servic

void boost::network::http::normal_delegate::connect(
asio::ip::tcp::endpoint & endpoint,
std::string const &host,
function<void(system::error_code const &)> handler) {
socket_.reset(new asio::ip::tcp::socket(service_));
socket_->async_connect(endpoint, handler);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#ifdef BOOST_NETWORK_DEBUG
#include <boost/network/uri/uri_io.hpp>
#endif
#include <boost/algorithm/string/case_conv.hpp>

#include <boost/network/protocol/http/message/wrappers/uri.hpp>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ struct ssl_delegate : connection_delegate, enable_shared_from_this<ssl_delegate>
client_options const &options);

virtual void connect(asio::ip::tcp::endpoint & endpoint,
std::string const &host,
function<void(system::error_code const &)> handler);
virtual void write(asio::streambuf & command_streambuf,
function<void(system::error_code const &, size_t)> handler);
Expand Down
55 changes: 38 additions & 17 deletions boost/network/protocol/http/client/connection/ssl_delegate.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,39 @@
boost::network::http::ssl_delegate::ssl_delegate(asio::io_service & service,
client_options const &options) :
service_(service),
options_(options) {}
options_(options) {
BOOST_NETWORK_MESSAGE("ssl_delegate::ssl_delegate(...)");
}

void boost::network::http::ssl_delegate::connect(
asio::ip::tcp::endpoint & endpoint,
std::string const &host,
function<void(system::error_code const &)> handler) {
BOOST_NETWORK_MESSAGE("ssl_delegate::connect(...)");
context_.reset(new asio::ssl::context(
service_,
asio::ssl::context::sslv23_client));
asio::ssl::context::sslv23));
std::list<std::string> const & certificate_paths =
options_.openssl_certificate_paths();
std::list<std::string> const & verifier_paths =
options_.openssl_verify_paths();
bool verify_peer = !certificate_paths.empty() || !verifier_paths.empty();
BOOST_NETWORK_MESSAGE("verify peer setting is: " << verify_peer);
if (verify_peer) context_->set_verify_mode(asio::ssl::context::verify_peer);
else context_->set_verify_mode(asio::ssl::context::verify_none);
for (std::list<std::string>::const_iterator it = certificate_paths.begin();
it != certificate_paths.end(); ++it) {
context_->load_verify_file(*it);
}
for (std::list<std::string>::const_iterator it = verifier_paths.begin();
it != verifier_paths.begin(); ++it) {
context_->add_verify_path(*it);
bool use_default_verification = certificate_paths.empty() && verifier_paths.empty();
if (!use_default_verification) {
for (std::list<std::string>::const_iterator it = certificate_paths.begin();
it != certificate_paths.end(); ++it) {
context_->load_verify_file(*it);
}
for (std::list<std::string>::const_iterator it = verifier_paths.begin();
it != verifier_paths.begin(); ++it) {
context_->add_verify_path(*it);
}
BOOST_NETWORK_MESSAGE("verifying peer: " << host);
context_->set_verify_mode(asio::ssl::context::verify_peer);
context_->set_verify_callback(asio::ssl::rfc2818_verification(host));
} else {
BOOST_NETWORK_MESSAGE("not verifying peer");
context_->set_default_verify_paths();
context_->set_verify_mode(asio::ssl::context::verify_peer);
context_->set_verify_callback(asio::ssl::rfc2818_verification(host));
}
socket_.reset(new asio::ssl::stream<asio::ip::tcp::socket>(service_, *context_));
BOOST_NETWORK_MESSAGE("scheduling asynchronous connection...");
Expand All @@ -51,12 +60,24 @@ void boost::network::http::ssl_delegate::connect(
handler));
}

void boost::network::http::ssl_delegate::handle_connected(system::error_code const & ec,
function<void(system::error_code const &)> handler) {

void boost::network::http::ssl_delegate::handle_connected(
system::error_code const & ec,
function<void(system::error_code const &)> handler) {
BOOST_NETWORK_MESSAGE("ssl_delegate::handle_connected(...)");
if (!ec) {
BOOST_NETWORK_MESSAGE("connected to endpoint.");
socket_->async_handshake(asio::ssl::stream_base::client, handler);
// Here we check if there's an existing session for the connection.
SSL_SESSION *existing_session = SSL_get1_session(socket_->impl()->ssl);
if (existing_session == NULL) {
BOOST_NETWORK_MESSAGE("found no existing session, performing handshake.");
socket_->async_handshake(asio::ssl::stream_base::client, handler);
} else {
BOOST_NETWORK_MESSAGE("found existing session, bypassing handshake.");
SSL_set_session(socket_->impl()->ssl, existing_session);
SSL_connect(socket_->impl()->ssl);
handler(ec);
}
} else {
BOOST_NETWORK_MESSAGE("encountered error: " << ec);
handler(ec);
Expand Down
8 changes: 8 additions & 0 deletions boost/network/protocol/http/message/wrappers/port.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ port_wrapper::operator boost::uint16_t () const {
uri::uri uri_;
request_.get_uri(uri_);
optional<boost::uint16_t> optional_port = port_us(uri_);
if (!optional_port) {
std::string scheme_ = scheme(uri_);
if (scheme_ == "http") {
return 80u;
} else if (scheme_ == "https") {
return 443u;
}
}
return optional_port ? *optional_port : 80u;
}

Expand Down
28 changes: 14 additions & 14 deletions boost/network/protocol/http/response/response.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ struct response_pimpl {
promise<std::string> destination_promise;
destination_promise.set_value(destination);
unique_future<std::string> tmp_future = destination_promise.get_future();
destination_future_ = move(tmp_future);
destination_future_ = boost::move(tmp_future);
}

void get_destination(std::string &destination) {
Expand All @@ -38,7 +38,7 @@ struct response_pimpl {
promise<std::string> source_promise;
source_promise.set_value(source);
unique_future<std::string> tmp_future = source_promise.get_future();
source_future_ = move(tmp_future);
source_future_ = boost::move(tmp_future);
}

void get_source(std::string &source) {
Expand Down Expand Up @@ -66,7 +66,7 @@ struct response_pimpl {
headers_promise.get_future();
std::multimap<std::string, std::string>().swap(added_headers_);
std::set<std::string>().swap(removed_headers_);
headers_future_ = move(tmp);
headers_future_ = boost::move(tmp);
}
}

Expand Down Expand Up @@ -102,7 +102,7 @@ struct response_pimpl {
promise<std::string> body_promise;
body_promise.set_value(body);
unique_future<std::string> tmp_future = body_promise.get_future();
body_future_ = move(tmp_future);
body_future_ = boost::move(tmp_future);
}

void append_body(std::string const & data) { /* FIXME: Do something! */ }
Expand All @@ -123,7 +123,7 @@ struct response_pimpl {
promise<boost::uint16_t> status_promise;
status_promise.set_value(status);
unique_future<boost::uint16_t> tmp_future = status_promise.get_future();
status_future_ = move(tmp_future);
status_future_ = boost::move(tmp_future);
}

void get_status(boost::uint16_t &status) {
Expand All @@ -138,7 +138,7 @@ struct response_pimpl {
promise<std::string> status_message_promise_;
status_message_promise_.set_value(status_message);
unique_future<std::string> tmp_future = status_message_promise_.get_future();
status_message_future_ = move(tmp_future);
status_message_future_ = boost::move(tmp_future);
}

void get_status_message(std::string &status_message) {
Expand All @@ -153,7 +153,7 @@ struct response_pimpl {
promise<std::string> version_promise;
version_promise.set_value(version);
unique_future<std::string> tmp_future = version_promise.get_future();
version_future_ = move(tmp_future);
version_future_ = boost::move(tmp_future);
}

void get_version(std::string &version) {
Expand All @@ -166,37 +166,37 @@ struct response_pimpl {

void set_source_promise(promise<std::string> &promise_) {
unique_future<std::string> tmp_future = promise_.get_future();
source_future_ = move(tmp_future);
source_future_ = boost::move(tmp_future);
}

void set_destination_promise(promise<std::string> &promise_) {
unique_future<std::string> tmp_future = promise_.get_future();
destination_future_ = move(tmp_future);
destination_future_ = boost::move(tmp_future);
}

void set_headers_promise(promise<std::multimap<std::string, std::string> > &promise_) {
unique_future<std::multimap<std::string, std::string> > tmp_future = promise_.get_future();
headers_future_ = move(tmp_future);
headers_future_ = boost::move(tmp_future);
}

void set_status_promise(promise<boost::uint16_t> &promise_) {
unique_future<boost::uint16_t> tmp_future = promise_.get_future();
status_future_ = move(tmp_future);
status_future_ = boost::move(tmp_future);
}

void set_status_message_promise(promise<std::string> &promise_) {
unique_future<std::string> tmp_future = promise_.get_future();
status_message_future_ = move(tmp_future);
status_message_future_ = boost::move(tmp_future);
}

void set_version_promise(promise<std::string> &promise_) {
unique_future<std::string> tmp_future = promise_.get_future();
version_future_ = move(tmp_future);
version_future_ = boost::move(tmp_future);
}

void set_body_promise(promise<std::string> &promise_) {
unique_future<std::string> tmp_future = promise_.get_future();
body_future_ = move(tmp_future);
body_future_ = boost::move(tmp_future);
}

bool equals(response_pimpl const &other) {
Expand Down
Loading

0 comments on commit b26c440

Please sign in to comment.