Skip to content

Commit b8bb431

Browse files
committed
Refactoring connection management out of the basic_client implementation.
1 parent 5925e6f commit b8bb431

File tree

6 files changed

+315
-185
lines changed

6 files changed

+315
-185
lines changed

boost/network/protocol/http/client.hpp

+14-180
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,16 @@
77
#ifndef __NETWORK_PROTOCOL_HTTP_CLIENT_20070908_1_HPP__
88
#define __NETWORK_PROTOCOL_HTTP_CLIENT_20070908_1_HPP__
99

10-
#ifndef BOOST_NETLIB_VERSION
11-
#define BOOST_NETLIB_VERSION "0.5"
12-
#endif
13-
10+
#include <boost/network/version.hpp>
1411
#include <boost/network/traits/ostringstream.hpp>
1512
#include <boost/network/protocol/http/message.hpp>
1613
#include <boost/network/protocol/http/response.hpp>
1714
#include <boost/network/protocol/http/request.hpp>
18-
#include <boost/network/protocol/http/traits/resolver_policy.hpp>
15+
#include <boost/network/protocol/http/traits/connection_policy.hpp>
1916
#include <boost/asio.hpp>
2017
#include <boost/lexical_cast.hpp>
2118
#include <boost/algorithm/string/classification.hpp>
2219
#include <boost/algorithm/string/split.hpp>
23-
#include <boost/tuple/tuple.hpp>
2420
#include <boost/foreach.hpp>
2521
#include <ostream>
2622
#include <istream>
@@ -31,184 +27,22 @@
3127
namespace boost { namespace network { namespace http {
3228

3329
template <class Tag, unsigned version_major, unsigned version_minor>
34-
class basic_client : resolver_policy<Tag>::type {
30+
class basic_client : connection_policy<Tag, version_major, version_minor>::type {
3531

3632
private:
37-
typedef typename resolver_policy<Tag>::type resolver_base;
33+
typedef typename connection_policy<Tag, version_major, version_minor>::type connection_base;
3834
boost::asio::io_service service_;
39-
typename resolver_base::resolver_type resolver_;
40-
bool follow_redirect_;
35+
typename connection_base::resolver_type resolver_;
4136

4237
typedef typename string<Tag>::type string_type;
4338

44-
void init_socket(boost::asio::ip::tcp::socket & socket_, string_type const & hostname, string_type const & port) {
45-
using boost::asio::ip::tcp;
46-
47-
boost::system::error_code error = boost::asio::error::host_not_found;
48-
49-
typename resolver_base::resolver_type::iterator endpoint_iterator, end;
50-
boost::tie(endpoint_iterator, end) = resolve(resolver_, hostname, port);
51-
52-
while (error && endpoint_iterator != end) {
53-
socket_.close();
54-
socket_.connect(
55-
tcp::endpoint(
56-
endpoint_iterator->endpoint().address()
57-
, endpoint_iterator->endpoint().port()
58-
)
59-
, error
60-
);
61-
++endpoint_iterator;
62-
}
63-
64-
if (error)
65-
throw boost::system::system_error(error);
66-
};
67-
68-
void create_request(boost::asio::streambuf & request_buffer, string_type const & method, basic_request<Tag> request_) const {
69-
std::ostream request_stream(&request_buffer);
70-
71-
request_stream
72-
<< method << " ";
73-
74-
if (request_.path().empty() || request_.path()[0] != '/')
75-
request_stream << '/';
76-
77-
request_stream
78-
<< request_.path()
79-
;
80-
81-
if (!request_.query().empty())
82-
request_stream
83-
<< '?'
84-
<< request_.query()
85-
;
86-
87-
if (!request_.anchor().empty())
88-
request_stream
89-
<< '#'
90-
<< request_.anchor()
91-
;
92-
93-
request_stream << " HTTP/" << version_major << '.' << version_minor << "\r\n"
94-
<< "Host: " << request_.host() << "\r\n"
95-
<< "Accept: */*\r\n";
96-
97-
typename headers_range<http::basic_request<Tag> >::type range = headers(request_);
98-
BOOST_FOREACH(typename headers_range<http::basic_request<Tag> >::type::value_type const & header, range) {
99-
request_stream << header.first << ": " << header.second << "\r\n";
100-
};
101-
102-
range = headers(request_)["user-agent"];
103-
if (begin(range) == end(range)) request_stream << "User-Agent: cpp-netlib/" << BOOST_NETLIB_VERSION << "\r\n";
104-
105-
request_stream
106-
<< "Connection: close\r\n\r\n";
107-
108-
string_type body_ = body(request_);
109-
if (!body_.empty())
110-
request_stream << body_;
111-
};
112-
113-
void send_request(boost::asio::ip::tcp::socket & socket, string_type const & method, basic_request<Tag> const & request_) const {
114-
boost::asio::streambuf request_buffer;
115-
create_request(request_buffer, method, request_);
116-
write(socket, request_buffer);
117-
};
118-
119-
void read_status(basic_response<Tag> & response_, boost::asio::ip::tcp::socket & socket, boost::asio::streambuf & response_buffer) const {
120-
boost::asio::read_until(socket, response_buffer, "\r\n");
121-
std::istream response_stream(&response_buffer);
122-
string_type http_version;
123-
unsigned int status_code;
124-
string_type status_message;
125-
response_stream >> http_version
126-
>> status_code;
127-
std::getline(response_stream, status_message);
128-
trim_left(status_message);
129-
trim_right_if(status_message, boost::is_space() || boost::is_any_of("\r"));
130-
131-
if (!response_stream || http_version.substr(0, 5) != "HTTP/")
132-
throw std::runtime_error("Invalid response");
133-
134-
response_.version() = http_version;
135-
response_.status() = status_code;
136-
response_.status_message() = status_message;
137-
};
138-
139-
void read_headers(basic_response<Tag> & response_, boost::asio::ip::tcp::socket & socket, boost::asio::streambuf & response_buffer) const {
140-
boost::asio::read_until(socket, response_buffer, "\r\n\r\n");
141-
std::istream response_stream(&response_buffer);
142-
string_type header_line, name;
143-
while (std::getline(response_stream, header_line) && header_line != "\r") {
144-
trim_right_if(header_line, boost::is_space() || boost::is_any_of("\r"));
145-
typename string_type::size_type colon_offset;
146-
if (header_line.size() && header_line[0] == ' ') {
147-
assert(!name.empty());
148-
if (name.empty())
149-
throw std::runtime_error(
150-
std::string("Malformed header: ")
151-
+ header_line
152-
);
153-
response_
154-
<< header(name, trim_left_copy(header_line));
155-
} else if ((colon_offset = header_line.find_first_of(':')) != string_type::npos) {
156-
name = header_line.substr(0, colon_offset);
157-
response_
158-
<< header(name, header_line.substr(colon_offset+2));
159-
};
160-
};
161-
};
162-
163-
void read_body(basic_response<Tag> & response_, boost::asio::ip::tcp::socket & socket, boost::asio::streambuf & response_buffer) const {
164-
typename ostringstream<Tag>::type body_stream;
165-
166-
if (response_buffer.size() > 0)
167-
body_stream << &response_buffer;
168-
169-
boost::system::error_code error;
170-
while (boost::asio::read(socket, response_buffer, boost::asio::transfer_at_least(1), error))
171-
body_stream << &response_buffer;
172-
173-
if (error != boost::asio::error::eof)
174-
throw boost::system::system_error(error);
175-
176-
response_ << body(body_stream.str());
177-
};
178-
17939
basic_response<Tag> const sync_request_skeleton(basic_request<Tag> const & request_, string_type method, bool get_body) {
18040
using boost::asio::ip::tcp;
18141

182-
basic_request<Tag> request_copy(request_);
183-
basic_response<Tag> response_;
184-
do {
185-
tcp::socket socket(service_);
186-
init_socket(socket, request_copy.host(), boost::lexical_cast<string_type>(request_copy.port()));
187-
send_request(socket, method, request_copy);
188-
189-
response_ = basic_response<Tag>();
190-
response_ << source(request_copy.host());
191-
192-
boost::asio::streambuf response_buffer;
193-
read_status(response_, socket, response_buffer);
194-
read_headers(response_, socket, response_buffer);
195-
if (get_body)
196-
read_body(response_, socket, response_buffer);
197-
198-
if (follow_redirect_) {
199-
uint16_t status = response_.status();
200-
if (status >= 300 && status <= 307) {
201-
typename headers_range<http::basic_response<Tag> >::type location_range = headers(response_)["Location"];
202-
typename range_iterator<typename headers_range<http::basic_request<Tag> >::type>::type location_header = begin(location_range);
203-
if (location_header != end(location_range)) {
204-
request_copy.uri(location_header->second);
205-
} else throw std::runtime_error("Location header not defined in redirect response.");
206-
} else break;
207-
} else break;
208-
} while(true);
209-
210-
return response_;
211-
};
42+
typename connection_base::connection_ptr connection_;
43+
connection_ = connection_base::get_connection(resolver_, request_);
44+
return connection_->send_request(method, request_, get_body);
45+
}
21246

21347
public:
21448

@@ -229,23 +63,23 @@ namespace boost { namespace network { namespace http {
22963
};
23064

23165
basic_client()
232-
: resolver_base(false), service_(), resolver_(service_), follow_redirect_(false)
66+
: connection_base(false, false), service_(), resolver_(service_)
23367
{};
23468

23569
explicit basic_client(cache_resolved_type (*)())
236-
: resolver_base(true), service_(), resolver_(service_), follow_redirect_(false)
70+
: connection_base(true, false), service_(), resolver_(service_)
23771
{};
23872

23973
explicit basic_client(follow_redirect_type (*)())
240-
: resolver_base(false), service_(), resolver_(service_), follow_redirect_(true)
74+
: connection_base(false, true), service_(), resolver_(service_)
24175
{};
24276

24377
basic_client(cache_resolved_type (*)(), follow_redirect_type (*)())
244-
: resolver_base(false), service_(), resolver_(service_), follow_redirect_(true)
78+
: connection_base(true, true), service_(), resolver_(service_)
24579
{};
24680

24781
void clear_resolved_cache() {
248-
resolver_base::endpoint_cache_.clear();
82+
connection_base::endpoint_cache_.clear();
24983
}
25084

25185
basic_response<Tag> const head (basic_request<Tag> const & request_) {

boost/network/protocol/http/client_fwd.hpp

+1-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@
77
#ifndef __NETWORK_PROTOCOL_HTTP_CLIENT_20080923_1_HPP__
88
#define __NETWORK_PROTOCOL_HTTP_CLIENT_20080923_1_HPP__
99

10-
#ifndef BOOST_NETLIB_VERSION
11-
#define BOOST_NETLIB_VERSION "0.5"
12-
#endif
13-
10+
#include <boost/network/version.hpp>
1411
#include <boost/network/tags.hpp>
1512

1613
namespace boost { namespace network { namespace http {

0 commit comments

Comments
 (0)