Skip to content

Commit 7e42af9

Browse files
committed
Adding support for HTTP 1.0 responses when using an HTTP 1.1 client.
1 parent 9da4cad commit 7e42af9

File tree

4 files changed

+88
-76
lines changed

4 files changed

+88
-76
lines changed

boost/network/protocol/http/client.hpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,11 @@ namespace boost { namespace network { namespace http {
9191
return sync_request_skeleton(request_, "POST", true);
9292
};
9393

94-
response const post (request const & request_, string_type const & content_type, string_type const & body_) {
95-
request request_copy = request_;
96-
request_copy << body(body_)
94+
response const post (request request_, string_type const & content_type, string_type const & body_) {
95+
request_ << body(body_)
9796
<< header("Content-Type", content_type)
9897
<< header("Content-Length", boost::lexical_cast<string_type>(body_.size()));
99-
return post(request_copy);
98+
return post(request_);
10099
};
101100

102101
response const post (request const & request_, string_type const & body_) {
@@ -111,12 +110,11 @@ namespace boost { namespace network { namespace http {
111110
return put(request_, "x-application/octet-stream", body_);
112111
};
113112

114-
response const put (request const & request_, string_type const & content_type, string_type const & body_) {
115-
request request_copy = request_;
116-
request_copy << body(body_)
113+
response const put (request request_, string_type const & content_type, string_type const & body_) {
114+
request_ << body(body_)
117115
<< header("Content-Type", content_type)
118116
<< header("Content-Length", boost::lexical_cast<string_type>(body_.size()));
119-
return put(request_copy);
117+
return put(request_);
120118
};
121119

122120
response const delete_ (request const & request_) {
@@ -137,7 +135,7 @@ namespace boost { namespace network { namespace http {
137135

138136
};
139137

140-
typedef basic_client<tags::http_default_8bit_tcp_resolve, 1, 0> client;
138+
typedef basic_client<tags::http_default_8bit_udp_resolve, 1, 0> client;
141139

142140
} // namespace http
143141

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

Lines changed: 76 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -99,74 +99,87 @@ namespace boost { namespace network { namespace http { namespace impl {
9999
}
100100

101101
template <class Socket>
102-
void read_body(Socket & socket_, basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) {
103-
typename ostringstream<Tag>::type body_stream;
104-
102+
void read_body_normal(Socket & socket_, basic_response<Tag> & response_, boost::asio::streambuf & response_buffer, typename ostringstream<Tag>::type & body_stream) {
105103
boost::system::error_code error;
106-
// TODO tag dispatch based on whether it's HTTP 1.0 or HTTP 1.1
107-
if (version_major == 1 && version_minor == 0) {
108-
if (response_buffer.size() > 0)
109-
body_stream << &response_buffer;
104+
if (response_buffer.size() > 0)
105+
body_stream << &response_buffer;
110106

111-
while (boost::asio::read(socket_, response_buffer, boost::asio::transfer_at_least(1), error)) {
107+
while (boost::asio::read(socket_, response_buffer, boost::asio::transfer_at_least(1), error)) {
108+
body_stream << &response_buffer;
109+
}
110+
}
111+
112+
template <class Socket>
113+
void read_body_transfer_chunk_encoding(Socket & socket_, basic_response<Tag> & response_, boost::asio::streambuf & response_buffer, typename ostringstream<Tag>::type & body_stream) {
114+
boost::system::error_code error;
115+
// look for the content-length header
116+
typename headers_range<basic_response<Tag> >::type content_length_range =
117+
headers(response_)["Content-Length"];
118+
if (empty(content_length_range)) {
119+
typename headers_range<basic_response<Tag> >::type transfer_encoding_range =
120+
headers(response_)["Transfer-Encoding"];
121+
if (empty(transfer_encoding_range)) throw std::runtime_error("Missing Transfer-Encoding Header from response.");
122+
if (boost::iequals(begin(transfer_encoding_range)->second, "chunked")) {
123+
bool stopping = false;
124+
do {
125+
std::size_t chunk_size_line = read_until(socket_, response_buffer, "\r\n", error);
126+
if ((chunk_size_line == 0) && (error != boost::asio::error::eof)) throw boost::system::system_error(error);
127+
std::size_t chunk_size = 0;
128+
string_type data;
129+
{
130+
std::istream chunk_stream(&response_buffer);
131+
std::getline(chunk_stream, data);
132+
typename istringstream<Tag>::type chunk_size_stream(data);
133+
chunk_size_stream >> std::hex >> chunk_size;
134+
}
135+
if (chunk_size == 0) {
136+
stopping = true;
137+
if (!read_until(socket_, response_buffer, "\r\n", error) && (error != boost::asio::error::eof))
138+
throw boost::system::system_error(error);
139+
} else {
140+
bool stopping_inner = false;
141+
do {
142+
std::size_t chunk_bytes_read = read(socket_, response_buffer, boost::asio::transfer_at_least(chunk_size + 2), error);
143+
if (chunk_bytes_read == 0) {
144+
if (error != boost::asio::error::eof) throw boost::system::system_error(error);
145+
stopping_inner = true;
146+
}
147+
148+
std::istreambuf_iterator<char> eos;
149+
std::istreambuf_iterator<char> stream_iterator(&response_buffer);
150+
for (; chunk_size > 0 && stream_iterator != eos; --chunk_size)
151+
body_stream << *stream_iterator++;
152+
response_buffer.consume(2);
153+
} while (!stopping_inner && chunk_size != 0);
154+
155+
if (chunk_size != 0)
156+
throw std::runtime_error("Size mismatch between tranfer encoding chunk data size and declared chunk size.");
157+
}
158+
} while (!stopping);
159+
} else throw std::runtime_error("Unsupported Transfer-Encoding.");
160+
} else {
161+
size_t length = lexical_cast<size_t>(begin(content_length_range)->second);
162+
size_t bytes_read = 0;
163+
while ((bytes_read = boost::asio::read(socket_, response_buffer, boost::asio::transfer_at_least(1), error))) {
112164
body_stream << &response_buffer;
165+
length -= bytes_read;
166+
if ((length <= 0) or error)
167+
break;
113168
}
169+
}
170+
}
171+
172+
template <class Socket>
173+
void read_body(Socket & socket_, basic_response<Tag> & response_, boost::asio::streambuf & response_buffer) {
174+
typename ostringstream<Tag>::type body_stream;
175+
// TODO tag dispatch based on whether it's HTTP 1.0 or HTTP 1.1
176+
if (version_major == 1 && version_minor == 0) {
177+
read_body_normal(socket_, response_, response_buffer, body_stream);
114178
} else if (version_major == 1 && version_minor == 1) {
115-
// look for the content-length header
116-
typename headers_range<basic_response<Tag> >::type content_length_range =
117-
headers(response_)["Content-Length"];
118-
if (empty(content_length_range)) {
119-
typename headers_range<basic_response<Tag> >::type transfer_encoding_range =
120-
headers(response_)["Transfer-Encoding"];
121-
if (empty(transfer_encoding_range)) throw std::runtime_error("Missing Transfer-Encoding Header from response.");
122-
if (boost::iequals(begin(transfer_encoding_range)->second, "chunked")) {
123-
bool stopping = false;
124-
do {
125-
std::size_t chunk_size_line = read_until(socket_, response_buffer, "\r\n", error);
126-
if ((chunk_size_line == 0) && (error != boost::asio::error::eof)) throw boost::system::system_error(error);
127-
std::size_t chunk_size = 0;
128-
string_type data;
129-
{
130-
std::istream chunk_stream(&response_buffer);
131-
std::getline(chunk_stream, data);
132-
typename istringstream<Tag>::type chunk_size_stream(data);
133-
chunk_size_stream >> std::hex >> chunk_size;
134-
}
135-
if (chunk_size == 0) {
136-
stopping = true;
137-
if (!read_until(socket_, response_buffer, "\r\n", error) && (error != boost::asio::error::eof))
138-
throw boost::system::system_error(error);
139-
} else {
140-
bool stopping_inner = false;
141-
do {
142-
std::size_t chunk_bytes_read = read(socket_, response_buffer, boost::asio::transfer_at_least(chunk_size + 2), error);
143-
if (chunk_bytes_read == 0) {
144-
if (error != boost::asio::error::eof) throw boost::system::system_error(error);
145-
stopping_inner = true;
146-
}
147-
148-
std::istreambuf_iterator<char> eos;
149-
std::istreambuf_iterator<char> stream_iterator(&response_buffer);
150-
for (; chunk_size > 0 && stream_iterator != eos; --chunk_size)
151-
body_stream << *stream_iterator++;
152-
response_buffer.consume(2);
153-
} while (!stopping_inner && chunk_size != 0);
154-
155-
if (chunk_size != 0)
156-
throw std::runtime_error("Size mismatch between tranfer encoding chunk data size and declared chunk size.");
157-
}
158-
} while (!stopping);
159-
} else throw std::runtime_error("Unsupported Transfer-Encoding.");
160-
} else {
161-
size_t length = lexical_cast<size_t>(begin(content_length_range)->second);
162-
size_t bytes_read = 0;
163-
while ((bytes_read = boost::asio::read(socket_, response_buffer, boost::asio::transfer_at_least(1), error))) {
164-
body_stream << &response_buffer;
165-
length -= bytes_read;
166-
if ((length <= 0) or error)
167-
break;
168-
}
169-
}
179+
if (response_.version() == "HTTP/1.0")
180+
read_body_normal(socket_, response_, response_buffer, body_stream);
181+
else
182+
read_body_transfer_chunk_encoding(socket_, response_, response_buffer, body_stream);
170183
} else {
171184
throw std::runtime_error("Unsupported HTTP version number.");
172185
}

libs/network/test/http_1_1_test.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(http_get_test, T, tag_types) {
1919
http::basic_request<T> request("http://www.boost.org/");
2020
http::basic_client<T, 1, 1> client_;
2121
http::basic_response<T> response_;
22-
response_ = client_.get(request);
22+
BOOST_CHECK_NO_THROW ( response_ = client_.get(request) );
2323
typename headers_range<typename http::basic_response<T> >::type range = headers(response_)["Content-Type"];
2424
BOOST_CHECK ( begin(range) != end(range) );
2525
BOOST_CHECK ( body(response_).size() != 0 );
@@ -29,7 +29,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(http_get_test_different_port, T, tag_types) {
2929
http::basic_request<T> request("http://www.boost.org:80/");
3030
http::basic_client<T, 1, 1> client_;
3131
http::basic_response<T> response_;
32-
response_ = client_.get(request);
32+
BOOST_CHECK_NO_THROW ( response_ = client_.get(request) );
3333
typename headers_range<typename http::basic_response<T> >::type range = headers(response_)["Content-Type"];
3434
BOOST_CHECK ( begin(range) != end(range) );
3535
BOOST_CHECK ( body(response_).size() != 0 );

libs/network/test/http_localhost_tests.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <boost/config.hpp>
1414
#include <boost/test/unit_test.hpp>
1515
#include <boost/network/include/http/client.hpp>
16+
#include <boost/range.hpp>
1617
#include <boost/cast.hpp>
1718
#include <string>
1819
#include <fstream>
@@ -209,7 +210,7 @@ BOOST_AUTO_TEST_CASE(cgi_query) {
209210
http::client::response r;
210211
BOOST_REQUIRE_NO_THROW(r = c.get(req));
211212
BOOST_CHECK(body(r).length() != 0);
212-
BOOST_CHECK(headers(r)["Content-Type"].begin() != headers(r)["Content-Type"].end());
213+
BOOST_CHECK(boost::empty(headers(r)["Content-Length"]));
213214
}
214215

215216
BOOST_AUTO_TEST_CASE(cgi_multi_line_headers) {
@@ -220,7 +221,7 @@ BOOST_AUTO_TEST_CASE(cgi_multi_line_headers) {
220221
http::client::response r;
221222
BOOST_REQUIRE_NO_THROW(r = c.get(req));
222223
BOOST_CHECK(body(r).length() != 0);
223-
BOOST_CHECK(headers(r)["Content-Type"].begin() != headers(r)["Content-Type"].end());
224+
BOOST_CHECK(boost::empty(headers(r)["Content-Type"]));
224225
headers_range<http::client::response>::type range=headers(r)["X-CppNetlib-Test"];
225226
BOOST_REQUIRE(begin(range) != end(range));
226227
BOOST_REQUIRE(distance(range) == 2);

0 commit comments

Comments
 (0)