Skip to content

Commit b4dc8ce

Browse files
committed
Allow overriding defaulted headers.
This change fixes the linearization of HTTP requests so that two things happen: - empty headers are ignored - explicitly defining the defaulted headers will suppress defaults Fixes #263
1 parent 79f6727 commit b4dc8ce

File tree

3 files changed

+145
-38
lines changed

3 files changed

+145
-38
lines changed

boost/network/protocol/http/algorithms/linearize.hpp

Lines changed: 76 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
// (See accompanying file LICENSE_1_0.txt or copy at
77
// http://www.boost.org/LICENSE_1_0.txt)
88

9+
#include <algorithm>
10+
#include <bitset>
911
#include <boost/network/traits/string.hpp>
1012
#include <boost/network/protocol/http/message/header/name.hpp>
1113
#include <boost/network/protocol/http/message/header/value.hpp>
@@ -15,6 +17,7 @@
1517
#include <boost/concept/requires.hpp>
1618
#include <boost/optional.hpp>
1719
#include <boost/range/algorithm/copy.hpp>
20+
#include <boost/algorithm/string/compare.hpp>
1821

1922
namespace boost { namespace network { namespace http {
2023

@@ -92,48 +95,86 @@ namespace boost { namespace network { namespace http {
9295
*oi = consts::dot_char();
9396
boost::copy(version_minor_str, oi);
9497
boost::copy(crlf, oi);
95-
boost::copy(host, oi);
96-
*oi = consts::colon_char();
97-
*oi = consts::space_char();
98-
boost::copy(request.host(), oi);
99-
boost::optional<boost::uint16_t> port_ = port(request);
100-
if (port_) {
101-
string_type port_str = boost::lexical_cast<string_type>(*port_);
102-
*oi = consts::colon_char();
103-
boost::copy(port_str, oi);
104-
}
105-
boost::copy(crlf, oi);
106-
boost::copy(accept, oi);
107-
*oi = consts::colon_char();
108-
*oi = consts::space_char();
109-
boost::copy(accept_mime, oi);
110-
boost::copy(crlf, oi);
111-
if (version_major == 1u && version_minor == 1u) {
112-
boost::copy(accept_encoding, oi);
113-
*oi = consts::colon_char();
114-
*oi = consts::space_char();
115-
boost::copy(default_accept_encoding, oi);
116-
boost::copy(crlf, oi);
117-
}
98+
99+
// We need to determine whether we've seen any of the following headers
100+
// before setting the defaults. We use a bitset to keep track of the
101+
// defaulted headers.
102+
enum { ACCEPT, ACCEPT_ENCODING, HOST, MAX };
103+
std::bitset<MAX> found_headers;
104+
static char const* defaulted_headers[][2] = {
105+
{consts::accept(),
106+
consts::accept() + std::strlen(consts::accept())},
107+
{consts::accept_encoding(),
108+
consts::accept_encoding() + std::strlen(consts::accept_encoding())},
109+
{consts::host(), consts::host() + std::strlen(consts::host())}
110+
};
111+
118112
typedef typename headers_range<Request>::type headers_range;
119113
typedef typename range_value<headers_range>::type headers_value;
120-
BOOST_FOREACH(const headers_value &header, headers(request))
121-
{
122-
string_type header_name = name(header),
123-
header_value = value(header);
124-
boost::copy(header_name, oi);
125-
*oi = consts::colon_char();
126-
*oi = consts::space_char();
127-
boost::copy(header_value, oi);
128-
boost::copy(crlf, oi);
114+
BOOST_FOREACH(const headers_value & header, headers(request)) {
115+
string_type header_name = name(header), header_value = value(header);
116+
// Here we check that we have not seen an override to the defaulted
117+
// headers.
118+
for (int header_index = 0; header_index < MAX; ++header_index)
119+
if (std::distance(header_name.begin(), header_name.end()) ==
120+
std::distance(defaulted_headers[header_index][0],
121+
defaulted_headers[header_index][1]) &&
122+
std::equal(header_name.begin(),
123+
header_name.end(),
124+
defaulted_headers[header_index][0],
125+
algorithm::is_iequal()))
126+
found_headers.set(header_index, true);
127+
128+
// We ignore empty headers.
129+
if (header_value.empty()) continue;
130+
boost::copy(header_name, oi);
131+
*oi = consts::colon_char();
132+
*oi = consts::space_char();
133+
boost::copy(header_value, oi);
134+
boost::copy(crlf, oi);
135+
136+
}
137+
138+
if (!found_headers[HOST]) {
139+
boost::copy(host, oi);
140+
*oi = consts::colon_char();
141+
*oi = consts::space_char();
142+
boost::copy(request.host(), oi);
143+
boost::optional<boost::uint16_t> port_ = port(request);
144+
if (port_) {
145+
string_type port_str = boost::lexical_cast<string_type>(*port_);
146+
*oi = consts::colon_char();
147+
boost::copy(port_str, oi);
148+
}
149+
boost::copy(crlf, oi);
150+
}
151+
152+
if (!found_headers[ACCEPT]) {
153+
boost::copy(accept, oi);
154+
*oi = consts::colon_char();
155+
*oi = consts::space_char();
156+
boost::copy(accept_mime, oi);
157+
boost::copy(crlf, oi);
158+
}
159+
160+
if (version_major == 1u &&
161+
version_minor == 1u &&
162+
!found_headers[ACCEPT_ENCODING]) {
163+
boost::copy(accept_encoding, oi);
164+
*oi = consts::colon_char();
165+
*oi = consts::space_char();
166+
boost::copy(default_accept_encoding, oi);
167+
boost::copy(crlf, oi);
129168
}
169+
130170
if (!connection_keepalive<Tag>::value) {
131-
boost::copy(connection, oi);
132-
*oi = consts::colon_char();
133-
*oi = consts::space_char();
171+
boost::copy(connection, oi);
172+
*oi = consts::colon_char();
173+
*oi = consts::space_char();
134174
boost::copy(close, oi);
135175
boost::copy(crlf, oi);
136176
}
177+
137178
boost::copy(crlf, oi);
138179
typename body_range<Request>::type body_data = body(request).range();
139180
return boost::copy(body_data, oi);

libs/network/test/http/CMakeLists.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,29 @@ if (OPENSSL_FOUND)
1212
endif()
1313

1414
if (Boost_FOUND)
15+
set ( TESTS
16+
request_incremental_parser_test
17+
request_linearize_test
18+
)
19+
foreach ( test ${TESTS} )
20+
if (${CMAKE_CXX_COMPILER_ID} MATCHES GNU)
21+
set_source_files_properties(${test}.cpp
22+
PROPERTIES COMPILE_FLAGS "-Wall")
23+
endif()
24+
add_executable(cpp-netlib-http-${test} ${test}.cpp)
25+
add_dependencies(cpp-netlib-http-${test}
26+
cppnetlib-uri)
27+
target_link_libraries(cpp-netlib-http-${test}
28+
${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
29+
cppnetlib-uri)
30+
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
31+
target_link_libraries(cpp-netlib-http-${test} rt)
32+
endif()
33+
set_target_properties(cpp-netlib-http-${test}
34+
PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CPP-NETLIB_BINARY_DIR}/tests)
35+
add_test(cpp-netlib-http-${test}
36+
${CPP-NETLIB_BINARY_DIR}/tests/cpp-netlib-http-${test})
37+
endforeach (test)
1538
set ( TESTS
1639
client_constructor_test
1740
client_get_test

libs/network/test/http/request_linearize_test.cpp

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <boost/test/unit_test.hpp>
1111
#include <boost/mpl/list.hpp>
1212
#include <iostream>
13+
#include <iterator>
1314

1415
namespace http = boost::network::http;
1516
namespace tags = boost::network::http::tags;
@@ -24,8 +25,50 @@ typedef mpl::list<
2425
> tag_types;
2526

2627
BOOST_AUTO_TEST_CASE_TEMPLATE(linearize_request, T, tag_types) {
27-
http::basic_request<T> request("http://www.boost.org");
28-
linearize(request, "GET", 1, 0, std::ostream_iterator<typename net::char_<T>::type>(std::cout));
29-
linearize(request, "GET", 1, 1, std::ostream_iterator<typename net::char_<T>::type>(std::cout));
28+
http::basic_request<T> request("http://www.boost.org");
29+
static char http_1_0_output[] =
30+
"GET / HTTP/1.0\r\n"
31+
"Host: www.boost.org\r\n"
32+
"Accept: */*\r\n"
33+
"Connection: Close\r\n"
34+
"\r\n";
35+
static char http_1_1_output[] =
36+
"GET / HTTP/1.1\r\n"
37+
"Host: www.boost.org\r\n"
38+
"Accept: */*\r\n"
39+
"Accept-Encoding: identity;q=1.0, *;q=0\r\n"
40+
"Connection: Close\r\n"
41+
"\r\n";
42+
typename http::basic_request<T>::string_type output_1_0;
43+
linearize(request, "GET", 1, 0, std::back_inserter(output_1_0));
44+
BOOST_CHECK_EQUAL(output_1_0, http_1_0_output);
45+
typename http::basic_request<T>::string_type output_1_1;
46+
linearize(request, "GET", 1, 1, std::back_inserter(output_1_1));
47+
BOOST_CHECK_EQUAL(output_1_1, http_1_1_output);
48+
}
49+
50+
BOOST_AUTO_TEST_CASE_TEMPLATE(linearize_request_override_headers,
51+
T,
52+
tag_types) {
53+
http::basic_request<T> request("http://www.boost.org");
54+
// We can override the defaulted headers and test that here.
55+
request << net::header("Accept", "");
56+
static char http_1_0_no_accept_output[] =
57+
"GET / HTTP/1.0\r\n"
58+
"Host: www.boost.org\r\n"
59+
"Connection: Close\r\n"
60+
"\r\n";
61+
static char http_1_1_no_accept_output[] =
62+
"GET / HTTP/1.1\r\n"
63+
"Host: www.boost.org\r\n"
64+
"Accept-Encoding: identity;q=1.0, *;q=0\r\n"
65+
"Connection: Close\r\n"
66+
"\r\n";
67+
typename http::basic_request<T>::string_type output_1_0;
68+
linearize(request, "GET", 1, 0, std::back_inserter(output_1_0));
69+
BOOST_CHECK_EQUAL(output_1_0, http_1_0_no_accept_output);
70+
typename http::basic_request<T>::string_type output_1_1;
71+
linearize(request, "GET", 1, 1, std::back_inserter(output_1_1));
72+
BOOST_CHECK_EQUAL(output_1_1, http_1_1_no_accept_output);
3073
}
3174

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy