Skip to content

Commit 0aed5d1

Browse files
committed
Implementing support for HTTP 1.1 chunked encoding, and keepalive connections.
1 parent c3eeb92 commit 0aed5d1

File tree

5 files changed

+125
-30
lines changed

5 files changed

+125
-30
lines changed

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

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
#include <boost/network/protocol/http/traits/resolver_policy.hpp>
1010
#include <boost/network/protocol/http/detail/connection_helper.hpp>
11+
#include <boost/network/traits/ostringstream.hpp>
12+
#include <boost/network/traits/istringstream.hpp>
1113
#include <boost/asio/ssl.hpp>
1214
#include <boost/tuple/tuple.hpp>
1315

@@ -97,35 +99,75 @@ namespace boost { namespace network { namespace http { namespace impl {
9799
void read_body(Socket & socket_, basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) {
98100
typename ostringstream<Tag>::type body_stream;
99101

100-
if (response_buffer.size() > 0)
101-
body_stream << &response_buffer;
102-
103102
boost::system::error_code error;
104-
if (!connection_keepalive<Tag>::value) {
103+
// TODO tag dispatch based on whether it's HTTP 1.0 or HTTP 1.1
104+
if (version_major == 1 && version_minor == 0) {
105+
if (response_buffer.size() > 0)
106+
body_stream << &response_buffer;
107+
105108
while (boost::asio::read(socket_, response_buffer, boost::asio::transfer_at_least(1), error)) {
106109
body_stream << &response_buffer;
107110
}
108-
} else {
111+
} else if (version_major == 1 && version_minor == 1) {
109112
// look for the content-length header
110113
typename headers_range<basic_response<Tag> >::type content_length_range =
111114
headers(response_)["Content-Length"];
112-
if (empty(content_length_range))
113-
throw std::runtime_error("Unsupported response, missing 'Content-Length' header.");
114-
size_t length = lexical_cast<size_t>(begin(content_length_range)->second);
115-
std::cerr << "Before reading body of size " << length << "...\n";
116-
size_t bytes_read = 0;
117-
while ((bytes_read = boost::asio::read(socket_, response_buffer, boost::asio::transfer_at_least(1), error))) {
118-
body_stream << &response_buffer;
119-
length -= bytes_read;
120-
if ((length <= 0) or error)
121-
break;
115+
if (empty(content_length_range)) {
116+
typename headers_range<basic_response<Tag> >::type transfer_encoding_range =
117+
headers(response_)["Transfer-Encoding"];
118+
if (empty(transfer_encoding_range)) throw std::runtime_error("Missing Transfer-Encoding Header from response.");
119+
if (boost::iequals(begin(transfer_encoding_range)->second, "chunked")) {
120+
bool stopping = false;
121+
do {
122+
std::size_t chunk_size_line = read_until(socket_, response_buffer, "\r\n", error);
123+
if ((chunk_size_line == 0) && (error != boost::asio::error::eof)) throw boost::system::system_error(error);
124+
std::size_t chunk_size = 0;
125+
string_type data;
126+
{
127+
std::istream chunk_stream(&response_buffer);
128+
std::getline(chunk_stream, data);
129+
typename istringstream<Tag>::type chunk_size_stream(data);
130+
chunk_size_stream >> std::hex >> chunk_size;
131+
}
132+
if (chunk_size == 0) {
133+
stopping = true;
134+
if (!read_until(socket_, response_buffer, "\r\n", error) && (error != boost::asio::error::eof))
135+
throw boost::system::system_error(error);
136+
} else {
137+
bool stopping_inner = false;
138+
do {
139+
std::size_t chunk_bytes_read = read(socket_, response_buffer, boost::asio::transfer_at_least(chunk_size + 2), error);
140+
if (chunk_bytes_read == 0) {
141+
if (error != boost::asio::error::eof) throw boost::system::system_error(error);
142+
stopping_inner = true;
143+
}
144+
145+
std::istreambuf_iterator<char> eos;
146+
std::istreambuf_iterator<char> stream_iterator(&response_buffer);
147+
for (; chunk_size > 0 && stream_iterator != eos; --chunk_size)
148+
body_stream << *stream_iterator++;
149+
response_buffer.consume(2);
150+
} while (!stopping_inner && chunk_size != 0);
151+
152+
if (chunk_size != 0)
153+
throw std::runtime_error("Size mismatch between tranfer encoding chunk data size and declared chunk size.");
154+
}
155+
} while (!stopping);
156+
} else throw std::runtime_error("Unsupported Transfer-Encoding.");
157+
} else {
158+
size_t length = lexical_cast<size_t>(begin(content_length_range)->second);
159+
size_t bytes_read = 0;
160+
while ((bytes_read = boost::asio::read(socket_, response_buffer, boost::asio::transfer_at_least(1), error))) {
161+
body_stream << &response_buffer;
162+
length -= bytes_read;
163+
if ((length <= 0) or error)
164+
break;
165+
}
122166
}
123-
std::cerr << "After reading body of size " << length << "...\n";
167+
} else {
168+
throw std::runtime_error("Unsupported HTTP version number.");
124169
}
125170

126-
if (error != boost::asio::error::eof)
127-
throw boost::system::system_error(error);
128-
129171
response_ << body(body_stream.str());
130172
}
131173

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

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@
1010

1111
#include <boost/unordered_map.hpp>
1212
#include <boost/shared_ptr.hpp>
13-
#include <utility>
1413
#include <boost/network/protocol/http/detail/connection_helper.hpp>
1514
#include <boost/network/protocol/http/impl/sync_connection_base.hpp>
1615
#include <boost/algorithm/string/predicate.hpp>
17-
18-
#include <iostream>
16+
#include <utility>
1917

2018
#ifndef BOOST_NETWORK_HTTP_MAXIMUM_REDIRECT_COUNT
2119
#define BOOST_NETWORK_HTTP_MAXIMUM_REDIRECT_COUNT 5
@@ -60,22 +58,18 @@ namespace boost { namespace network { namespace http {
6058
if (!pimpl->is_open()) {
6159
pimpl->init_socket(request_.host(), lexical_cast<string_type>(request_.port()));
6260
}
63-
std::cerr << "Sending request to " << request_.host() << "...\n";
6461
pimpl->send_request_impl(method, request_);
6562
response_ = basic_response<Tag>();
6663
response_ << source(request_.host());
6764

6865
boost::asio::streambuf response_buffer;
69-
std::cerr << "Reading status from " << request_.host() << "...\n";
7066
pimpl->read_status(response_, response_buffer);
71-
std::cerr << "Reading headers from " << request_.host() << "...\n";
7267
pimpl->read_headers(response_, response_buffer);
7368
if (
7469
get_body && response_.status() != 304
7570
&& (response_.status() != 204)
7671
&& not (response_.status() >= 100 && response_.status() <= 199)
7772
) {
78-
std::cerr << "Reading body from " << request_.host() << "...\n";
7973
pimpl->read_body(response_, response_buffer);
8074
}
8175

@@ -95,9 +89,7 @@ namespace boost { namespace network { namespace http {
9589

9690
typename headers_range<basic_response<Tag> >::type connection_range = headers(response_)["Connection"];
9791
if (!empty(connection_range) && begin(connection_range)->second == string_type("close")) {
98-
std::cerr << "Before closing socket for " << request_.host() << ":" << request_.port() << "...\n";
9992
pimpl->close_socket();
100-
std::cerr << "After closing socket for " << request_.host() << ":" << request_.port() << "...\n";
10193
}
10294

10395
return response_;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) Glyn Matthews 2009.
2+
// Copyright (c) Dean Michael Berris 2009.
3+
// Distributed under the Boost Software License, Version 1.0.
4+
// (See accompanying file LICENSE_1_0.txt or copy at
5+
// http://www.boost.org/LICENSE_1_0.txt)
6+
7+
#ifndef BOOST_NETWORK_TRAITS_ISTRINGSTREAM_INC
8+
# define BOOST_NETWORK_TRAITS_ISTRINGSTREAM_INC
9+
10+
# include <sstream>
11+
# include <boost/network/tags.hpp>
12+
13+
namespace boost { namespace network {
14+
15+
template <class Tag>
16+
struct unsupported_tag;
17+
18+
template <class Tag>
19+
struct istringstream {
20+
typedef unsupported_tag<Tag> type;
21+
};
22+
23+
template <>
24+
struct istringstream<tags::default_string> {
25+
typedef std::istringstream type;
26+
};
27+
28+
29+
template <>
30+
struct istringstream<tags::default_wstring> {
31+
typedef std::wistringstream type;
32+
};
33+
34+
template <>
35+
struct istringstream<tags::http_default_8bit_tcp_resolve> {
36+
typedef std::istringstream type;
37+
};
38+
39+
template <>
40+
struct istringstream<tags::http_default_8bit_udp_resolve> {
41+
typedef std::istringstream type;
42+
};
43+
44+
template <>
45+
struct istringstream<tags::http_keepalive_8bit_tcp_resolve> {
46+
typedef std::istringstream type;
47+
};
48+
49+
template <>
50+
struct istringstream<tags::http_keepalive_8bit_udp_resolve> {
51+
typedef std::istringstream type;
52+
};
53+
54+
} // namespace network
55+
56+
} // namespace boost
57+
58+
59+
#endif // BOOST_NETWORK_TRAITS_ISTRINGSTREAM_INC
60+

libs/network/example/http_client.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ int main(int argc, char * argv[]) {
5757
using namespace boost::network;
5858

5959
http::basic_client<tags::http_keepalive_8bit_udp_resolve,1,1>::request request(source);
60+
request << header("Connection", "close");
6061
http::basic_client<tags::http_keepalive_8bit_udp_resolve,1,1> client(
6162
http::basic_client<tags::http_keepalive_8bit_udp_resolve,1,1>::follow_redirects
6263
);

libs/network/example/simple_wget.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ main(int argc, char *argv[]) {
4747
try {
4848
http::client client;
4949
const char *uri = argv[1];
50-
http::request request(uri);
51-
http::response response = client.get(request);
50+
http::client::request request(uri);
51+
http::client::response response = client.get(request);
5252

5353
std::string filename = get_filename(uri);
5454
std::cout << "Saving to: " << filename << std::endl;

0 commit comments

Comments
 (0)