Skip to content

Commit 6540a84

Browse files
committed
Added async_resolver.
1 parent b700bcc commit 6540a84

File tree

7 files changed

+238
-36
lines changed

7 files changed

+238
-36
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Copyright (C) 2013 by Glyn Matthews
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// (See accompanying file LICENSE_1_0.txt or copy at
4+
// http://www.boost.org/LICENSE_1_0.txt)
5+
6+
#ifndef __NETWORK_HTTP_V2_CLIENT_CONNECTION_ASYNC_RESOLVER_INC__
7+
#define __NETWORK_HTTP_V2_CLIENT_CONNECTION_ASYNC_RESOLVER_INC__
8+
9+
#include <boost/asio/io_service.hpp>
10+
#include <boost/asio/strand.hpp>
11+
#include <boost/asio/ip/tcp.hpp>
12+
#include <boost/range/iterator_range.hpp>
13+
#include <boost/algorithm/string/case_conv.hpp>
14+
#include <boost/exception/all.hpp>
15+
#include <stdexcept>
16+
#include <cstdint>
17+
#include <string>
18+
#include <unordered_map>
19+
20+
namespace network {
21+
namespace http {
22+
namespace v2 {
23+
24+
class resolver_error : std::runtime_error {
25+
26+
public:
27+
28+
resolver_error(const std::string &msg)
29+
: std::runtime_error(msg) {
30+
31+
}
32+
33+
virtual ~resolver_error() noexcept {
34+
35+
}
36+
37+
};
38+
39+
class async_resolver {
40+
41+
async_resolver(const async_resolver &) = delete;
42+
async_resolver &operator = (const async_resolver &) = delete;
43+
44+
public:
45+
46+
typedef boost::asio::ip::tcp::resolver resolver;
47+
typedef resolver::iterator resolver_iterator;
48+
49+
/**
50+
* \brief Constructor.
51+
*/
52+
async_resolver(boost::asio::io_service &service, bool cache_resolved)
53+
: resolver_(service)
54+
, cache_resolved_(cache_resolved_)
55+
, resolver_strand_(new boost::asio::io_service::strand(service)) {
56+
57+
}
58+
59+
/**
60+
* \brief Destructor.
61+
*/
62+
~async_resolver() noexcept {
63+
64+
}
65+
66+
/**
67+
* \brief Resolves a host asynchronously.
68+
*/
69+
template <class OnResolved>
70+
void resolve(const std::string &host, std::uint16_t port, OnResolved on_resolved) {
71+
if (cache_resolved_) {
72+
endpoint_cache::iterator it = endpoint_cache_.find(boost::to_lower_copy(host));
73+
if (it != endpoint_cache_.end()) {
74+
boost::system::error_code ignored;
75+
on_resolved(ignored, it->second);
76+
return;
77+
}
78+
}
79+
80+
resolver::query query(host, std::to_string(port));
81+
resolver_.async_resolve(query,
82+
resolver_strand_->wrap(
83+
[=](const boost::system::error_code &ec,
84+
resolver_iterator endpoint_iterator) {
85+
if (!ec && cache_resolved_) {
86+
endpoint_cache::iterator cache_it;
87+
bool inserted = false;
88+
std::tie(cache_it, inserted) = endpoint_cache_.insert(
89+
std::make_pair(host,
90+
std::make_pair(endpoint_iterator, resolver_iterator())));
91+
on_resolved(ec, cache_it->second);
92+
}
93+
else {
94+
on_resolved(ec, std::make_pair(endpoint_iterator, resolver_iterator()));
95+
}
96+
}));
97+
}
98+
99+
void clear_resolved_cache() {
100+
endpoint_cache_.clear();
101+
}
102+
103+
private:
104+
105+
typedef boost::asio::io_service::strand strand;
106+
typedef std::unordered_map<
107+
std::string, boost::iterator_range<resolver_iterator>> endpoint_cache;
108+
109+
resolver resolver_;
110+
std::unique_ptr<strand> resolver_strand_;
111+
bool cache_resolved_;
112+
endpoint_cache endpoint_cache_;
113+
114+
};
115+
116+
} // namespace v2
117+
} // namespace http
118+
} // namespace network
119+
120+
121+
#endif // __NETWORK_HTTP_V2_CLIENT_CONNECTION_ASYNC_RESOLVER_INC__

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

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
#ifndef __NETWORK_HTTP_V2_CLIENT_REQUEST_INC__
77
#define __NETWORK_HTTP_V2_CLIENT_REQUEST_INC__
88

