Unity Scopes API
HttpAsyncReader.h
1 /*
2  * Copyright (C) 2014 Canonical Ltd
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License version 3 as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Author: Xavi Garcia <xavi.garcia.mena@canonical.com>
17  */
18 
19 #pragma once
20 
21 #ifndef _ENABLE_QT_EXPERIMENTAL_
22 #error You should define _ENABLE_QT_EXPERIMENTAL_ in order to use this experimental header file.
23 #endif
24 
25 #include <unity/util/DefinesPtrs.h>
26 #include <unity/util/NonCopyable.h>
27 #include <unity/UnityExceptions.h>
28 
29 #include <unity/scopes/ScopeExceptions.h>
30 
31 #include <core/net/http/client.h>
32 #include <core/net/http/request.h>
33 #include <core/net/http/response.h>
34 
35 #include <deque>
36 #include <future>
37 #include <memory>
38 #include <vector>
39 
40 namespace unity
41 {
42 
43 namespace scopes
44 {
45 
46 namespace qt
47 {
48 
57 {
58 public:
60  NONCOPYABLE(HttpAsyncReader);
61  UNITY_DEFINES_PTRS(HttpAsyncReader);
62 
63  // aliases
64  template <typename PARSER>
65  using FactoryFunc = std::function<std::shared_ptr<PARSER>(const std::string&, std::string&)>;
66 
67  template <typename TYPE, typename PARSER>
68  using ParseFunc =
69  std::function<bool(PARSER& root, const std::string&, std::deque<std::shared_ptr<TYPE>>&, std::string&)>;
70 
71  template <typename TYPE>
72  using ResultsList = std::deque<std::shared_ptr<TYPE>>;
73 
74  template <typename TYPE>
75  using ResultsFuture = std::future<ResultsList<TYPE>>;
76 
77  template <typename PARSER>
78  using ParserFuture = std::future<std::shared_ptr<PARSER>>;
79 
81 
82  virtual ~HttpAsyncReader();
84 
109  template <typename BASE, typename TYPE, typename PARSER>
110  ResultsFuture<BASE> async_get(std::string const& uri,
111  std::string const& object_name,
112  FactoryFunc<PARSER> const& create,
113  ParseFunc<BASE, PARSER> const& parse) const;
114 
133  template <typename TYPE, typename PARSER>
134  ResultsFuture<TYPE> async_get(std::string const& uri,
135  std::string const& object_name,
136  FactoryFunc<PARSER> const& create,
137  ParseFunc<TYPE, PARSER> const& parse) const;
138 
149  template <typename PARSER>
150  ParserFuture<PARSER> async_get_parser(std::string const& uri, FactoryFunc<PARSER> const& create) const;
151 
162  template <typename T>
163  static T get_or_throw(std::future<T>& f, int64_t seconds = 20);
164 
169  std::string get_uri(std::string const& host,
170  std::vector<std::pair<std::string, std::string>> const& parameters) const;
171 
172 protected:
174  core::net::http::Request::Progress::Next progress_report(core::net::http::Request::Progress const& progress) const;
175 
176  void async_execute(core::net::http::Request::Handler const& handler, std::string const& uri) const;
177 
178  class Priv;
179  std::shared_ptr<Priv> p_;
181 };
182 
183 template <typename BASE, typename TYPE, typename PARSER>
184 HttpAsyncReader::ResultsFuture<BASE> HttpAsyncReader::async_get(std::string const& uri,
185  std::string const& object_name,
186  FactoryFunc<PARSER> const& create,
187  ParseFunc<BASE, PARSER> const& parse) const
188 {
189  static_assert(std::is_base_of<BASE, TYPE>::value,
190  "Second template parameter type must be a valid base class of the first one.");
191 
192  auto prom = std::make_shared<std::promise<std::deque<std::shared_ptr<BASE>>>>();
193  core::net::http::Request::Handler handler;
194 
195  handler.on_progress(bind(&HttpAsyncReader::progress_report, this, std::placeholders::_1));
196 
197  handler.on_error([prom, uri](core::net::Error const& e)
198  {
199  unity::LogicException logic_exception("AsyncReader::async_get: " + std::string(e.what()) +
200  "( uri = " + uri + " )");
201  prom->set_exception(logic_exception.self());
202  });
203 
204  handler.on_response(
205  [prom, object_name, parse, create, uri](core::net::http::Response const& response)
206  {
207  if (response.status != core::net::http::Status::ok)
208  {
209  unity::LogicException e("AsyncReader::async_get: " + response.body + "( uri = " + uri + " )");
210  prom->set_exception(e.self());
211  }
212  else
213  {
214  std::string error_string;
215  std::shared_ptr<PARSER> reader = create(response.body, error_string);
216  if (!reader)
217  {
218  unity::LogicException e("AsyncReader::async_get: error obtaining parser: " + error_string);
219  prom->set_exception(e.self());
220  }
221  else
222  {
223  std::deque<std::shared_ptr<BASE>> results;
224  std::string error_message;
225  if (!parse(*reader, object_name, results, error_message))
226  {
227  unity::LogicException e("AsyncReader::async_get: error parsing data: " + error_message);
228  prom->set_exception(e.self());
229  }
230  else
231  {
232  prom->set_value(results);
233  }
234  }
235  }
236  });
237 
238  async_execute(handler, uri);
239 
240  return prom->get_future();
241 }
242 
243 template <typename TYPE, typename PARSER>
244 HttpAsyncReader::ResultsFuture<TYPE> HttpAsyncReader::async_get(std::string const& uri,
245  std::string const& object_name,
246  FactoryFunc<PARSER> const& create,
247  ParseFunc<TYPE, PARSER> const& parse) const
248 {
249  return async_get<TYPE, TYPE, PARSER>(uri, object_name, create, parse);
250 }
251 
252 template <typename PARSER>
253 HttpAsyncReader::ParserFuture<PARSER> HttpAsyncReader::async_get_parser(std::string const& uri,
254  FactoryFunc<PARSER> const& create) const
255 {
256  auto prom = std::make_shared<std::promise<std::shared_ptr<PARSER>>>();
257  core::net::http::Request::Handler handler;
258  handler.on_progress(bind(&HttpAsyncReader::progress_report, this, std::placeholders::_1));
259 
260  handler.on_error([prom, uri](core::net::Error const& e)
261  {
262  unity::LogicException logic_exception("AsyncReader::async_get: " + std::string(e.what()) +
263  "( uri = " + uri + " )");
264  prom->set_exception(logic_exception.self());
265  });
266 
267  handler.on_response(
268  [this, prom, create, uri](core::net::http::Response const& response)
269  {
270  if (response.status != core::net::http::Status::ok)
271  {
272  unity::LogicException e("AsyncReader::async_get_parser: " + response.body + "( uri = " + uri + " )");
273  prom->set_exception(e.self());
274  }
275  else
276  {
277  std::string error_string;
278  std::shared_ptr<PARSER> reader = create(response.body, error_string);
279  if (!reader)
280  {
281  unity::LogicException e("AsyncReader::async_get: error obtaining parser: " + error_string);
282  prom->set_exception(e.self());
283  }
284  else
285  {
286  prom->set_value(reader);
287  }
288  }
289  });
290 
291  async_execute(handler, uri);
292 
293  return prom->get_future();
294 }
295 
296 template <typename T>
297 T HttpAsyncReader::get_or_throw(std::future<T>& f, int64_t seconds)
298 {
299  if (f.wait_for(std::chrono::seconds(seconds)) != std::future_status::ready)
300  {
301  throw unity::scopes::TimeoutException("AsyncReader::get_or_throw: Wait for future timeout after " +
302  std::to_string(seconds) + " seconds");
303  }
304  return f.get();
305 }
306 
307 } // namespace qt
308 
309 } // namespace scopes
310 
311 } // namespace unity
ResultsFuture< BASE > async_get(std::string const &uri, std::string const &object_name, FactoryFunc< PARSER > const &create, ParseFunc< BASE, PARSER > const &parse) const
Downloads a HTTP remote file asynchronously and returns a future to a list of results This method dow...
Definition: HttpAsyncReader.h:184
ParserFuture< PARSER > async_get_parser(std::string const &uri, FactoryFunc< PARSER > const &create) const
Downloads a HTTP remote file asynchronously and returns a future to a valid parser containing the dat...
Definition: HttpAsyncReader.h:253
Top-level namespace for all things Unity-related.
Definition: Version.h:49
Class that downloads http files asynchronously.
Definition: HttpAsyncReader.h:56
std::string get_uri(std::string const &host, std::vector< std::pair< std::string, std::string >> const &parameters) const
Constructs a URI with the given host and parameters. This is a convenience method that constructs a u...
Definition: HttpAsyncReader.cpp:85
Exception to indicate that a twoway request timed out.
Definition: ScopeExceptions.h:108
static T get_or_throw(std::future< T > &f, int64_t seconds=20)
Gets the data of the given future in the gived timeout. If the time given expires and the data in the...
Definition: HttpAsyncReader.h:297