Skip to content

Commit 4831538

Browse files
committed
Implementing keep-alive connections in HTTP 1.1, but no chunked transfer encoding support yet.
1 parent 288d7ae commit 4831538

14 files changed

+182
-28
lines changed

boost/network/protocol/http/client.hpp

+4
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ namespace boost { namespace network { namespace http {
7171
//
7272
// =================================================================
7373

74+
~basic_client() {
75+
connection_base::cleanup();
76+
}
77+
7478
void clear_resolved_cache() {
7579
connection_base::endpoint_cache_.clear();
7680
}

boost/network/protocol/http/detail/connection_helper.hpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <boost/network/version.hpp>
1010
#include <boost/foreach.hpp>
11+
#include <boost/network/protocol/http/traits/connection_keepalive.hpp>
1112

1213
namespace boost { namespace network { namespace http { namespace detail {
1314

@@ -47,6 +48,10 @@ namespace boost { namespace network { namespace http { namespace detail {
4748
<< "Host: " << request_.host() << "\r\n"
4849
<< "Accept: */*\r\n";
4950

51+
if (version_major == 1 && version_minor == 1)
52+
request_stream
53+
<< "Accept-Encoding: identity;q=1.0, *;q=0\r\n"; // only accept identity encoding
54+
5055
typename headers_range<http::basic_request<Tag> >::type range = headers(request_);
5156
BOOST_FOREACH(typename headers_range<http::basic_request<Tag> >::type::value_type const & header, range) {
5257
request_stream << header.first << ": " << header.second << "\r\n";
@@ -55,8 +60,12 @@ namespace boost { namespace network { namespace http { namespace detail {
5560
range = headers(request_)["user-agent"];
5661
if (empty(range)) request_stream << "User-Agent: cpp-netlib/" << BOOST_NETLIB_VERSION << "\r\n";
5762

63+
if (!connection_keepalive<Tag>::value) {
64+
request_stream
65+
<< "Connection: close\r\n";
66+
}
5867
request_stream
59-
<< "Connection: close\r\n\r\n";
68+
<< "\r\n";
6069

6170
string_type body_ = body(request_);
6271
if (!body_.empty())

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

+29-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ namespace boost { namespace network { namespace http { namespace impl {
3030
void read_status(basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) {}
3131
void read_headers(basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) {}
3232
void read_body(basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) {}
33+
bool is_open() { return socket_.lowest_layer().is_open(); }
34+
void close_socket() { if (is_open()) { socket_.lowest_layer().close(); } }
3335
~https_sync_connection() {}
3436
private:
3537
resolver_type & resolver_;
@@ -129,15 +131,38 @@ namespace boost { namespace network { namespace http { namespace impl {
129131
body_stream << &response_buffer;
130132

131133
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+
if (!connection_keepalive<Tag>::value) {
135+
while (boost::asio::read(socket_, response_buffer, boost::asio::transfer_at_least(1), error)) {
136+
body_stream << &response_buffer;
137+
}
138+
} else {
139+
// look for the content-length header
140+
typename headers_range<basic_response<Tag> >::type content_length_range =
141+
headers(response_)["Content-Length"];
142+
if (empty(content_length_range))
143+
throw std::runtime_error("Unsupported response, missing 'Content-Length' header.");
144+
size_t length = lexical_cast<size_t>(begin(content_length_range)->second);
145+
std::cerr << "Before reading body of size " << length << "...\n";
146+
size_t bytes_read = 0;
147+
while ((bytes_read = boost::asio::read(socket_, response_buffer, boost::asio::transfer_at_least(1), error))) {
148+
body_stream << &response_buffer;
149+
length -= bytes_read;
150+
if ((length <= 0) or error)
151+
break;
152+
}
153+
std::cerr << "After reading body of size " << length << "...\n";
154+
}
134155

135156
if (error != boost::asio::error::eof)
136157
throw boost::system::system_error(error);
137158

138159
response_ << body(body_stream.str());
139160
}
140161

162+
bool is_open() { return socket_.is_open(); }
163+
164+
void close_socket() { if (is_open()) { socket_.close(); } }
165+
141166
private:
142167

143168
resolver_type & resolver_;
@@ -165,6 +190,8 @@ namespace boost { namespace network { namespace http { namespace impl {
165190
virtual void read_status(basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) = 0;
166191
virtual void read_headers(basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) = 0;
167192
virtual void read_body(basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) = 0;
193+
virtual bool is_open() = 0;
194+
virtual void close_socket() = 0;
168195
virtual ~sync_connection_base() {}
169196
protected:
170197
sync_connection_base() {}

boost/network/protocol/http/policies/pooled_connection.hpp

+50-14
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@
1010

1111
#include <boost/unordered_map.hpp>
1212
#include <boost/shared_ptr.hpp>
13+
#include <utility>
1314
#include <boost/network/protocol/http/detail/connection_helper.hpp>
1415
#include <boost/network/protocol/http/impl/sync_connection_base.hpp>
16+
#include <boost/algorithm/string/predicate.hpp>
17+
18+
#include <iostream>
1519

1620
#ifndef BOOST_NETWORK_HTTP_MAXIMUM_REDIRECT_COUNT
1721
#define BOOST_NETWORK_HTTP_MAXIMUM_REDIRECT_COUNT 5
@@ -29,14 +33,22 @@ namespace boost { namespace network { namespace http {
2933
typedef function<typename resolver_base::resolver_iterator_pair(resolver_type &, string_type const &, string_type const &)> resolver_function_type;
3034

3135
struct connection_impl {
36+
typedef function<shared_ptr<connection_impl>(resolver_type &,basic_request<Tag> const &)> get_connection_function;
3237

33-
connection_impl(resolver_type & resolver, bool follow_redirect, string_type const & host, string_type const & port, resolver_function_type resolve, pooled_connection_policy & source)
34-
: pimpl(impl::sync_connection_base<Tag,version_major,version_minor>::new_connection(resolver, resolve)), source_(source) {}
38+
connection_impl(resolver_type & resolver, bool follow_redirect, string_type const & host, string_type const & port, resolver_function_type resolve, get_connection_function get_connection, bool https)
39+
: pimpl(impl::sync_connection_base<Tag,version_major,version_minor>::new_connection(resolver, resolve, https))
40+
, resolver_(resolver)
41+
, connection_follow_redirect_(follow_redirect)
42+
, get_connection_(get_connection) {}
3543

3644
basic_response<Tag> send_request(string_type const & method, basic_request<Tag> request_, bool get_body) {
3745
return send_request_recursive(method, request_, get_body, 0);
3846
}
3947

48+
~connection_impl () {
49+
pimpl.reset();
50+
}
51+
4052
private:
4153

4254
basic_response<Tag> send_request_recursive(string_type const & method, basic_request<Tag> request_, bool get_body, int count) {
@@ -45,49 +57,64 @@ namespace boost { namespace network { namespace http {
4557

4658
basic_response<Tag> response_;
4759
// check if the socket is open first
48-
if (!pimpl->socket_.is_open()) {
60+
if (!pimpl->is_open()) {
4961
pimpl->init_socket(request_.host(), lexical_cast<string_type>(request_.port()));
5062
}
63+
std::cerr << "Sending request to " << request_.host() << "...\n";
5164
pimpl->send_request_impl(method, request_);
5265
response_ = basic_response<Tag>();
5366
response_ << source(request_.host());
5467

5568
boost::asio::streambuf response_buffer;
69+
std::cerr << "Reading status from " << request_.host() << "...\n";
5670
pimpl->read_status(response_, response_buffer);
71+
std::cerr << "Reading headers from " << request_.host() << "...\n";
5772
pimpl->read_headers(response_, response_buffer);
58-
if (get_body) pimpl->read_body(response_, response_buffer);
73+
if (
74+
get_body && response_.status() != 304
75+
&& (response_.status() != 204)
76+
&& not (response_.status() >= 100 && response_.status() <= 199)
77+
) {
78+
std::cerr << "Reading body from " << request_.host() << "...\n";
79+
pimpl->read_body(response_, response_buffer);
80+
}
5981

60-
if (follow_redirect_) {
82+
if (connection_follow_redirect_) {
6183
boost::uint16_t status = response_.status();
6284
if (status >= 300 && status <= 307) {
6385
typename headers_range<basic_response<Tag> >::type location_range = headers(response_)["Location"];
6486
typename range_iterator<typename headers_range<basic_request<Tag> >::type>::type location_header = begin(location_range);
6587
if (location_header != end(location_range)) {
6688
request_.uri(location_header->second);
6789
connection_ptr connection_;
68-
connection_ = source_.get_connection(pimpl->resolver_, request_);
90+
connection_ = get_connection_(resolver_, request_);
6991
return connection_->send_request_recursive(method, request_, get_body, ++count);
7092
} else throw std::runtime_error("Location header not defined in redirect response.");
7193
}
7294
}
7395

96+
typename headers_range<basic_response<Tag> >::type connection_range = headers(response_)["Connection"];
97+
if (!empty(connection_range) && begin(connection_range)->second == string_type("close")) {
98+
std::cerr << "Before closing socket for " << request_.host() << ":" << request_.port() << "...\n";
99+
pimpl->close_socket();
100+
std::cerr << "After closing socket for " << request_.host() << ":" << request_.port() << "...\n";
101+
}
102+
74103
return response_;
75104
}
105+
76106
shared_ptr<http::impl::sync_connection_base<Tag,version_major,version_minor> > pimpl;
77-
pooled_connection_policy & source_;
78-
template <class T, unsigned vma, unsigned vmi> friend struct pooled_connection;
107+
resolver_type & resolver_;
108+
bool connection_follow_redirect_;
109+
get_connection_function get_connection_;
79110
};
80111

81112
typedef shared_ptr<connection_impl> connection_ptr;
82113

83-
private:
84-
85114
typedef unordered_map<string_type, connection_ptr> host_connection_map;
86115
host_connection_map host_connections;
87116
bool follow_redirect_;
88117

89-
protected:
90-
91118
connection_ptr get_connection(resolver_type & resolver, basic_request<Tag> const & request_) {
92119
string_type index = (request_.host() + ':') + lexical_cast<string_type>(request_.port());
93120
connection_ptr connection_;
@@ -104,15 +131,24 @@ namespace boost { namespace network { namespace http {
104131
this,
105132
_1, _2, _3
106133
)
107-
, *this
134+
, bind(
135+
&pooled_connection_policy<Tag,version_major,version_minor>::get_connection,
136+
this,
137+
_1, _2
138+
)
139+
, boost::iequals(request_.protocol(), string_type("https"))
108140
)
109141
);
110-
host_connections.insert(index, connection_);
142+
host_connections.insert(std::make_pair(index, connection_));
111143
return connection_;
112144
}
113145
return it->second;
114146
}
115147

148+
void cleanup() {
149+
host_connection_map().swap(host_connections);
150+
}
151+
116152
pooled_connection_policy(bool cache_resolved, bool follow_redirect)
117153
: resolver_base(cache_resolved), host_connections(), follow_redirect_(follow_redirect) {}
118154

boost/network/protocol/http/policies/simple_connection.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ namespace boost { namespace network { namespace http {
9292
return connection_;
9393
}
9494

95+
void cleanup() { }
96+
9597
simple_connection_policy(bool cache_resolved, bool follow_redirect)
9698
: resolver_base(cache_resolved), follow_redirect_(follow_redirect) {}
9799

boost/network/protocol/http/traits.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@
1313
// Convenience header for including different traits implementations.
1414
#include <boost/network/protocol/http/traits/message_traits.hpp>
1515
#include <boost/network/protocol/http/traits/parser_traits.hpp>
16+
#include <boost/network/protocol/http/traits/connection_keepalive.hpp>
1617

1718
#endif // BOOST_NETWORK_PROTOCOL_HTTP_TRAITS_HPP
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#ifndef BOOST_NETWORK_PROTOCOL_HTTP_TRAITS_CONECTION_KEEPALIVE_20091218
2+
#define BOOST_NETWORK_PROTOCOL_HTTP_TRAITS_CONECTION_KEEPALIVE_20091218
3+
4+
#include <boost/network/tags.hpp>
5+
6+
namespace boost { namespace network { namespace http {
7+
8+
template <class Tag>
9+
struct connection_keepalive {
10+
static bool const value = false;
11+
};
12+
13+
template <>
14+
struct connection_keepalive<tags::http_keepalive_8bit_udp_resolve> {
15+
static bool const value = true;
16+
};
17+
18+
template <>
19+
struct connection_keepalive<tags::http_keepalive_8bit_tcp_resolve> {
20+
static bool const value = true;
21+
};
22+
23+
} /* http */
24+
25+
} /* network */
26+
27+
} /* boost */
28+
29+
#endif // BOOST_NETWORK_PROTOCOL_HTTP_TRAITS_CONECTION_KEEPALIVE_20091218
30+

boost/network/protocol/http/traits/resolver.hpp

+10
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ namespace boost { namespace network { namespace http {
2828
typedef boost::asio::ip::udp::resolver type;
2929
};
3030

31+
template <>
32+
struct resolver<tags::http_keepalive_8bit_tcp_resolve> {
33+
typedef boost::asio::ip::tcp::resolver type;
34+
};
35+
36+
template <>
37+
struct resolver<tags::http_keepalive_8bit_udp_resolve> {
38+
typedef boost::asio::ip::udp::resolver type;
39+
};
40+
3141
} // namespace http
3242

3343
} // namespace network

boost/network/protocol/http/traits/resolver_policy.hpp

+10
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ namespace boost { namespace network { namespace http {
2929
typedef policies::sync_resolver<tags::http_default_8bit_udp_resolve> type;
3030
};
3131

32+
template <>
33+
struct resolver_policy<tags::http_keepalive_8bit_udp_resolve> {
34+
typedef policies::sync_resolver<tags::http_keepalive_8bit_udp_resolve> type;
35+
};
36+
37+
template <>
38+
struct resolver_policy<tags::http_keepalive_8bit_tcp_resolve> {
39+
typedef policies::sync_resolver<tags::http_keepalive_8bit_tcp_resolve> type;
40+
};
41+
3242
} // namespace http
3343

3444
} // namespace network

boost/network/traits/ostringstream.hpp

+16-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) Glyn Matthews 2009.
2+
// Copyright (c) Dean Michael Berris 2009.
23
// Distributed under the Boost Software License, Version 1.0.
34
// (See accompanying file LICENSE_1_0.txt or copy at
45
// http://www.boost.org/LICENSE_1_0.txt)
@@ -11,14 +12,14 @@
1112

1213
namespace boost { namespace network {
1314

14-
template <
15-
class Tag
16-
>
15+
template <class Tag>
16+
struct unsupported_tag;
17+
18+
template <class Tag>
1719
struct ostringstream {
18-
typedef void type;
20+
typedef unsupported_tag<Tag> type;
1921
};
2022

21-
2223
template <>
2324
struct ostringstream<tags::default_string> {
2425
typedef std::ostringstream type;
@@ -40,6 +41,16 @@ namespace boost { namespace network {
4041
typedef std::ostringstream type;
4142
};
4243

44+
template <>
45+
struct ostringstream<tags::http_keepalive_8bit_tcp_resolve> {
46+
typedef std::ostringstream type;
47+
};
48+
49+
template <>
50+
struct ostringstream<tags::http_keepalive_8bit_udp_resolve> {
51+
typedef std::ostringstream type;
52+
};
53+
4354
} // namespace network
4455

4556
} // namespace boost

boost/network/traits/string.hpp

+10
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ namespace boost { namespace network {
4646
typedef std::string type;
4747
};
4848

49+
template <>
50+
struct string<tags::http_keepalive_8bit_tcp_resolve> {
51+
typedef std::string type;
52+
};
53+
54+
template <>
55+
struct string<tags::http_keepalive_8bit_udp_resolve> {
56+
typedef std::string type;
57+
};
58+
4959
} // namespace network
5060

5161
} // namespace boost

0 commit comments

Comments
 (0)