Skip to content

Commit 6add11d

Browse files
author
Dean Michael Berris
committed
Refactoring implementation (incomplete) to support SSL; lots broken still more work to do -- this is a checkpoint commit, please bear with me.
1 parent dc151fc commit 6add11d

File tree

11 files changed

+441
-160
lines changed

11 files changed

+441
-160
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#ifndef BOOST_NETWORK_PROTOCOL_HTTP_DETAIL_CONNECTION_HELPER_20091217
2+
#define BOOST_NETWORK_PROTOCOL_HTTP_DETAIL_CONNECTION_HELPER_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/version.hpp>
10+
#include <boost/foreach.hpp>
11+
12+
namespace boost { namespace network { namespace http { namespace detail {
13+
14+
template <class Tag, unsigned version_major, unsigned version_minor>
15+
struct connection_helper {
16+
protected:
17+
18+
typedef typename string<Tag>::type string_type;
19+
20+
void create_request(boost::asio::streambuf & request_buffer, string_type const & method, basic_request<Tag> request_) const {
21+
// TODO make this use Boost.Karma instead of an ad-hoc implementation
22+
std::ostream request_stream(&request_buffer);
23+
24+
request_stream
25+
<< method << " ";
26+
27+
if (request_.path().empty() || request_.path()[0] != '/')
28+
request_stream << '/';
29+
30+
request_stream
31+
<< request_.path()
32+
;
33+
34+
if (!request_.query().empty())
35+
request_stream
36+
<< '?'
37+
<< request_.query()
38+
;
39+
40+
if (!request_.anchor().empty())
41+
request_stream
42+
<< '#'
43+
<< request_.anchor()
44+
;
45+
46+
request_stream << " HTTP/" << version_major << '.' << version_minor << "\r\n"
47+
<< "Host: " << request_.host() << "\r\n"
48+
<< "Accept: */*\r\n";
49+
50+
typename headers_range<http::basic_request<Tag> >::type range = headers(request_);
51+
BOOST_FOREACH(typename headers_range<http::basic_request<Tag> >::type::value_type const & header, range) {
52+
request_stream << header.first << ": " << header.second << "\r\n";
53+
};
54+
55+
range = headers(request_)["user-agent"];
56+
if (empty(range)) request_stream << "User-Agent: cpp-netlib/" << BOOST_NETLIB_VERSION << "\r\n";
57+
58+
request_stream
59+
<< "Connection: close\r\n\r\n";
60+
61+
string_type body_ = body(request_);
62+
if (!body_.empty())
63+
request_stream << body_;
64+
}
65+
66+
};
67+
68+
} // namespace detail
69+
70+
} // namespace http
71+
72+
} // namespace network
73+
74+
} // namespace boost
75+
76+
#endif // BOOST_NETWORK_PROTOCOL_HTTP_DETAIL_CONNECTION_HELPER_20091217
77+

boost/network/protocol/http/impl/request.hpp

+5
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ namespace boost { namespace network { namespace http {
8484
string_type const anchor() const {
8585
return uri_.fragment();
8686
}
87+
88+
string_type const protocol() const {
89+
return uri_.protocol();
90+
}
91+
8792
};
8893

8994
template <class Tag>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
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

Comments
 (0)