Skip to content

Commit b700bcc

Browse files
committed
Added tests and methods to the HTTP request.
1 parent 41f4ec2 commit b700bcc

File tree

3 files changed

+169
-15
lines changed

3 files changed

+169
-15
lines changed

http/src/network/http/v2/client/request.hpp

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,42 @@
1212
#include <network/http/v2/constants.hpp>
1313
#include <network/http/v2/message_base.hpp>
1414
#include <network/http/v2/client/client_errors.hpp>
15+
#include <boost/range/iterator_range.hpp>
1516
#include <boost/range/algorithm/equal.hpp>
1617
#include <boost/range/as_literal.hpp>
1718
#include <network/uri.hpp>
1819

1920
namespace network {
2021
namespace http {
2122
namespace v2 {
23+
/**
24+
* \class byte_source network::http::v2::byte_source <network/http/request.hpp>
25+
* \brief An abstract class that allows a request object to read
26+
* data from any source.
27+
*/
2228
class byte_source {
2329

2430
public:
2531

2632
typedef message_base::string_type string_type;
2733
typedef message_base::size_type size_type;
2834

29-
virtual ~byte_source() {}
35+
/**
36+
* \brief Destructor.
37+
*/
38+
virtual ~byte_source() noexcept {}
3039

40+
/**
41+
* \brief Allows the request to read the data into a local
42+
* copy of it's source string.
43+
*/
3144
virtual size_type read(string_type &source, size_type length) = 0;
3245

3346
};
3447

48+
/**
49+
* \class string_byte_source network::http::v2::string_byte_source <network/http/request.hpp>
50+
*/
3551
class string_byte_source : public byte_source {
3652

3753
public:
@@ -53,6 +69,9 @@ namespace network {
5369
public:
5470

5571
typedef byte_source::string_type string_type;
72+
typedef std::vector<std::pair<std::string, std::string>> headers_type;
73+
typedef headers_type::iterator headers_iterator;
74+
typedef headers_type::const_iterator const_headers_iterator;
5675

5776
request()
5877
: byte_source_(nullptr) { }
@@ -105,12 +124,26 @@ namespace network {
105124
byte_source_ = byte_source;
106125
}
107126

108-
void add_header(string_type key, string_type value) {
109-
headers_.emplace(key, value);
127+
void append_header(string_type key, string_type value) {
128+
headers_.emplace_back(std::make_pair(key, value));
129+
}
130+
131+
boost::iterator_range<const_headers_iterator> headers() const {
132+
return boost::make_iterator_range(std::begin(headers_), std::end(headers_));
110133
}
111134

112135
void remove_header(string_type key) {
113-
headers_.erase(key);
136+
bool found_all = false;
137+
while (!found_all) {
138+
auto it = std::find_if(std::begin(headers_), std::end(headers_),
139+
[&key] (const std::pair<string_type, string_type> &header) {
140+
return header.first == key;
141+
});
142+
found_all = (it == std::end(headers_));
143+
if (!found_all) {
144+
headers_.erase(it);
145+
}
146+
}
114147
}
115148

116149
void clear_headers() {
@@ -133,14 +166,30 @@ namespace network {
133166
return version_;
134167
}
135168

169+
private:
170+
136171
uri destination_;
137172
std::shared_ptr<byte_source> byte_source_;
138-
std::multimap<string_type, string_type> headers_;
173+
std::vector<std::pair<string_type, string_type>> headers_;
139174
string_type method_, version_;
140175

176+
friend std::ostream &operator << (std::ostream &os, const request &req) {
177+
std::string path{std::begin(*req.destination_.path()), std::end(*req.destination_.path())};
178+
std::string host;
179+
host.append(std::string{std::begin(*req.destination_.scheme()),
180+
std::end(*req.destination_.scheme())});
181+
host.append("://");
182+
host.append(std::string{std::begin(*req.destination_.authority()),
183+
std::end(*req.destination_.authority())});
184+
185+
os << req.method_ << " " << path << " HTTP/" << req.version_ << "\r\n";
186+
os << "Host: " << host << "\r\n";
187+
for (auto header : req.headers_) {
188+
os << header.first << ": " << header.second << "\r\n";
189+
}
190+
return os;
191+
}
141192
};
142-
143-
std::ostream &operator << (std::ostream &os, const request &req);
144193
} // namespace v2
145194
} // namespace http
146195
} // namespace network

http/test/v2/client/request_test.cpp

Lines changed: 112 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,120 @@
66
#include <gtest/gtest.h>
77
#include <network/http/v2/client/request.hpp>
88

9-
TEST(request_test, request_constructor_url) {
10-
ASSERT_NO_THROW(network::http::v2::request(network::uri("http://www.example.com/")));
9+
namespace http = network::http::v2;
10+
11+
12+
TEST(request_test, constructor_url) {
13+
ASSERT_NO_THROW(http::request{network::uri{"http://www.example.com/"}});
14+
}
15+
16+
TEST(request_test, constructor_https_url) {
17+
ASSERT_NO_THROW(http::request{network::uri{"https://www.example.com/"}});
18+
}
19+
20+
TEST(request_test, constructor_invalid_url) {
21+
ASSERT_THROW(http::request{network::uri{"mailto:john.doe@example.com"}},
22+
http::invalid_scheme);
23+
}
24+
25+
TEST(request_test, constructor_empty_uri) {
26+
ASSERT_THROW(http::request{network::uri{}},
27+
http::invalid_scheme);
28+
}
29+
30+
TEST(request_test, constructor_uri_no_scheme) {
31+
network::uri_builder builder;
32+
builder.host("www.example.com").path("/");
33+
ASSERT_THROW(http::request{builder.uri()},
34+
http::invalid_scheme);
35+
}
36+
37+
TEST(request_test, stream) {
38+
http::request instance{network::uri{"http://www.example.com/"}};
39+
instance.set_version("1.1");
40+
instance.set_method("GET");
41+
instance.append_header("Connection", "close");
42+
instance.append_header("User-Agent", "request_test");
43+
std::ostringstream oss;
44+
oss << instance;
45+
ASSERT_EQ("GET / HTTP/1.1\r\n"
46+
"Host: http://www.example.com\r\n"
47+
"Connection: close\r\n"
48+
"User-Agent: request_test\r\n", oss.str());
1149
}
1250

13-
TEST(request_test, request_constructor_https_url) {
14-
ASSERT_NO_THROW(network::http::v2::request(network::uri("https://www.example.com/")));
51+
TEST(request_test, stream_2) {
52+
http::request instance{network::uri{"http://www.example.com/path/to/resource/index.html"}};
53+
instance.set_version("1.1");
54+
instance.set_method("GET");
55+
instance.append_header("Connection", "close");
56+
instance.append_header("User-Agent", "request_test");
57+
std::ostringstream oss;
58+
oss << instance;
59+
ASSERT_EQ("GET /path/to/resource/index.html HTTP/1.1\r\n"
60+
"Host: http://www.example.com\r\n"
61+
"Connection: close\r\n"
62+
"User-Agent: request_test\r\n", oss.str());
1563
}
1664

17-
TEST(request_test, request_constructor_invalid_url) {
18-
ASSERT_THROW(network::http::v2::request(network::uri("mailto:john.doe@example.com")),
19-
network::http::v2::invalid_scheme);
65+
TEST(request, read_headers) {
66+
http::request instance{network::uri{"http://www.example.com/path/to/resource/index.html"}};
67+
instance.set_version("1.1");
68+
instance.set_method("GET");
69+
instance.append_header("Connection", "close");
70+
instance.append_header("User-Agent", "request_test");
71+
72+
auto headers = instance.headers();
73+
auto headers_it = std::begin(headers);
74+
ASSERT_EQ("Connection", headers_it->first);
75+
ASSERT_EQ("close", headers_it->second);
76+
++headers_it;
77+
ASSERT_EQ("User-Agent", headers_it->first);
78+
ASSERT_EQ("request_test", headers_it->second);
79+
}
80+
81+
TEST(request, clear_headers) {
82+
http::request instance{network::uri{"http://www.example.com/path/to/resource/index.html"}};
83+
instance.set_version("1.1");
84+
instance.set_method("GET");
85+
instance.append_header("Connection", "close");
86+
instance.append_header("User-Agent", "request_test");
87+
instance.clear_headers();
88+
auto headers = instance.headers();
89+
ASSERT_TRUE(std::begin(headers) == std::end(headers));
90+
}
91+
92+
TEST(request, remove_headers) {
93+
http::request instance{network::uri{"http://www.example.com/path/to/resource/index.html"}};
94+
instance.set_version("1.1");
95+
instance.set_method("GET");
96+
instance.append_header("Connection", "close");
97+
instance.append_header("User-Agent", "request_test");
98+
instance.remove_header("User-Agent");
99+
100+
auto headers = instance.headers();
101+
auto headers_it = std::begin(headers);
102+
103+
ASSERT_EQ("Connection", headers_it->first);
104+
ASSERT_EQ("close", headers_it->second);
105+
++headers_it;
106+
ASSERT_TRUE(headers_it == std::end(headers));
107+
}
108+
109+
TEST(request, remove_duplicate_headers) {
110+
http::request instance{network::uri{"http://www.example.com/path/to/resource/index.html"}};
111+
instance.set_version("1.1");
112+
instance.set_method("GET");
113+
instance.append_header("Connection", "close");
114+
instance.append_header("User-Agent", "request_test");
115+
instance.append_header("User-Agent", "request_test_2");
116+
instance.remove_header("User-Agent");
117+
118+
auto headers = instance.headers();
119+
auto headers_it = std::begin(headers);
120+
121+
ASSERT_EQ("Connection", headers_it->first);
122+
ASSERT_EQ("close", headers_it->second);
123+
++headers_it;
124+
ASSERT_TRUE(headers_it == std::end(headers));
20125
}

0 commit comments

Comments
 (0)