9-
#include <cstdint>
109
#include <memory>
1110
#include <string>
11+
#include <vector>
12+
#include <utility>
1213
#include <network/http/v2/constants.hpp>
1314
#include <network/http/v2/message_base.hpp>
1415
#include <network/http/v2/client/client_errors.hpp>
@@ -69,7 +70,7 @@ namespace network {
6970
public:
7071

7172
typedef byte_source::string_type string_type;
72-
typedef std::vector<std::pair<std::string, std::string>> headers_type;
73+
typedef std::vector<std::pair<string_type, string_type>> headers_type;
7374
typedef headers_type::iterator headers_iterator;
7475
typedef headers_type::const_iterator const_headers_iterator;
7576

@@ -91,17 +92,17 @@ namespace network {
9192

9293
request(const request &other)
9394
: destination_(other.destination_)
94-
, byte_source_(other.byte_source_)
95-
, headers_(other.headers_)
9695
, method_(other.method_)
97-
, version_(other.version_) { }
96+
, version_(other.version_)
97+
, headers_(other.headers_)
98+
, byte_source_(other.byte_source_) { }
9899

99100
request(request &&other) noexcept
100101
: destination_(std::move(other.destination_))
101-
, byte_source_(std::move(other.byte_source_))
102-
, headers_(std::move(other.headers_))
103102
, method_(std::move(other.method_))
104-
, version_(std::move(other.version_)) { }
103+
, version_(std::move(other.version_))
104+
, headers_(std::move(other.headers_))
105+
, byte_source_(std::move(other.byte_source_)) { }
105106

106107
request &operator = (request other) {
107108
other.swap(*this);
@@ -110,10 +111,10 @@ namespace network {
110111

111112
void swap(request &other) noexcept {
112113
std::swap(destination_, other.destination_);
113-
std::swap(byte_source_, other.byte_source_);
114-
std::swap(headers_, other.headers_);
115114
std::swap(method_, other.method_);
116115
std::swap(version_, other.version_);
116+
std::swap(headers_, other.headers_);
117+
std::swap(byte_source_, other.byte_source_);
117118
}
118119

119120
void set_destination(uri destination) {
@@ -169,25 +170,25 @@ namespace network {
169170
private:
170171

171172
uri destination_;
172-
std::shared_ptr<byte_source> byte_source_;
173-
std::vector<std::pair<string_type, string_type>> headers_;
174173
string_type method_, version_;
174+
headers_type headers_;
175+
std::shared_ptr<byte_source> byte_source_;
175176

176177
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())});
178+
request::string_type path{std::begin(*req.destination_.path()), std::end(*req.destination_.path())};
179+
request::string_type host;
180+
host.append(request::string_type{std::begin(*req.destination_.scheme()),
181+
std::end(*req.destination_.scheme())});
181182
host.append("://");
182-
host.append(std::string{std::begin(*req.destination_.authority()),
183-
std::end(*req.destination_.authority())});
183+
host.append(request::string_type{std::begin(*req.destination_.authority()),
184+
std::end(*req.destination_.authority())});
184185

185186
os << req.method_ << " " << path << " HTTP/" << req.version_ << "\r\n";
186187
os << "Host: " << host << "\r\n";
187188
for (auto header : req.headers_) {
188189
os << header.first << ": " << header.second << "\r\n";
189190
}
190-
return os;
191+
return os << "\r\n";
191192
}
192193
};
193194
} // namespace v2

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

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
33
// (See accompanying file LICENSE_1_0.txt or copy at
44
// http://www.boost.org/LICENSE_1_0.txt)
55

6-
#ifndef __NETWORK_HTTP_V2_RESPONSE_INC__
7-
#define __NETWORK_HTTP_V2_RESPONSE_INC__
6+
#ifndef __NETWORK_HTTP_V2_CLIENT_RESPONSE_INC__
7+
#define __NETWORK_HTTP_V2_CLIENT_RESPONSE_INC__
88

99
#include <cstdint>
10-
#include <map>
10+
#include <vector>
11+
#include <utility>
1112
#include <string>
1213
#include <future>
14+
#include <network/http/v2/constants.hpp>
15+
#include <boost/range/iterator_range.hpp>
1316
#include <network/uri.hpp>
1417

