|
| 1 | +#ifndef BOOST_NETWORK_PROTOCOL_HTTP_IMPL_SYNC_CONNECTION_BASE_20091217 |
| 2 | +#define BOOST_NETWORK_PROTOCOL_HTTP_IMPL_SYNC_CONNECTION_BASE_20091217 |
| 3 | + |
| 4 | +// Copyright Dean Michael Berris 2009. |
| 5 | +// Distributed under the Boost Software License, Version 1.0. |
| 6 | +// (See accompanying file LICENSE_1_0.txt or copy at |
| 7 | +// http://www.boost.org/LICENSE_1_0.txt) |
| 8 | + |
| 9 | +#include <boost/network/protocol/http/traits/resolver_policy.hpp> |
| 10 | +#include <boost/network/protocol/http/detail/connection_helper.hpp> |
| 11 | +#include <boost/asio/ssl.hpp> |
| 12 | +#include <boost/tuple/tuple.hpp> |
| 13 | + |
| 14 | +namespace boost { namespace network { namespace http { namespace impl { |
| 15 | + template <class Tag, unsigned version_major, unsigned version_minor> |
| 16 | + struct sync_connection_base; |
| 17 | + |
| 18 | + template <class Tag, unsigned version_major, unsigned version_minor> |
| 19 | + struct https_sync_connection : public virtual sync_connection_base<Tag,version_major,version_minor>, detail::connection_helper<Tag, version_major, version_minor> { |
| 20 | + typedef typename resolver_policy<Tag>::type resolver_base; |
| 21 | + typedef typename resolver_base::resolver_type resolver_type; |
| 22 | + typedef typename string<Tag>::type string_type; |
| 23 | + typedef function<typename resolver_base::resolver_iterator_pair(resolver_type&, string_type const &, string_type const &)> resolver_function_type; |
| 24 | + |
| 25 | + https_sync_connection(resolver_type & resolver, resolver_function_type resolve) |
| 26 | + : resolver_(resolver), resolve_(resolve), context_(resolver.io_service(), boost::asio::ssl::context::sslv23_client), socket_(resolver.io_service(), context_) { } |
| 27 | + |
| 28 | + void init_socket(string_type const & hostname, string_type const & port) {} |
| 29 | + void send_request_impl(string_type const & method, basic_request<Tag> const & request_) {} |
| 30 | + void read_status(basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) {} |
| 31 | + void read_headers(basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) {} |
| 32 | + void read_body(basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) {} |
| 33 | + ~https_sync_connection() {} |
| 34 | + private: |
| 35 | + resolver_type & resolver_; |
| 36 | + resolver_function_type resolve_; |
| 37 | + boost::asio::ssl::context context_; |
| 38 | + boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_; |
| 39 | + |
| 40 | + }; |
| 41 | + |
| 42 | + template <class Tag, unsigned version_major, unsigned version_minor> |
| 43 | + struct http_sync_connection : public virtual sync_connection_base<Tag, version_major, version_minor>, detail::connection_helper<Tag, version_major, version_minor> { |
| 44 | + typedef typename resolver_policy<Tag>::type resolver_base; |
| 45 | + typedef typename resolver_base::resolver_type resolver_type; |
| 46 | + typedef typename string<Tag>::type string_type; |
| 47 | + typedef function<typename resolver_base::resolver_iterator_pair(resolver_type&, string_type const &, string_type const &)> resolver_function_type; |
| 48 | + |
| 49 | + http_sync_connection(resolver_type & resolver, resolver_function_type resolve) |
| 50 | + : resolver_(resolver), resolve_(resolve), socket_(resolver.io_service()) { } |
| 51 | + |
| 52 | + ~http_sync_connection() {} |
| 53 | + |
| 54 | + void init_socket(string_type const & hostname, string_type const & port) { |
| 55 | + using boost::asio::ip::tcp; |
| 56 | + boost::system::error_code error = boost::asio::error::host_not_found; |
| 57 | + typename resolver_type::iterator endpoint_iterator, end; |
| 58 | + boost::tie(endpoint_iterator, end) = resolve_(resolver_, hostname, port); |
| 59 | + while (error && endpoint_iterator != end) { |
| 60 | + socket_.close(); |
| 61 | + socket_.connect( |
| 62 | + tcp::endpoint( |
| 63 | + endpoint_iterator->endpoint().address() |
| 64 | + , endpoint_iterator->endpoint().port() |
| 65 | + ) |
| 66 | + , error |
| 67 | + ); |
| 68 | + ++endpoint_iterator; |
| 69 | + } |
| 70 | + |
| 71 | + if (error) |
| 72 | + throw boost::system::system_error(error); |
| 73 | + } |
| 74 | + |
| 75 | + void send_request_impl(string_type const & method, basic_request<Tag> const & request_) { |
| 76 | + boost::asio::streambuf request_buffer; |
| 77 | + create_request(request_buffer, method, request_); |
| 78 | + write(socket_, request_buffer); |
| 79 | + } |
| 80 | + |
| 81 | + void read_status(basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) { |
| 82 | + boost::asio::read_until(socket_, response_buffer, "\r\n"); |
| 83 | + std::istream response_stream(&response_buffer); |
| 84 | + string_type http_version; |
| 85 | + unsigned int status_code; |
| 86 | + string_type status_message; |
| 87 | + response_stream >> http_version |
| 88 | + >> status_code; |
| 89 | + std::getline(response_stream, status_message); |
| 90 | + trim_left(status_message); |
| 91 | + trim_right_if(status_message, boost::is_space() || boost::is_any_of("\r")); |
| 92 | + |
| 93 | + if (!response_stream || http_version.substr(0, 5) != "HTTP/") |
| 94 | + throw std::runtime_error("Invalid response"); |
| 95 | + |
| 96 | + response_.version() = http_version; |
| 97 | + response_.status() = status_code; |
| 98 | + response_.status_message() = status_message; |
| 99 | + } |
| 100 | + |
| 101 | + void read_headers(basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) { |
| 102 | + boost::asio::read_until(socket_, response_buffer, "\r\n\r\n"); |
| 103 | + std::istream response_stream(&response_buffer); |
| 104 | + string_type header_line, name; |
| 105 | + while (std::getline(response_stream, header_line) && header_line != "\r") { |
| 106 | + trim_right_if(header_line, boost::is_space() || boost::is_any_of("\r")); |
| 107 | + typename string_type::size_type colon_offset; |
| 108 | + if (header_line.size() && header_line[0] == ' ') { |
| 109 | + assert(!name.empty()); |
| 110 | + if (name.empty()) |
| 111 | + throw std::runtime_error( |
| 112 | + std::string("Malformed header: ") |
| 113 | + + header_line |
| 114 | + ); |
| 115 | + response_ |
| 116 | + << header(name, trim_left_copy(header_line)); |
| 117 | + } else if ((colon_offset = header_line.find_first_of(':')) != string_type::npos) { |
| 118 | + name = header_line.substr(0, colon_offset); |
| 119 | + response_ |
| 120 | + << header(name, header_line.substr(colon_offset+2)); |
| 121 | + }; |
| 122 | + }; |
| 123 | + } |
| 124 | + |
| 125 | + void read_body(basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) { |
| 126 | + typename ostringstream<Tag>::type body_stream; |
| 127 | + |
| 128 | + if (response_buffer.size() > 0) |
| 129 | + body_stream << &response_buffer; |
| 130 | + |
| 131 | + boost::system::error_code error; |
| 132 | + while (boost::asio::read(socket_, response_buffer, boost::asio::transfer_at_least(1), error)) |
| 133 | + body_stream << &response_buffer; |
| 134 | + |
| 135 | + if (error != boost::asio::error::eof) |
| 136 | + throw boost::system::system_error(error); |
| 137 | + |
| 138 | + response_ << body(body_stream.str()); |
| 139 | + } |
| 140 | + |
| 141 | + private: |
| 142 | + |
| 143 | + resolver_type & resolver_; |
| 144 | + resolver_function_type resolve_; |
| 145 | + boost::asio::ip::tcp::socket socket_; |
| 146 | + |
| 147 | + }; |
| 148 | + |
| 149 | + template <class Tag, unsigned version_major, unsigned version_minor> |
| 150 | + struct sync_connection_base { |
| 151 | + typedef typename resolver_policy<Tag>::type resolver_base; |
| 152 | + typedef typename resolver_base::resolver_type resolver_type; |
| 153 | + typedef typename string<Tag>::type string_type; |
| 154 | + typedef function<typename resolver_base::resolver_iterator_pair(resolver_type&, string_type const &, string_type const &)> resolver_function_type; |
| 155 | + |
| 156 | + static sync_connection_base<Tag,version_major,version_minor> * new_connection(resolver_type & resolver, resolver_function_type resolve, bool https) { |
| 157 | + if (https) { |
| 158 | + return dynamic_cast<sync_connection_base<Tag,version_major,version_minor>*>(new https_sync_connection<Tag,version_major,version_minor>(resolver, resolve)); |
| 159 | + } |
| 160 | + return dynamic_cast<sync_connection_base<Tag,version_major,version_minor>*>(new http_sync_connection<Tag,version_major,version_minor>(resolver, resolve)); |
| 161 | + } |
| 162 | + |
| 163 | + virtual void init_socket(string_type const & hostname, string_type const & port) = 0; |
| 164 | + virtual void send_request_impl(string_type const & method, basic_request<Tag> const & request_) = 0; |
| 165 | + virtual void read_status(basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) = 0; |
| 166 | + virtual void read_headers(basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) = 0; |
| 167 | + virtual void read_body(basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) = 0; |
| 168 | + virtual ~sync_connection_base() {} |
| 169 | + protected: |
| 170 | + sync_connection_base() {} |
| 171 | + }; |
| 172 | + |
| 173 | +} // namespace impl |
| 174 | + |
| 175 | +} // namespace http |
| 176 | + |
| 177 | +} // namespace network |
| 178 | + |
| 179 | +} // namespace boost |
| 180 | + |
| 181 | +#endif // BOOST_NETWORK_PROTOCOL_HTTP_IMPL_SYNC_CONNECTION_BASE_20091217 |
| 182 | + |
0 commit comments