Merge lp:~thomas-voss/net-cpp/add-streaming into lp:net-cpp
- add-streaming
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Marcus Tomlinson | ||||
Approved revision: | 51 | ||||
Merged at revision: | 48 | ||||
Proposed branch: | lp:~thomas-voss/net-cpp/add-streaming | ||||
Merge into: | lp:net-cpp | ||||
Diff against target: |
1051 lines (+802/-28) 11 files modified
CMakeLists.txt (+3/-1) debian/changelog (+6/-0) debian/libnet-cpp1.symbols (+4/-0) include/core/net/http/streaming_client.h (+88/-0) include/core/net/http/streaming_request.h (+60/-0) src/CMakeLists.txt (+2/-0) src/core/net/http/impl/curl/client.cpp (+74/-8) src/core/net/http/impl/curl/client.h (+24/-16) src/core/net/http/impl/curl/request.h (+17/-3) tests/CMakeLists.txt (+16/-0) tests/http_streaming_client_test.cpp (+508/-0) |
||||
To merge this branch: | bzr merge lp:~thomas-voss/net-cpp/add-streaming | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Marcus Tomlinson (community) | Approve | ||
PS Jenkins bot | continuous-integration | Approve | |
Ubuntu Phablet Team | Pending | ||
Review via email: mp+253850@code.launchpad.net |
Commit message
Add streaming support to net-cpp in an ABI-stable way.
Description of the change
Add streaming support to net-cpp in an ABI-stable way.
PS Jenkins bot (ps-jenkins) wrote : | # |
Marcus Tomlinson (marcustomlinson) wrote : | # |
361 +class Client : public core::net:
Just noticing a few overriding methods in this class that don't specify "override" in their declarations. i.e. url_escape, timings, run, stop, get, head, post, and pull.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:50
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Marcus Tomlinson (marcustomlinson) wrote : | # |
Client::url_escape should also be marked override, but no biggy. Looks good.
Preview Diff
1 | === modified file 'CMakeLists.txt' | |||
2 | --- CMakeLists.txt 2014-07-30 15:16:19 +0000 | |||
3 | +++ CMakeLists.txt 2015-04-01 06:23:51 +0000 | |||
4 | @@ -27,7 +27,7 @@ | |||
5 | 27 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined") | 27 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined") |
6 | 28 | 28 | ||
7 | 29 | set(NET_CPP_VERSION_MAJOR 1) | 29 | set(NET_CPP_VERSION_MAJOR 1) |
9 | 30 | set(NET_CPP_VERSION_MINOR 1) | 30 | set(NET_CPP_VERSION_MINOR 2) |
10 | 31 | set(NET_CPP_VERSION_PATCH 0) | 31 | set(NET_CPP_VERSION_PATCH 0) |
11 | 32 | 32 | ||
12 | 33 | include(CTest) | 33 | include(CTest) |
13 | @@ -36,6 +36,8 @@ | |||
14 | 36 | include/ | 36 | include/ |
15 | 37 | ) | 37 | ) |
16 | 38 | 38 | ||
17 | 39 | file(GLOB_RECURSE NET_CPP_INTERFACE_HEADERS include/*.h) | ||
18 | 40 | |||
19 | 39 | add_subdirectory(doc) | 41 | add_subdirectory(doc) |
20 | 40 | add_subdirectory(data) | 42 | add_subdirectory(data) |
21 | 41 | add_subdirectory(include) | 43 | add_subdirectory(include) |
22 | 42 | 44 | ||
23 | === modified file 'debian/changelog' | |||
24 | --- debian/changelog 2015-03-05 12:08:09 +0000 | |||
25 | +++ debian/changelog 2015-04-01 06:23:51 +0000 | |||
26 | @@ -1,3 +1,9 @@ | |||
27 | 1 | net-cpp (1.2.0) UNRELEASED; urgency=medium | ||
28 | 2 | |||
29 | 3 | * Introduce a streaming http interface. | ||
30 | 4 | |||
31 | 5 | -- Thomas Voß <thomas.voss@canonical.com> Wed, 01 Apr 2015 08:22:32 +0200 | ||
32 | 6 | |||
33 | 1 | net-cpp (1.1.0+15.04.20150305-0ubuntu1) vivid; urgency=medium | 7 | net-cpp (1.1.0+15.04.20150305-0ubuntu1) vivid; urgency=medium |
34 | 2 | 8 | ||
35 | 3 | [ thomas-voss ] | 9 | [ thomas-voss ] |
36 | 4 | 10 | ||
37 | === modified file 'debian/libnet-cpp1.symbols' | |||
38 | --- debian/libnet-cpp1.symbols 2014-12-04 20:54:26 +0000 | |||
39 | +++ debian/libnet-cpp1.symbols 2015-04-01 06:23:51 +0000 | |||
40 | @@ -1,5 +1,6 @@ | |||
41 | 1 | libnet-cpp.so.1 libnet-cpp1 #MINVER# | 1 | libnet-cpp.so.1 libnet-cpp1 #MINVER# |
42 | 2 | (c++)"core::net::http::make_client()@Base" 0.0.1+14.10.20140611 | 2 | (c++)"core::net::http::make_client()@Base" 0.0.1+14.10.20140611 |
43 | 3 | (c++)"core::net::http::make_streaming_client()@Base" 1.1.0+15.04.20150305-0ubuntu1 | ||
44 | 3 | (c++)"core::net::make_uri(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, std::vector<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > > const&)@Base" 1.1.0+14.10.20140804 | 4 | (c++)"core::net::make_uri(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, std::vector<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > > const&)@Base" 1.1.0+14.10.20140804 |
45 | 4 | (c++)"core::net::http::Client::uri_to_string(core::net::Uri const&) const@Base" 1.1.0+14.10.20140804 | 5 | (c++)"core::net::http::Client::uri_to_string(core::net::Uri const&) const@Base" 1.1.0+14.10.20140804 |
46 | 5 | (c++)"core::net::http::Client::Errors::HttpMethodNotSupported::HttpMethodNotSupported(core::net::http::Method, core::Location const&)@Base" 0.0.1+14.10.20140611 | 6 | (c++)"core::net::http::Client::Errors::HttpMethodNotSupported::HttpMethodNotSupported(core::net::http::Method, core::Location const&)@Base" 0.0.1+14.10.20140611 |
47 | @@ -41,3 +42,6 @@ | |||
48 | 41 | (c++)"vtable for core::net::http::Header@Base" 0.0.1+14.10.20140611 | 42 | (c++)"vtable for core::net::http::Header@Base" 0.0.1+14.10.20140611 |
49 | 42 | (c++)"vtable for core::net::http::Request::Errors::AlreadyActive@Base" 0.0.1+14.10.20140611 | 43 | (c++)"vtable for core::net::http::Request::Errors::AlreadyActive@Base" 0.0.1+14.10.20140611 |
50 | 43 | (c++)"vtable for core::net::http::Request@Base" 0.0.1+14.10.20140611 | 44 | (c++)"vtable for core::net::http::Request@Base" 0.0.1+14.10.20140611 |
51 | 45 | (c++)"typeinfo for core::net::http::StreamingRequest@Base" 1.1.0+15.04.20150305-0ubuntu1 | ||
52 | 46 | (c++)"typeinfo name for core::net::http::StreamingRequest@Base" 1.1.0+15.04.20150305-0ubuntu1 | ||
53 | 47 | (c++)"vtable for core::net::http::StreamingRequest@Base" 1.1.0+15.04.20150305-0ubuntu1 | ||
54 | 44 | \ No newline at end of file | 48 | \ No newline at end of file |
55 | 45 | 49 | ||
56 | === added file 'include/core/net/http/streaming_client.h' | |||
57 | --- include/core/net/http/streaming_client.h 1970-01-01 00:00:00 +0000 | |||
58 | +++ include/core/net/http/streaming_client.h 2015-04-01 06:23:51 +0000 | |||
59 | @@ -0,0 +1,88 @@ | |||
60 | 1 | /* | ||
61 | 2 | * Copyright © 2013 Canonical Ltd. | ||
62 | 3 | * | ||
63 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
64 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
65 | 6 | * as published by the Free Software Foundation. | ||
66 | 7 | * | ||
67 | 8 | * This program is distributed in the hope that it will be useful, | ||
68 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
69 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
70 | 11 | * GNU Lesser General Public License for more details. | ||
71 | 12 | * | ||
72 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
73 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
74 | 15 | * | ||
75 | 16 | * Authored by: Thomas Voß <thomas.voss@canonical.com> | ||
76 | 17 | */ | ||
77 | 18 | #ifndef CORE_NET_HTTP_STREAMING_CLIENT_H_ | ||
78 | 19 | #define CORE_NET_HTTP_STREAMING_CLIENT_H_ | ||
79 | 20 | |||
80 | 21 | #include <core/net/http/client.h> | ||
81 | 22 | |||
82 | 23 | #include <core/net/http/streaming_request.h> | ||
83 | 24 | |||
84 | 25 | namespace core | ||
85 | 26 | { | ||
86 | 27 | namespace net | ||
87 | 28 | { | ||
88 | 29 | namespace http | ||
89 | 30 | { | ||
90 | 31 | class StreamingClient : public Client | ||
91 | 32 | { | ||
92 | 33 | public: | ||
93 | 34 | |||
94 | 35 | virtual ~StreamingClient() = default; | ||
95 | 36 | |||
96 | 37 | /** | ||
97 | 38 | * @brief streaming_get is a convenience method for issueing a GET request for the given URI. | ||
98 | 39 | * @throw Errors::HttpMethodNotSupported if the underlying implementation does not support the provided HTTP method. | ||
99 | 40 | * @param configuration The configuration to issue a get request for. | ||
100 | 41 | * @return An executable instance of class Request. | ||
101 | 42 | */ | ||
102 | 43 | virtual std::shared_ptr<StreamingRequest> streaming_get(const Request::Configuration& configuration) = 0; | ||
103 | 44 | |||
104 | 45 | /** | ||
105 | 46 | * @brief streaming_head is a convenience method for issueing a HEAD request for the given URI. | ||
106 | 47 | * @throw Errors::HttpMethodNotSupported if the underlying implementation does not support the provided HTTP method. | ||
107 | 48 | * @param configuration The configuration to issue a get request for. | ||
108 | 49 | * @return An executable instance of class Request. | ||
109 | 50 | */ | ||
110 | 51 | virtual std::shared_ptr<StreamingRequest> streaming_head(const Request::Configuration& configuration) = 0; | ||
111 | 52 | |||
112 | 53 | /** | ||
113 | 54 | * @brief streaming_put is a convenience method for issuing a PUT request for the given URI. | ||
114 | 55 | * @throw Errors::HttpMethodNotSupported if the underlying implementation does not support the provided HTTP method. | ||
115 | 56 | * @param configuration The configuration to issue a get request for. | ||
116 | 57 | * @param payload The data to be transmitted as part of the PUT request. | ||
117 | 58 | * @param size Size of the payload data in bytes. | ||
118 | 59 | * @return An executable instance of class Request. | ||
119 | 60 | */ | ||
120 | 61 | virtual std::shared_ptr<StreamingRequest> streaming_put(const Request::Configuration& configuration, std::istream& payload, std::size_t size) = 0; | ||
121 | 62 | |||
122 | 63 | /** | ||
123 | 64 | * @brief streaming_post is a convenience method for issuing a POST request for the given URI. | ||
124 | 65 | * @throw Errors::HttpMethodNotSupported if the underlying implementation does not support the provided HTTP method. | ||
125 | 66 | * @param configuration The configuration to issue a get request for. | ||
126 | 67 | * @param payload The data to be transmitted as part of the POST request. | ||
127 | 68 | * @param type The content-type of the data. | ||
128 | 69 | * @return An executable instance of class Request. | ||
129 | 70 | */ | ||
130 | 71 | virtual std::shared_ptr<StreamingRequest> streaming_post(const Request::Configuration& configuration, const std::string& payload, const std::string& type) = 0; | ||
131 | 72 | |||
132 | 73 | /** | ||
133 | 74 | * @brief streaming_post_form is a convenience method for issuing a POST request for the given URI, with url-encoded payload. | ||
134 | 75 | * @throw Errors::HttpMethodNotSupported if the underlying implementation does not support the provided HTTP method. | ||
135 | 76 | * @param configuration The configuration to issue a get request for. | ||
136 | 77 | * @param values Key-value pairs to be added to the payload in url-encoded format. | ||
137 | 78 | * @return An executable instance of class Request. | ||
138 | 79 | */ | ||
139 | 80 | virtual std::shared_ptr<StreamingRequest> streaming_post_form(const Request::Configuration& configuration, const std::map<std::string, std::string>& values) = 0; | ||
140 | 81 | }; | ||
141 | 82 | |||
142 | 83 | /** @brief Dispatches to the default implementation and returns a streaming client instance. */ | ||
143 | 84 | CORE_NET_DLL_PUBLIC std::shared_ptr<StreamingClient> make_streaming_client(); | ||
144 | 85 | } | ||
145 | 86 | } | ||
146 | 87 | } | ||
147 | 88 | #endif // CORE_NET_HTTP_STREAMING_CLIENT_H_ | ||
148 | 0 | 89 | ||
149 | === added file 'include/core/net/http/streaming_request.h' | |||
150 | --- include/core/net/http/streaming_request.h 1970-01-01 00:00:00 +0000 | |||
151 | +++ include/core/net/http/streaming_request.h 2015-04-01 06:23:51 +0000 | |||
152 | @@ -0,0 +1,60 @@ | |||
153 | 1 | /* | ||
154 | 2 | * Copyright © 2013 Canonical Ltd. | ||
155 | 3 | * | ||
156 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
157 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
158 | 6 | * as published by the Free Software Foundation. | ||
159 | 7 | * | ||
160 | 8 | * This program is distributed in the hope that it will be useful, | ||
161 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
162 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
163 | 11 | * GNU Lesser General Public License for more details. | ||
164 | 12 | * | ||
165 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
166 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
167 | 15 | * | ||
168 | 16 | * Authored by: Thomas Voß <thomas.voss@canonical.com> | ||
169 | 17 | */ | ||
170 | 18 | #ifndef CORE_NET_HTTP_STREAMING_REQUEST_H_ | ||
171 | 19 | #define CORE_NET_HTTP_STREAMING_REQUEST_H_ | ||
172 | 20 | |||
173 | 21 | #include <core/net/http/request.h> | ||
174 | 22 | |||
175 | 23 | namespace core | ||
176 | 24 | { | ||
177 | 25 | namespace net | ||
178 | 26 | { | ||
179 | 27 | namespace http | ||
180 | 28 | { | ||
181 | 29 | /** | ||
182 | 30 | * @brief The StreamingRequest class encapsulates a request for a web resource, | ||
183 | 31 | * streaming data to the receiver as it receives in addition to accumulating all incoming data. | ||
184 | 32 | */ | ||
185 | 33 | class CORE_NET_DLL_PUBLIC StreamingRequest : public Request | ||
186 | 34 | { | ||
187 | 35 | public: | ||
188 | 36 | |||
189 | 37 | /** DataHandler is invoked when a new chunk of data arrives from the server. */ | ||
190 | 38 | typedef std::function<void(const std::string&)> DataHandler; | ||
191 | 39 | |||
192 | 40 | /** | ||
193 | 41 | * @brief Synchronously executes the request. | ||
194 | 42 | * @throw core::net::http::Error in case of http-related errors. | ||
195 | 43 | * @throw core::net::Error in case of network-related errors. | ||
196 | 44 | * @return The response to the request. | ||
197 | 45 | */ | ||
198 | 46 | virtual Response execute(const ProgressHandler& ph, const DataHandler& dh) = 0; | ||
199 | 47 | |||
200 | 48 | /** | ||
201 | 49 | * @brief Asynchronously executes the request, reporting errors, progress and completion to the given handlers. | ||
202 | 50 | * @param handler The handlers to called for events happening during execution of the request. | ||
203 | 51 | * @param dh The data handler receiving chunks of data while executing the request. | ||
204 | 52 | * @return The response to the request. | ||
205 | 53 | */ | ||
206 | 54 | virtual void async_execute(const Handler& handler, const DataHandler& dh) = 0; | ||
207 | 55 | }; | ||
208 | 56 | } | ||
209 | 57 | } | ||
210 | 58 | } | ||
211 | 59 | |||
212 | 60 | #endif // CORE_NET_HTTP_STREAMING_REQUEST_H_ | ||
213 | 0 | 61 | ||
214 | === modified file 'src/CMakeLists.txt' | |||
215 | --- src/CMakeLists.txt 2014-06-10 14:19:14 +0000 | |||
216 | +++ src/CMakeLists.txt 2015-04-01 06:23:51 +0000 | |||
217 | @@ -22,6 +22,8 @@ | |||
218 | 22 | add_library( | 22 | add_library( |
219 | 23 | net-cpp SHARED | 23 | net-cpp SHARED |
220 | 24 | 24 | ||
221 | 25 | ${NET_CPP_INTERFACE_HEADERS} | ||
222 | 26 | |||
223 | 25 | core/location.cpp | 27 | core/location.cpp |
224 | 26 | 28 | ||
225 | 27 | core/net/error.cpp | 29 | core/net/error.cpp |
226 | 28 | 30 | ||
227 | === modified file 'src/core/net/http/impl/curl/client.cpp' | |||
228 | --- src/core/net/http/impl/curl/client.cpp 2014-06-10 14:19:14 +0000 | |||
229 | +++ src/core/net/http/impl/curl/client.cpp 2015-04-01 06:23:51 +0000 | |||
230 | @@ -20,6 +20,7 @@ | |||
231 | 20 | #include "curl.h" | 20 | #include "curl.h" |
232 | 21 | #include "request.h" | 21 | #include "request.h" |
233 | 22 | 22 | ||
234 | 23 | #include <core/net/http/content_type.h> | ||
235 | 23 | #include <core/net/http/method.h> | 24 | #include <core/net/http/method.h> |
236 | 24 | 25 | ||
237 | 25 | #include <boost/archive/iterators/base64_from_binary.hpp> | 26 | #include <boost/archive/iterators/base64_from_binary.hpp> |
238 | @@ -107,7 +108,7 @@ | |||
239 | 107 | multi.stop(); | 108 | multi.stop(); |
240 | 108 | } | 109 | } |
241 | 109 | 110 | ||
243 | 110 | std::shared_ptr<http::Request> http::impl::curl::Client::head(const http::Request::Configuration& configuration) | 111 | std::shared_ptr<http::impl::curl::Request> http::impl::curl::Client::head_impl(const http::Request::Configuration& configuration) |
244 | 111 | { | 112 | { |
245 | 112 | ::curl::easy::Handle handle; | 113 | ::curl::easy::Handle handle; |
246 | 113 | handle.method(http::Method::head) | 114 | handle.method(http::Method::head) |
247 | @@ -126,10 +127,10 @@ | |||
248 | 126 | handle.http_credentials(credentials.username, credentials.password); | 127 | handle.http_credentials(credentials.username, credentials.password); |
249 | 127 | } | 128 | } |
250 | 128 | 129 | ||
252 | 129 | return std::shared_ptr<http::Request>{new http::impl::curl::Request{multi, handle}}; | 130 | return std::shared_ptr<http::impl::curl::Request>{new http::impl::curl::Request{multi, handle}}; |
253 | 130 | } | 131 | } |
254 | 131 | 132 | ||
256 | 132 | std::shared_ptr<http::Request> http::impl::curl::Client::get(const http::Request::Configuration& configuration) | 133 | std::shared_ptr<http::impl::curl::Request> http::impl::curl::Client::get_impl(const http::Request::Configuration& configuration) |
257 | 133 | { | 134 | { |
258 | 134 | ::curl::easy::Handle handle; | 135 | ::curl::easy::Handle handle; |
259 | 135 | handle.method(http::Method::get) | 136 | handle.method(http::Method::get) |
260 | @@ -147,10 +148,10 @@ | |||
261 | 147 | handle.http_credentials(credentials.username, credentials.password); | 148 | handle.http_credentials(credentials.username, credentials.password); |
262 | 148 | } | 149 | } |
263 | 149 | 150 | ||
265 | 150 | return std::shared_ptr<http::Request>{new http::impl::curl::Request{multi, handle}}; | 151 | return std::shared_ptr<http::impl::curl::Request>{new http::impl::curl::Request{multi, handle}}; |
266 | 151 | } | 152 | } |
267 | 152 | 153 | ||
269 | 153 | std::shared_ptr<http::Request> http::impl::curl::Client::post( | 154 | std::shared_ptr<http::impl::curl::Request> http::impl::curl::Client::post_impl( |
270 | 154 | const Request::Configuration& configuration, | 155 | const Request::Configuration& configuration, |
271 | 155 | const std::string& payload, | 156 | const std::string& payload, |
272 | 156 | const std::string& ct) | 157 | const std::string& ct) |
273 | @@ -172,10 +173,10 @@ | |||
274 | 172 | handle.http_credentials(credentials.username, credentials.password); | 173 | handle.http_credentials(credentials.username, credentials.password); |
275 | 173 | } | 174 | } |
276 | 174 | 175 | ||
278 | 175 | return std::shared_ptr<http::Request>{new http::impl::curl::Request{multi, handle}}; | 176 | return std::shared_ptr<http::impl::curl::Request>{new http::impl::curl::Request{multi, handle}}; |
279 | 176 | } | 177 | } |
280 | 177 | 178 | ||
282 | 178 | std::shared_ptr<http::Request> http::impl::curl::Client::put( | 179 | std::shared_ptr<http::impl::curl::Request> http::impl::curl::Client::put_impl( |
283 | 179 | const Request::Configuration& configuration, | 180 | const Request::Configuration& configuration, |
284 | 180 | std::istream& payload, | 181 | std::istream& payload, |
285 | 181 | std::size_t size) | 182 | std::size_t size) |
286 | @@ -201,10 +202,75 @@ | |||
287 | 201 | handle.http_credentials(credentials.username, credentials.password); | 202 | handle.http_credentials(credentials.username, credentials.password); |
288 | 202 | } | 203 | } |
289 | 203 | 204 | ||
291 | 204 | return std::shared_ptr<http::Request>{new http::impl::curl::Request{multi, handle}}; | 205 | return std::shared_ptr<http::impl::curl::Request>{new http::impl::curl::Request{multi, handle}}; |
292 | 206 | } | ||
293 | 207 | |||
294 | 208 | std::shared_ptr<http::StreamingRequest> http::impl::curl::Client::streaming_get(const http::Request::Configuration& configuration) | ||
295 | 209 | { | ||
296 | 210 | return get_impl(configuration); | ||
297 | 211 | } | ||
298 | 212 | |||
299 | 213 | std::shared_ptr<http::StreamingRequest> http::impl::curl::Client::streaming_head(const http::Request::Configuration& configuration) | ||
300 | 214 | { | ||
301 | 215 | return head_impl(configuration); | ||
302 | 216 | } | ||
303 | 217 | |||
304 | 218 | std::shared_ptr<http::StreamingRequest> http::impl::curl::Client::streaming_put(const http::Request::Configuration& configuration, std::istream& payload, std::size_t size) | ||
305 | 219 | { | ||
306 | 220 | return put_impl(configuration, payload, size); | ||
307 | 221 | } | ||
308 | 222 | |||
309 | 223 | std::shared_ptr<http::StreamingRequest> http::impl::curl::Client::streaming_post(const http::Request::Configuration& configuration, const std::string& payload, const std::string& type) | ||
310 | 224 | { | ||
311 | 225 | return post_impl(configuration, payload, type); | ||
312 | 226 | } | ||
313 | 227 | |||
314 | 228 | std::shared_ptr<http::StreamingRequest> http::impl::curl::Client::streaming_post_form(const http::Request::Configuration& configuration, const std::map<std::string, std::string>& values) | ||
315 | 229 | { | ||
316 | 230 | std::stringstream ss; | ||
317 | 231 | bool first{true}; | ||
318 | 232 | |||
319 | 233 | for (const auto& pair : values) | ||
320 | 234 | { | ||
321 | 235 | ss << (first ? "" : "&") << url_escape(pair.first) << "=" << url_escape(pair.second); | ||
322 | 236 | first = false; | ||
323 | 237 | } | ||
324 | 238 | |||
325 | 239 | return post_impl(configuration, ss.str(), http::ContentType::x_www_form_urlencoded); | ||
326 | 240 | } | ||
327 | 241 | |||
328 | 242 | std::shared_ptr<http::Request> http::impl::curl::Client::head(const http::Request::Configuration& configuration) | ||
329 | 243 | { | ||
330 | 244 | return head_impl(configuration); | ||
331 | 245 | } | ||
332 | 246 | |||
333 | 247 | std::shared_ptr<http::Request> http::impl::curl::Client::get(const http::Request::Configuration& configuration) | ||
334 | 248 | { | ||
335 | 249 | return get_impl(configuration); | ||
336 | 250 | } | ||
337 | 251 | |||
338 | 252 | std::shared_ptr<http::Request> http::impl::curl::Client::post( | ||
339 | 253 | const Request::Configuration& configuration, | ||
340 | 254 | const std::string& payload, | ||
341 | 255 | const std::string& ct) | ||
342 | 256 | { | ||
343 | 257 | return post_impl(configuration, payload, ct); | ||
344 | 258 | } | ||
345 | 259 | |||
346 | 260 | std::shared_ptr<http::Request> http::impl::curl::Client::put( | ||
347 | 261 | const Request::Configuration& configuration, | ||
348 | 262 | std::istream& payload, | ||
349 | 263 | std::size_t size) | ||
350 | 264 | { | ||
351 | 265 | return put_impl(configuration, payload, size); | ||
352 | 205 | } | 266 | } |
353 | 206 | 267 | ||
354 | 207 | std::shared_ptr<http::Client> http::make_client() | 268 | std::shared_ptr<http::Client> http::make_client() |
355 | 208 | { | 269 | { |
356 | 209 | return std::make_shared<http::impl::curl::Client>(); | 270 | return std::make_shared<http::impl::curl::Client>(); |
357 | 210 | } | 271 | } |
358 | 272 | |||
359 | 273 | std::shared_ptr<http::StreamingClient> http::make_streaming_client() | ||
360 | 274 | { | ||
361 | 275 | return std::make_shared<http::impl::curl::Client>(); | ||
362 | 276 | } | ||
363 | 211 | 277 | ||
364 | === modified file 'src/core/net/http/impl/curl/client.h' | |||
365 | --- src/core/net/http/impl/curl/client.h 2014-06-10 14:19:14 +0000 | |||
366 | +++ src/core/net/http/impl/curl/client.h 2015-04-01 06:23:51 +0000 | |||
367 | @@ -18,9 +18,10 @@ | |||
368 | 18 | #ifndef CORE_NET_HTTP_IMPL_CURL_CLIENT_H_ | 18 | #ifndef CORE_NET_HTTP_IMPL_CURL_CLIENT_H_ |
369 | 19 | #define CORE_NET_HTTP_IMPL_CURL_CLIENT_H_ | 19 | #define CORE_NET_HTTP_IMPL_CURL_CLIENT_H_ |
370 | 20 | 20 | ||
372 | 21 | #include <core/net/http/client.h> | 21 | #include <core/net/http/streaming_client.h> |
373 | 22 | 22 | ||
374 | 23 | #include "curl.h" | 23 | #include "curl.h" |
375 | 24 | #include "request.h" | ||
376 | 24 | 25 | ||
377 | 25 | namespace core | 26 | namespace core |
378 | 26 | { | 27 | { |
379 | @@ -32,7 +33,7 @@ | |||
380 | 32 | { | 33 | { |
381 | 33 | namespace curl | 34 | namespace curl |
382 | 34 | { | 35 | { |
384 | 35 | class Client : public core::net::http::Client | 36 | class Client : public core::net::http::StreamingClient |
385 | 36 | { | 37 | { |
386 | 37 | public: | 38 | public: |
387 | 38 | Client(); | 39 | Client(); |
388 | @@ -45,23 +46,30 @@ | |||
389 | 45 | 46 | ||
390 | 46 | std::string base64_decode(const std::string& s) const override; | 47 | std::string base64_decode(const std::string& s) const override; |
391 | 47 | 48 | ||
405 | 48 | core::net::http::Client::Timings timings(); | 49 | core::net::http::Client::Timings timings() override; |
406 | 49 | 50 | ||
407 | 50 | void run(); | 51 | void run() override; |
408 | 51 | 52 | ||
409 | 52 | void stop(); | 53 | void stop() override; |
410 | 53 | 54 | ||
411 | 54 | std::shared_ptr<Request> get(const Request::Configuration& configuration); | 55 | std::shared_ptr<http::Request> get(const Request::Configuration& configuration) override; |
412 | 55 | 56 | std::shared_ptr<http::Request> head(const Request::Configuration& configuration) override; | |
413 | 56 | std::shared_ptr<Request> head(const Request::Configuration& configuration); | 57 | std::shared_ptr<http::Request> post(const Request::Configuration& configuration, const std::string&, const std::string&) override; |
414 | 57 | 58 | std::shared_ptr<http::Request> put(const Request::Configuration& configuration, std::istream& payload, std::size_t size) override; | |
415 | 58 | std::shared_ptr<Request> post(const Request::Configuration& configuration, const std::string&, const std::string&); | 59 | |
416 | 59 | 60 | std::shared_ptr<http::StreamingRequest> streaming_get(const Request::Configuration& configuration) override; | |
417 | 60 | std::shared_ptr<Request> put(const Request::Configuration& configuration, std::istream& payload, std::size_t size); | 61 | std::shared_ptr<http::StreamingRequest> streaming_head(const Request::Configuration& configuration) override; |
418 | 62 | std::shared_ptr<http::StreamingRequest> streaming_put(const Request::Configuration& configuration, std::istream& payload, std::size_t size) override; | ||
419 | 63 | std::shared_ptr<http::StreamingRequest> streaming_post(const Request::Configuration& configuration, const std::string& payload, const std::string& type) override; | ||
420 | 64 | std::shared_ptr<http::StreamingRequest> streaming_post_form(const Request::Configuration& configuration, const std::map<std::string, std::string>& values) override; | ||
421 | 61 | 65 | ||
422 | 62 | private: | 66 | private: |
424 | 63 | ::curl::multi::Handle multi; | 67 | std::shared_ptr<curl::Request> get_impl(const Request::Configuration& configuration); |
425 | 68 | std::shared_ptr<curl::Request> head_impl(const Request::Configuration& configuration); | ||
426 | 69 | std::shared_ptr<curl::Request> post_impl(const Request::Configuration& configuration, const std::string&, const std::string&); | ||
427 | 70 | std::shared_ptr<curl::Request> put_impl(const Request::Configuration& configuration, std::istream& payload, std::size_t size); | ||
428 | 64 | 71 | ||
429 | 72 | ::curl::multi::Handle multi; | ||
430 | 65 | }; | 73 | }; |
431 | 66 | } | 74 | } |
432 | 67 | } | 75 | } |
433 | 68 | 76 | ||
434 | === modified file 'src/core/net/http/impl/curl/request.h' | |||
435 | --- src/core/net/http/impl/curl/request.h 2015-01-23 06:23:31 +0000 | |||
436 | +++ src/core/net/http/impl/curl/request.h 2015-04-01 06:23:51 +0000 | |||
437 | @@ -18,7 +18,7 @@ | |||
438 | 18 | #ifndef CORE_NET_HTTP_IMPL_CURL_REQUEST_H_ | 18 | #ifndef CORE_NET_HTTP_IMPL_CURL_REQUEST_H_ |
439 | 19 | #define CORE_NET_HTTP_IMPL_CURL_REQUEST_H_ | 19 | #define CORE_NET_HTTP_IMPL_CURL_REQUEST_H_ |
440 | 20 | 20 | ||
442 | 21 | #include <core/net/http/request.h> | 21 | #include <core/net/http/streaming_request.h> |
443 | 22 | 22 | ||
444 | 23 | #include <core/net/http/error.h> | 23 | #include <core/net/http/error.h> |
445 | 24 | #include <core/net/http/response.h> | 24 | #include <core/net/http/response.h> |
446 | @@ -80,7 +80,7 @@ | |||
447 | 80 | std::atomic<core::net::http::Request::State>& state; | 80 | std::atomic<core::net::http::Request::State>& state; |
448 | 81 | }; | 81 | }; |
449 | 82 | 82 | ||
451 | 83 | class Request : public core::net::http::Request, | 83 | class Request : public core::net::http::StreamingRequest, |
452 | 84 | public std::enable_shared_from_this<Request> | 84 | public std::enable_shared_from_this<Request> |
453 | 85 | { | 85 | { |
454 | 86 | public: | 86 | public: |
455 | @@ -118,6 +118,11 @@ | |||
456 | 118 | 118 | ||
457 | 119 | Response execute(const Request::ProgressHandler& ph) | 119 | Response execute(const Request::ProgressHandler& ph) |
458 | 120 | { | 120 | { |
459 | 121 | return execute(ph, [](const std::string&){}); | ||
460 | 122 | } | ||
461 | 123 | |||
462 | 124 | Response execute(const Request::ProgressHandler& ph, const StreamingRequest::DataHandler& dh) | ||
463 | 125 | { | ||
464 | 121 | if (atomic_state.load() != core::net::http::Request::State::ready) | 126 | if (atomic_state.load() != core::net::http::Request::State::ready) |
465 | 122 | throw core::net::http::Request::Errors::AlreadyActive{CORE_FROM_HERE()}; | 127 | throw core::net::http::Request::Errors::AlreadyActive{CORE_FROM_HERE()}; |
466 | 123 | 128 | ||
467 | @@ -149,6 +154,8 @@ | |||
468 | 149 | easy.on_write_data( | 154 | easy.on_write_data( |
469 | 150 | [&](char* data, std::size_t size, std::size_t nmemb) | 155 | [&](char* data, std::size_t size, std::size_t nmemb) |
470 | 151 | { | 156 | { |
471 | 157 | // Report out to the data handler prior to accumulating data. | ||
472 | 158 | dh(std::string{data, size * nmemb}); | ||
473 | 152 | context.body.write(data, size * nmemb); | 159 | context.body.write(data, size * nmemb); |
474 | 153 | return size * nmemb; | 160 | return size * nmemb; |
475 | 154 | }); | 161 | }); |
476 | @@ -183,6 +190,11 @@ | |||
477 | 183 | 190 | ||
478 | 184 | void async_execute(const Request::Handler& handler) | 191 | void async_execute(const Request::Handler& handler) |
479 | 185 | { | 192 | { |
480 | 193 | async_execute(handler, [](const std::string&){}); | ||
481 | 194 | } | ||
482 | 195 | |||
483 | 196 | void async_execute(const Request::Handler& handler, const StreamingRequest::DataHandler& dh) | ||
484 | 197 | { | ||
485 | 186 | if (atomic_state.load() != core::net::http::Request::State::ready) | 198 | if (atomic_state.load() != core::net::http::Request::State::ready) |
486 | 187 | throw core::net::http::Request::Errors::AlreadyActive{CORE_FROM_HERE()}; | 199 | throw core::net::http::Request::Errors::AlreadyActive{CORE_FROM_HERE()}; |
487 | 188 | 200 | ||
488 | @@ -235,8 +247,10 @@ | |||
489 | 235 | } | 247 | } |
490 | 236 | 248 | ||
491 | 237 | easy.on_write_data( | 249 | easy.on_write_data( |
493 | 238 | [context](char* data, std::size_t size, std::size_t nmemb) | 250 | [context, dh](char* data, std::size_t size, std::size_t nmemb) |
494 | 239 | { | 251 | { |
495 | 252 | // Report out to the data handler prior to accumulating data. | ||
496 | 253 | dh(std::string{data, size * nmemb}); | ||
497 | 240 | context->body.write(data, size * nmemb); | 254 | context->body.write(data, size * nmemb); |
498 | 241 | return size * nmemb; | 255 | return size * nmemb; |
499 | 242 | }); | 256 | }); |
500 | 243 | 257 | ||
501 | === modified file 'tests/CMakeLists.txt' | |||
502 | --- tests/CMakeLists.txt 2014-05-22 11:50:55 +0000 | |||
503 | +++ tests/CMakeLists.txt 2015-04-01 06:23:51 +0000 | |||
504 | @@ -57,6 +57,11 @@ | |||
505 | 57 | ) | 57 | ) |
506 | 58 | 58 | ||
507 | 59 | add_executable( | 59 | add_executable( |
508 | 60 | http_streaming_client_test | ||
509 | 61 | http_streaming_client_test.cpp | ||
510 | 62 | ) | ||
511 | 63 | |||
512 | 64 | add_executable( | ||
513 | 60 | http_client_load_test | 65 | http_client_load_test |
514 | 61 | http_client_load_test.cpp | 66 | http_client_load_test.cpp |
515 | 62 | ) | 67 | ) |
516 | @@ -82,6 +87,16 @@ | |||
517 | 82 | ) | 87 | ) |
518 | 83 | 88 | ||
519 | 84 | target_link_libraries( | 89 | target_link_libraries( |
520 | 90 | http_streaming_client_test | ||
521 | 91 | |||
522 | 92 | net-cpp | ||
523 | 93 | |||
524 | 94 | ${GMOCK_BOTH_LIBRARIES} | ||
525 | 95 | ${JSON_CPP_LDFLAGS} | ||
526 | 96 | ${PROCESS_CPP_LDFLAGS} | ||
527 | 97 | ) | ||
528 | 98 | |||
529 | 99 | target_link_libraries( | ||
530 | 85 | http_client_load_test | 100 | http_client_load_test |
531 | 86 | 101 | ||
532 | 87 | net-cpp | 102 | net-cpp |
533 | @@ -93,4 +108,5 @@ | |||
534 | 93 | 108 | ||
535 | 94 | add_test(header_test ${CMAKE_CURRENT_BINARY_DIR}/header_test) | 109 | add_test(header_test ${CMAKE_CURRENT_BINARY_DIR}/header_test) |
536 | 95 | add_test(http_client_test ${CMAKE_CURRENT_BINARY_DIR}/http_client_test) | 110 | add_test(http_client_test ${CMAKE_CURRENT_BINARY_DIR}/http_client_test) |
537 | 111 | add_test(http_streaming_client_test ${CMAKE_CURRENT_BINARY_DIR}/http_streaming_client_test) | ||
538 | 96 | add_test(http_client_load_test ${CMAKE_CURRENT_BINARY_DIR}/http_client_load_test) | 112 | add_test(http_client_load_test ${CMAKE_CURRENT_BINARY_DIR}/http_client_load_test) |
539 | 97 | 113 | ||
540 | === added file 'tests/http_streaming_client_test.cpp' | |||
541 | --- tests/http_streaming_client_test.cpp 1970-01-01 00:00:00 +0000 | |||
542 | +++ tests/http_streaming_client_test.cpp 2015-04-01 06:23:51 +0000 | |||
543 | @@ -0,0 +1,508 @@ | |||
544 | 1 | /* | ||
545 | 2 | * Copyright © 2013 Canonical Ltd. | ||
546 | 3 | * | ||
547 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
548 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
549 | 6 | * as published by the Free Software Foundation. | ||
550 | 7 | * | ||
551 | 8 | * This program is distributed in the hope that it will be useful, | ||
552 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
553 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
554 | 11 | * GNU Lesser General Public License for more details. | ||
555 | 12 | * | ||
556 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
557 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
558 | 15 | * | ||
559 | 16 | * Authored by: Thomas Voß <thomas.voss@canonical.com> | ||
560 | 17 | */ | ||
561 | 18 | |||
562 | 19 | #include <core/net/error.h> | ||
563 | 20 | #include <core/net/uri.h> | ||
564 | 21 | #include <core/net/http/streaming_client.h> | ||
565 | 22 | #include <core/net/http/content_type.h> | ||
566 | 23 | #include <core/net/http/request.h> | ||
567 | 24 | #include <core/net/http/response.h> | ||
568 | 25 | |||
569 | 26 | #include "httpbin.h" | ||
570 | 27 | |||
571 | 28 | #include <gmock/gmock.h> | ||
572 | 29 | #include <gtest/gtest.h> | ||
573 | 30 | |||
574 | 31 | #include <json/json.h> | ||
575 | 32 | |||
576 | 33 | #include <future> | ||
577 | 34 | #include <memory> | ||
578 | 35 | |||
579 | 36 | namespace http = core::net::http; | ||
580 | 37 | namespace json = Json; | ||
581 | 38 | namespace net = core::net; | ||
582 | 39 | |||
583 | 40 | namespace | ||
584 | 41 | { | ||
585 | 42 | class MockDataHandler : public std::enable_shared_from_this<MockDataHandler> | ||
586 | 43 | { | ||
587 | 44 | public: | ||
588 | 45 | // We are enabling shared from this, thus forcing creation of | ||
589 | 46 | // managed instances here. | ||
590 | 47 | static std::shared_ptr<MockDataHandler> create() | ||
591 | 48 | { | ||
592 | 49 | return std::shared_ptr<MockDataHandler>(new MockDataHandler); | ||
593 | 50 | } | ||
594 | 51 | |||
595 | 52 | MOCK_METHOD1(on_new_data, void(const std::string&)); | ||
596 | 53 | |||
597 | 54 | http::StreamingRequest::DataHandler to_data_handler() | ||
598 | 55 | { | ||
599 | 56 | auto thiz = shared_from_this(); | ||
600 | 57 | return [thiz](const std::string& s) | ||
601 | 58 | { | ||
602 | 59 | thiz->on_new_data(s); | ||
603 | 60 | }; | ||
604 | 61 | } | ||
605 | 62 | |||
606 | 63 | private: | ||
607 | 64 | MockDataHandler() = default; | ||
608 | 65 | }; | ||
609 | 66 | |||
610 | 67 | auto default_progress_reporter = [](const http::Request::Progress& progress) | ||
611 | 68 | { | ||
612 | 69 | if (progress.download.current > 0. && progress.download.total > 0.) | ||
613 | 70 | std::cout << "Download progress: " << progress.download.current / progress.download.total << std::endl; | ||
614 | 71 | if (progress.upload.current > 0. && progress.upload.total > 0.) | ||
615 | 72 | std::cout << "Upload progress: " << progress.upload.current / progress.upload.total << std::endl; | ||
616 | 73 | |||
617 | 74 | return http::Request::Progress::Next::continue_operation; | ||
618 | 75 | }; | ||
619 | 76 | |||
620 | 77 | bool init() | ||
621 | 78 | { | ||
622 | 79 | static httpbin::Instance instance; | ||
623 | 80 | return true; | ||
624 | 81 | } | ||
625 | 82 | |||
626 | 83 | static const bool is_initialized = init(); | ||
627 | 84 | } | ||
628 | 85 | |||
629 | 86 | TEST(StreamingStreamingHttpClient, head_request_for_existing_resource_succeeds) | ||
630 | 87 | { | ||
631 | 88 | using namespace ::testing; | ||
632 | 89 | |||
633 | 90 | // We obtain a default client instance, dispatching to the default implementation. | ||
634 | 91 | auto client = http::make_streaming_client(); | ||
635 | 92 | |||
636 | 93 | // Url pointing to the resource we would like to access via http. | ||
637 | 94 | auto url = std::string(httpbin::host) + httpbin::resources::get(); | ||
638 | 95 | |||
639 | 96 | // The client mostly acts as a factory for http requests. | ||
640 | 97 | auto request = client->streaming_head(http::Request::Configuration::from_uri_as_string(url)); | ||
641 | 98 | |||
642 | 99 | // Our mocked data handler. | ||
643 | 100 | auto dh = MockDataHandler::create(); EXPECT_CALL(*dh, on_new_data(_)).Times(AtLeast(1)); | ||
644 | 101 | |||
645 | 102 | // We finally execute the query synchronously and story the response. | ||
646 | 103 | auto response = request->execute(default_progress_reporter, dh->to_data_handler()); | ||
647 | 104 | |||
648 | 105 | // We expect the query to complete successfully | ||
649 | 106 | EXPECT_EQ(core::net::http::Status::ok, response.status); | ||
650 | 107 | } | ||
651 | 108 | |||
652 | 109 | TEST(StreamingHttpClient, get_request_for_existing_resource_succeeds) | ||
653 | 110 | { | ||
654 | 111 | using namespace ::testing; | ||
655 | 112 | |||
656 | 113 | // We obtain a default client instance, dispatching to the default implementation. | ||
657 | 114 | auto client = http::make_streaming_client(); | ||
658 | 115 | |||
659 | 116 | // Url pointing to the resource we would like to access via http. | ||
660 | 117 | auto url = std::string(httpbin::host) + httpbin::resources::get(); | ||
661 | 118 | |||
662 | 119 | // The client mostly acts as a factory for http requests. | ||
663 | 120 | auto request = client->streaming_get(http::Request::Configuration::from_uri_as_string(url)); | ||
664 | 121 | |||
665 | 122 | // Our mocked data handler. | ||
666 | 123 | auto dh = MockDataHandler::create(); EXPECT_CALL(*dh, on_new_data(_)).Times(AtLeast(1)); | ||
667 | 124 | |||
668 | 125 | // All endpoint data on httpbin.org is JSON encoded. | ||
669 | 126 | json::Value root; | ||
670 | 127 | json::Reader reader; | ||
671 | 128 | |||
672 | 129 | // We finally execute the query synchronously and story the response. | ||
673 | 130 | auto response = request->execute(default_progress_reporter, dh->to_data_handler()); | ||
674 | 131 | |||
675 | 132 | // We expect the query to complete successfully | ||
676 | 133 | EXPECT_EQ(core::net::http::Status::ok, response.status); | ||
677 | 134 | // Parsing the body of the response as JSON should succeed. | ||
678 | 135 | EXPECT_TRUE(reader.parse(response.body, root)); | ||
679 | 136 | // The url field of the payload should equal the original url we requested. | ||
680 | 137 | EXPECT_EQ(url, root["url"].asString()); | ||
681 | 138 | } | ||
682 | 139 | |||
683 | 140 | TEST(StreamingHttpClient, get_request_with_custom_headers_for_existing_resource_succeeds) | ||
684 | 141 | { | ||
685 | 142 | using namespace ::testing; | ||
686 | 143 | |||
687 | 144 | // We obtain a default client instance, dispatching to the default implementation. | ||
688 | 145 | auto client = http::make_streaming_client(); | ||
689 | 146 | |||
690 | 147 | // Url pointing to the resource we would like to access via http. | ||
691 | 148 | auto url = std::string(httpbin::host) + httpbin::resources::headers(); | ||
692 | 149 | |||
693 | 150 | // The client mostly acts as a factory for http requests. | ||
694 | 151 | auto configuration = http::Request::Configuration::from_uri_as_string(url); | ||
695 | 152 | configuration.header.set("Test1", "42"); | ||
696 | 153 | configuration.header.set("Test2", "43"); | ||
697 | 154 | |||
698 | 155 | auto request = client->streaming_get(configuration); | ||
699 | 156 | |||
700 | 157 | // Our mocked data handler. | ||
701 | 158 | auto dh = MockDataHandler::create(); EXPECT_CALL(*dh, on_new_data(_)).Times(AtLeast(1)); | ||
702 | 159 | |||
703 | 160 | // All endpoint data on httpbin.org is JSON encoded. | ||
704 | 161 | json::Value root; | ||
705 | 162 | json::Reader reader; | ||
706 | 163 | |||
707 | 164 | // We finally execute the query synchronously and story the response. | ||
708 | 165 | auto response = request->execute(default_progress_reporter, dh->to_data_handler()); | ||
709 | 166 | |||
710 | 167 | // We expect the query to complete successfully | ||
711 | 168 | EXPECT_EQ(core::net::http::Status::ok, response.status); | ||
712 | 169 | |||
713 | 170 | // Parsing the body of the response as JSON should succeed. | ||
714 | 171 | EXPECT_TRUE(reader.parse(response.body, root)); | ||
715 | 172 | |||
716 | 173 | auto headers = root["headers"]; | ||
717 | 174 | |||
718 | 175 | EXPECT_EQ("42", headers["Test1"].asString()); | ||
719 | 176 | EXPECT_EQ("43", headers["Test2"].asString()); | ||
720 | 177 | } | ||
721 | 178 | |||
722 | 179 | TEST(StreamingHttpClient, empty_header_values_are_handled_correctly) | ||
723 | 180 | { | ||
724 | 181 | using namespace ::testing; | ||
725 | 182 | |||
726 | 183 | // We obtain a default client instance, dispatching to the default implementation. | ||
727 | 184 | auto client = http::make_streaming_client(); | ||
728 | 185 | |||
729 | 186 | // Url pointing to the resource we would like to access via http. | ||
730 | 187 | auto url = std::string(httpbin::host) + httpbin::resources::headers(); | ||
731 | 188 | |||
732 | 189 | // The client mostly acts as a factory for http requests. | ||
733 | 190 | auto configuration = http::Request::Configuration::from_uri_as_string(url); | ||
734 | 191 | configuration.header.set("Empty", std::string{}); | ||
735 | 192 | |||
736 | 193 | auto request = client->streaming_get(configuration); | ||
737 | 194 | |||
738 | 195 | // Our mocked data handler. | ||
739 | 196 | auto dh = MockDataHandler::create(); EXPECT_CALL(*dh, on_new_data(_)).Times(AtLeast(1)); | ||
740 | 197 | |||
741 | 198 | // All endpoint data on httpbin.org is JSON encoded. | ||
742 | 199 | json::Value root; | ||
743 | 200 | json::Reader reader; | ||
744 | 201 | |||
745 | 202 | // We finally execute the query synchronously and story the response. | ||
746 | 203 | auto response = request->execute(default_progress_reporter, dh->to_data_handler()); | ||
747 | 204 | |||
748 | 205 | // We expect the query to complete successfully | ||
749 | 206 | EXPECT_EQ(core::net::http::Status::ok, response.status); | ||
750 | 207 | |||
751 | 208 | // Parsing the body of the response as JSON should succeed. | ||
752 | 209 | EXPECT_TRUE(reader.parse(response.body, root)); | ||
753 | 210 | |||
754 | 211 | auto headers = root["headers"]; | ||
755 | 212 | EXPECT_EQ(std::string{}, headers["Empty"].asString()); | ||
756 | 213 | } | ||
757 | 214 | |||
758 | 215 | TEST(StreamingHttpClient, get_request_for_existing_resource_guarded_by_basic_auth_succeeds) | ||
759 | 216 | { | ||
760 | 217 | using namespace ::testing; | ||
761 | 218 | |||
762 | 219 | // We obtain a default client instance, dispatching to the default implementation. | ||
763 | 220 | auto client = http::make_streaming_client(); | ||
764 | 221 | |||
765 | 222 | // Url pointing to the resource we would like to access via http. | ||
766 | 223 | auto url = std::string(httpbin::host) + httpbin::resources::basic_auth(); | ||
767 | 224 | |||
768 | 225 | // The client mostly acts as a factory for http requests. | ||
769 | 226 | auto configuration = http::Request::Configuration::from_uri_as_string(url); | ||
770 | 227 | configuration.authentication_handler.for_http = [](const std::string&) | ||
771 | 228 | { | ||
772 | 229 | return http::Request::Credentials{"user", "passwd"}; | ||
773 | 230 | }; | ||
774 | 231 | auto request = client->streaming_get(configuration); | ||
775 | 232 | |||
776 | 233 | // Our mocked data handler. | ||
777 | 234 | auto dh = MockDataHandler::create(); EXPECT_CALL(*dh, on_new_data(_)).Times(AtLeast(1)); | ||
778 | 235 | |||
779 | 236 | // All endpoint data on httpbin.org is JSON encoded. | ||
780 | 237 | json::Value root; | ||
781 | 238 | json::Reader reader; | ||
782 | 239 | |||
783 | 240 | // We finally execute the query synchronously and story the response. | ||
784 | 241 | auto response = request->execute(default_progress_reporter, dh->to_data_handler()); | ||
785 | 242 | |||
786 | 243 | // We expect the query to complete successfully | ||
787 | 244 | EXPECT_EQ(core::net::http::Status::ok, response.status); | ||
788 | 245 | // Parsing the body of the response as JSON should succeed. | ||
789 | 246 | EXPECT_TRUE(reader.parse(response.body, root)); | ||
790 | 247 | // We expect authentication to work. | ||
791 | 248 | EXPECT_TRUE(root["authenticated"].asBool()); | ||
792 | 249 | // With the correct user id | ||
793 | 250 | EXPECT_EQ("user", root["user"].asString()); | ||
794 | 251 | } | ||
795 | 252 | |||
796 | 253 | // Digest auth is broken on httpbin.org. It even fails in the browser after the first successful access. | ||
797 | 254 | TEST(StreamingHttpClient, DISABLED_get_request_for_existing_resource_guarded_by_digest_auth_succeeds) | ||
798 | 255 | { | ||
799 | 256 | using namespace ::testing; | ||
800 | 257 | |||
801 | 258 | // We obtain a default client instance, dispatching to the default implementation. | ||
802 | 259 | auto client = http::make_streaming_client(); | ||
803 | 260 | |||
804 | 261 | // Url pointing to the resource we would like to access via http. | ||
805 | 262 | auto url = std::string(httpbin::host) + httpbin::resources::digest_auth(); | ||
806 | 263 | |||
807 | 264 | // The client mostly acts as a factory for http requests. | ||
808 | 265 | auto configuration = http::Request::Configuration::from_uri_as_string(url); | ||
809 | 266 | configuration.authentication_handler.for_http = [](const std::string&) | ||
810 | 267 | { | ||
811 | 268 | return http::Request::Credentials{"user", "passwd"}; | ||
812 | 269 | }; | ||
813 | 270 | auto request = client->streaming_get(configuration); | ||
814 | 271 | |||
815 | 272 | // Our mocked data handler. | ||
816 | 273 | auto dh = MockDataHandler::create(); EXPECT_CALL(*dh, on_new_data(_)).Times(AtLeast(1)); | ||
817 | 274 | |||
818 | 275 | // All endpoint data on httpbin.org is JSON encoded. | ||
819 | 276 | json::Value root; | ||
820 | 277 | json::Reader reader; | ||
821 | 278 | |||
822 | 279 | // We finally execute the query synchronously and story the response. | ||
823 | 280 | auto response = request->execute(default_progress_reporter, dh->to_data_handler()); | ||
824 | 281 | |||
825 | 282 | // We expect the query to complete successfully | ||
826 | 283 | EXPECT_EQ(core::net::http::Status::ok, response.status); | ||
827 | 284 | // Parsing the body of the response as JSON should succeed. | ||
828 | 285 | EXPECT_TRUE(reader.parse(response.body, root)); | ||
829 | 286 | // We expect authentication to work. | ||
830 | 287 | EXPECT_TRUE(root["authenticated"].asBool()); | ||
831 | 288 | // With the correct user id | ||
832 | 289 | EXPECT_EQ("user", root["user"].asString()); | ||
833 | 290 | } | ||
834 | 291 | |||
835 | 292 | TEST(StreamingHttpClient, async_get_request_for_existing_resource_succeeds) | ||
836 | 293 | { | ||
837 | 294 | using namespace ::testing; | ||
838 | 295 | |||
839 | 296 | // We obtain a default client instance, dispatching to the default implementation. | ||
840 | 297 | auto client = http::make_streaming_client(); | ||
841 | 298 | |||
842 | 299 | // Execute the client | ||
843 | 300 | std::thread worker{[client]() { client->run(); }}; | ||
844 | 301 | |||
845 | 302 | // Url pointing to the resource we would like to access via http. | ||
846 | 303 | auto url = std::string(httpbin::host) + httpbin::resources::get(); | ||
847 | 304 | |||
848 | 305 | // The client mostly acts as a factory for http requests. | ||
849 | 306 | auto request = client->streaming_get(http::Request::Configuration::from_uri_as_string(url)); | ||
850 | 307 | |||
851 | 308 | // Our mocked data handler. | ||
852 | 309 | auto dh = MockDataHandler::create(); EXPECT_CALL(*dh, on_new_data(_)).Times(AtLeast(1)); | ||
853 | 310 | |||
854 | 311 | std::promise<core::net::http::Response> promise; | ||
855 | 312 | auto future = promise.get_future(); | ||
856 | 313 | |||
857 | 314 | // We finally execute the query asynchronously. | ||
858 | 315 | request->async_execute( | ||
859 | 316 | http::Request::Handler() | ||
860 | 317 | .on_progress(default_progress_reporter) | ||
861 | 318 | .on_response([&](const core::net::http::Response& response) | ||
862 | 319 | { | ||
863 | 320 | promise.set_value(response); | ||
864 | 321 | }) | ||
865 | 322 | .on_error([&](const core::net::Error& e) | ||
866 | 323 | { | ||
867 | 324 | promise.set_exception(std::make_exception_ptr(e)); | ||
868 | 325 | }), | ||
869 | 326 | dh->to_data_handler()); | ||
870 | 327 | |||
871 | 328 | auto response = future.get(); | ||
872 | 329 | |||
873 | 330 | // All endpoint data on httpbin.org is JSON encoded. | ||
874 | 331 | json::Value root; | ||
875 | 332 | json::Reader reader; | ||
876 | 333 | |||
877 | 334 | // We expect the query to complete successfully | ||
878 | 335 | EXPECT_EQ(core::net::http::Status::ok, response.status); | ||
879 | 336 | // Parsing the body of the response as JSON should succeed. | ||
880 | 337 | EXPECT_TRUE(reader.parse(response.body, root)); | ||
881 | 338 | // The url field of the payload should equal the original url we requested. | ||
882 | 339 | EXPECT_EQ(url, root["url"].asString()); | ||
883 | 340 | |||
884 | 341 | client->stop(); | ||
885 | 342 | |||
886 | 343 | // We shut down our worker thread | ||
887 | 344 | if (worker.joinable()) | ||
888 | 345 | worker.join(); | ||
889 | 346 | } | ||
890 | 347 | |||
891 | 348 | TEST(StreamingHttpClient, async_get_request_for_existing_resource_guarded_by_basic_authentication_succeeds) | ||
892 | 349 | { | ||
893 | 350 | using namespace ::testing; | ||
894 | 351 | |||
895 | 352 | // We obtain a default client instance, dispatching to the default implementation. | ||
896 | 353 | auto client = http::make_streaming_client(); | ||
897 | 354 | |||
898 | 355 | // Execute the client | ||
899 | 356 | std::thread worker{[client]() { client->run(); }}; | ||
900 | 357 | |||
901 | 358 | // Url pointing to the resource we would like to access via http. | ||
902 | 359 | auto url = std::string(httpbin::host) + httpbin::resources::basic_auth(); | ||
903 | 360 | |||
904 | 361 | // The client mostly acts as a factory for http requests. | ||
905 | 362 | auto configuration = http::Request::Configuration::from_uri_as_string(url); | ||
906 | 363 | |||
907 | 364 | configuration.authentication_handler.for_http = [](const std::string&) | ||
908 | 365 | { | ||
909 | 366 | return http::Request::Credentials{"user", "passwd"}; | ||
910 | 367 | }; | ||
911 | 368 | |||
912 | 369 | auto request = client->streaming_get(configuration); | ||
913 | 370 | |||
914 | 371 | // Our mocked data handler. | ||
915 | 372 | auto dh = MockDataHandler::create(); EXPECT_CALL(*dh, on_new_data(_)).Times(AtLeast(1)); | ||
916 | 373 | |||
917 | 374 | // All endpoint data on httpbin.org is JSON encoded. | ||
918 | 375 | json::Value root; | ||
919 | 376 | json::Reader reader; | ||
920 | 377 | |||
921 | 378 | std::promise<core::net::http::Response> promise; | ||
922 | 379 | auto future = promise.get_future(); | ||
923 | 380 | |||
924 | 381 | // We finally execute the query asynchronously. | ||
925 | 382 | request->async_execute( | ||
926 | 383 | http::Request::Handler() | ||
927 | 384 | .on_progress(default_progress_reporter) | ||
928 | 385 | .on_response([&](const core::net::http::Response& response) | ||
929 | 386 | { | ||
930 | 387 | promise.set_value(response); | ||
931 | 388 | client->stop(); | ||
932 | 389 | }) | ||
933 | 390 | .on_error([&](const core::net::Error& e) | ||
934 | 391 | { | ||
935 | 392 | promise.set_exception(std::make_exception_ptr(e)); | ||
936 | 393 | client->stop(); | ||
937 | 394 | }), | ||
938 | 395 | dh->to_data_handler()); | ||
939 | 396 | |||
940 | 397 | // And wait here for the response to arrive. | ||
941 | 398 | auto response = future.get(); | ||
942 | 399 | |||
943 | 400 | // We shut down our worker thread | ||
944 | 401 | if (worker.joinable()) | ||
945 | 402 | worker.join(); | ||
946 | 403 | |||
947 | 404 | // We expect the query to complete successfully | ||
948 | 405 | EXPECT_EQ(core::net::http::Status::ok, response.status); | ||
949 | 406 | // Parsing the body of the response as JSON should succeed. | ||
950 | 407 | EXPECT_TRUE(reader.parse(response.body, root)); | ||
951 | 408 | // We expect authentication to work. | ||
952 | 409 | EXPECT_TRUE(root["authenticated"].asBool()); | ||
953 | 410 | // With the correct user id | ||
954 | 411 | EXPECT_EQ("user", root["user"].asString()); | ||
955 | 412 | } | ||
956 | 413 | |||
957 | 414 | TEST(StreamingHttpClient, post_request_for_existing_resource_succeeds) | ||
958 | 415 | { | ||
959 | 416 | using namespace ::testing; | ||
960 | 417 | |||
961 | 418 | // We obtain a default client instance, dispatching to the default implementation. | ||
962 | 419 | auto client = http::make_streaming_client(); | ||
963 | 420 | |||
964 | 421 | // Url pointing to the resource we would like to access via http. | ||
965 | 422 | auto url = std::string(httpbin::host) + httpbin::resources::post(); | ||
966 | 423 | |||
967 | 424 | std::string payload = "{ 'test': 'test' }"; | ||
968 | 425 | |||
969 | 426 | // The client mostly acts as a factory for http requests. | ||
970 | 427 | auto request = client->streaming_post(http::Request::Configuration::from_uri_as_string(url), | ||
971 | 428 | payload, | ||
972 | 429 | core::net::http::ContentType::json); | ||
973 | 430 | |||
974 | 431 | // Our mocked data handler. | ||
975 | 432 | auto dh = MockDataHandler::create(); EXPECT_CALL(*dh, on_new_data(_)).Times(AtLeast(1)); | ||
976 | 433 | |||
977 | 434 | // All endpoint data on httpbin.org is JSON encoded. | ||
978 | 435 | json::Value root; | ||
979 | 436 | json::Reader reader; | ||
980 | 437 | |||
981 | 438 | // We finally execute the query synchronously and story the response. | ||
982 | 439 | auto response = request->execute(default_progress_reporter, dh->to_data_handler()); | ||
983 | 440 | |||
984 | 441 | // We expect the query to complete successfully | ||
985 | 442 | EXPECT_EQ(core::net::http::Status::ok, response.status); | ||
986 | 443 | // Parsing the body of the response as JSON should succeed. | ||
987 | 444 | EXPECT_TRUE(reader.parse(response.body, root)); | ||
988 | 445 | // The url field of the payload should equal the original url we requested. | ||
989 | 446 | EXPECT_EQ(payload, root["data"].asString()); | ||
990 | 447 | } | ||
991 | 448 | |||
992 | 449 | TEST(StreamingHttpClient, post_form_request_for_existing_resource_succeeds) | ||
993 | 450 | { | ||
994 | 451 | using namespace ::testing; | ||
995 | 452 | |||
996 | 453 | // We obtain a default client instance, dispatching to the default implementation. | ||
997 | 454 | auto client = http::make_streaming_client(); | ||
998 | 455 | |||
999 | 456 | // Url pointing to the resource we would like to access via http. | ||
1000 | 457 | auto url = std::string(httpbin::host) + httpbin::resources::post(); | ||
1001 | 458 | |||
1002 | 459 | std::map<std::string, std::string> values | ||
1003 | 460 | { | ||
1004 | 461 | {"test", "test"} | ||
1005 | 462 | }; | ||
1006 | 463 | |||
1007 | 464 | // The client mostly acts as a factory for http requests. | ||
1008 | 465 | auto request = client->streaming_post_form(http::Request::Configuration::from_uri_as_string(url), | ||
1009 | 466 | values); | ||
1010 | 467 | |||
1011 | 468 | // Our mocked data handler. | ||
1012 | 469 | auto dh = MockDataHandler::create(); EXPECT_CALL(*dh, on_new_data(_)).Times(AtLeast(1)); | ||
1013 | 470 | |||
1014 | 471 | // We finally execute the query synchronously and store the response. | ||
1015 | 472 | auto response = request->execute(default_progress_reporter, dh->to_data_handler()); | ||
1016 | 473 | |||
1017 | 474 | // All endpoint data on httpbin.org is JSON encoded. | ||
1018 | 475 | json::Value root; | ||
1019 | 476 | json::Reader reader; | ||
1020 | 477 | |||
1021 | 478 | EXPECT_EQ(core::net::http::Status::ok, response.status); | ||
1022 | 479 | EXPECT_TRUE(reader.parse(response.body, root)); | ||
1023 | 480 | EXPECT_EQ("test", root["form"]["test"].asString()); | ||
1024 | 481 | } | ||
1025 | 482 | |||
1026 | 483 | TEST(StreamingHttpClient, put_request_for_existing_resource_succeeds) | ||
1027 | 484 | { | ||
1028 | 485 | using namespace ::testing; | ||
1029 | 486 | |||
1030 | 487 | auto client = http::make_streaming_client(); | ||
1031 | 488 | auto url = std::string(httpbin::host) + httpbin::resources::put(); | ||
1032 | 489 | |||
1033 | 490 | const std::string value{"{ 'test': 'test' }"}; | ||
1034 | 491 | std::stringstream payload(value); | ||
1035 | 492 | |||
1036 | 493 | auto request = client->streaming_put(http::Request::Configuration::from_uri_as_string(url), | ||
1037 | 494 | payload, | ||
1038 | 495 | value.size()); | ||
1039 | 496 | |||
1040 | 497 | // Our mocked data handler. | ||
1041 | 498 | auto dh = MockDataHandler::create(); EXPECT_CALL(*dh, on_new_data(_)).Times(AtLeast(1)); | ||
1042 | 499 | |||
1043 | 500 | json::Value root; | ||
1044 | 501 | json::Reader reader; | ||
1045 | 502 | |||
1046 | 503 | auto response = request->execute(default_progress_reporter, dh->to_data_handler()); | ||
1047 | 504 | |||
1048 | 505 | EXPECT_EQ(core::net::http::Status::ok, response.status); | ||
1049 | 506 | EXPECT_TRUE(reader.parse(response.body, root)); | ||
1050 | 507 | EXPECT_EQ(payload.str(), root["data"].asString()); | ||
1051 | 508 | } |
PASSED: Continuous integration, rev:49 jenkins. qa.ubuntu. com/job/ net-cpp- ci/6/ jenkins. qa.ubuntu. com/job/ net-cpp- vivid-amd64- ci/6 jenkins. qa.ubuntu. com/job/ net-cpp- vivid-armhf- ci/6 jenkins. qa.ubuntu. com/job/ net-cpp- vivid-armhf- ci/6/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ net-cpp- vivid-i386- ci/6
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/net- cpp-ci/ 6/rebuild
http://