1518
namespace network {
@@ -19,18 +22,54 @@ namespace network {
1922

2023
public:
2124

22-
typedef std::multimap<std::string, std::string> headers_type;
23-
typedef headers_type::const_iterator headers_iterator;
25+
typedef std::string string_type;
26+
typedef std::vector<std::pair<string_type, string_type>> headers_type;
27+
typedef headers_type::iterator headers_iterator;
2428
typedef headers_type::const_iterator const_headers_iterator;
2529

2630
response() { }
2731

28-
std::uint16_t status() const;
29-
std::string status_message() const;
30-
const_headers_iterator headers_begin() const;
31-
const_headers_iterator end_begin() const;
32-
std::pair<headers_iterator, headers_iterator> headers() const;
33-
std::future<std::string> read_body(std::size_t length) const;
32+
response(const response &other)
33+
: status_(other.status_)
34+
, version_(other.version_)
35+
, status_message_(other.status_message_)
36+
, headers_(other.headers_) {
37+
38+
}
39+
40+
response(response &&other) noexcept
41+
: status_(std::move(other.status_))
42+
, version_(std::move(other.version_))
43+
, status_message_(std::move(other.status_message_))
44+
, headers_(std::move(other.headers_)) {
45+
46+
}
47+
48+
response &operator= (response other) {
49+
other.swap(*this);
50+
return *this;
51+
}
52+
53+
void swap(response &other) noexcept {
54+
std::swap(status_, other.status_);
55+
std::swap(version_, other.version_);
56+
std::swap(status_message_, other.status_message_);
57+
std::swap(headers_, other.headers_);
58+
}
59+
60+
std::uint16_t status() const {
61+
return status_;
62+
}
63+
64+
string_type status_message() const {
65+
return status_message_;
66+
}
67+
68+
boost::iterator_range<const_headers_iterator> headers() const {
69+
return boost::make_iterator_range(std::begin(headers_), std::end(headers_));
70+
}
71+
72+
std::future<string_type> read_body(std::size_t length) const;
3473

3574
// destination
3675
// source
@@ -40,10 +79,19 @@ namespace network {
4079
// append_body
4180
// get_body
4281

82+
string_type version() const {
83+
return version_;
84+
}
85+
86+
private:
87+
88+
std::uint16_t status_;
89+
string_type version_, status_message_;
90+
headers_type headers_;
4391

4492
};
4593
} // namespace v2
4694
} // namespace http
4795
} // namespace network
4896

49-
#endif // __NETWORK_HTTP_V2_RESPONSE_INC__
97+
#endif // __NETWORK_HTTP_V2_CLIENT_RESPONSE_INC__

http/test/v2/client/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
# http://www.boost.org/LICENSE_1_0.txt)
55

66
set(CPP-NETLIB_CLIENT_TESTS
7-
response_test
87
client_options_test
98
request_options_test
109
byte_source_test
10+
response_test
1111
request_test
12+
async_resolver_test
1213
client_test
1314
)
1415

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (C) 2013 by Glyn Matthews
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// (See accompanying file LICENSE_1_0.txt or copy at
4+
// http://www.boost.org/LICENSE_1_0.txt)
5+
6+
#include <gtest/gtest.h>
7+
#include <boost/asio.hpp>
8+
#include <network/http/v2/client/connection/async_resolver.hpp>
9+
#include <iostream>
10+
11+
namespace http = network::http::v2;
12+
13+
TEST(async_resolver_test, resolve_localhost) {
14+
// server must be running on 127.0.0.1:80
15+
16+
boost::asio::io_service io_service;
17+
http::async_resolver resolver(io_service, false);
18+
resolver.resolve("127.0.0.1", 80,
19+
[] (const boost::system::error_code &ec,
20+
const boost::iterator_range<http::async_resolver::resolver_iterator> &endpoints) {
21+
for (auto endpoint : endpoints) {
22+
std::cout << "Endpoint" << std::endl;
23+
}
24+
});
25+
26+
io_service.run();
27+
}

http/test/v2/client/request_test.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ TEST(request_test, stream_2) {
6262
"User-Agent: request_test\r\n", oss.str());
6363
}
6464

65-
TEST(request, read_headers) {
65+
TEST(request_test, read_headers) {
6666
http::request instance{network::uri{"http://www.example.com/path/to/resource/index.html"}};
6767
instance.set_version("1.1");
6868
instance.set_method("GET");
@@ -78,7 +78,7 @@ TEST(request, read_headers) {
7878
ASSERT_EQ("request_test", headers_it->second);
7979
}
8080

81-
TEST(request, clear_headers) {
81+
TEST(request_test, clear_headers) {
8282
http::request instance{network::uri{"http://www.example.com/path/to/resource/index.html"}};
8383
instance.set_version("1.1");
8484
instance.set_method("GET");
@@ -89,7 +89,7 @@ TEST(request, clear_headers) {
8989
ASSERT_TRUE(std::begin(headers) == std::end(headers));
9090
}
9191

92-
TEST(request, remove_headers) {
92+
TEST(request_test, remove_headers) {
9393
http::request instance{network::uri{"http://www.example.com/path/to/resource/index.html"}};
9494
instance.set_version("1.1");
9595
instance.set_method("GET");
@@ -106,7 +106,7 @@ TEST(request, remove_headers) {
106106
ASSERT_TRUE(headers_it == std::end(headers));
107107
}
108108

109-
TEST(request, remove_duplicate_headers) {
109+
TEST(request_test, remove_duplicate_headers) {
110110
http::request instance{network::uri{"http://www.example.com/path/to/resource/index.html"}};
111111
instance.set_version("1.1");
112112
instance.set_method("GET");

0 commit comments

Comments
 (0)