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