Aktualizr
C++ SOTA Client
httpfake.h
1 #ifndef HTTPFAKE_H_
2 #define HTTPFAKE_H_
3 
4 #include <boost/algorithm/hex.hpp>
5 #include <boost/algorithm/string.hpp>
6 #include <boost/filesystem.hpp>
7 #include <chrono>
8 #include <fstream>
9 #include <string>
10 #include <thread>
11 
12 #include "json/json.h"
13 
14 #include "crypto/crypto.h"
15 #include "http/httpinterface.h"
16 #include "logging/logging.h"
17 #include "metafake.h"
18 #include "utilities/utils.h"
19 
20 class HttpFake : public HttpInterface {
21  public:
22  // old style HttpFake with centralized multi repo and url rewriting
23  HttpFake(const boost::filesystem::path &test_dir_in, std::string flavor = "",
24  const boost::filesystem::path &meta_dir_in = "")
25  : test_dir(test_dir_in), flavor_(std::move(flavor)), meta_dir(meta_dir_in) {
26  if (meta_dir.empty()) {
27  meta_dir = temp_meta_dir.Path();
28  MetaFake meta(meta_dir);
29  }
30  }
31 
32  virtual ~HttpFake() {}
33 
34  void setCerts(const std::string &ca, CryptoSource ca_source, const std::string &cert, CryptoSource cert_source,
35  const std::string &pkey, CryptoSource pkey_source) override {
36  (void)ca;
37  (void)ca_source;
38  (void)cert;
39  (void)cert_source;
40  (void)pkey;
41  (void)pkey_source;
42  }
43 
44  // rewrite xxx/yyy.json to xxx/yyy_flavor.json
45  bool rewrite(std::string &url, const std::string &pattern) {
46  size_t pat_pos = url.find(pattern);
47  if (pat_pos == std::string::npos) {
48  return false;
49  }
50  size_t ext_pos = pattern.find(".json");
51  if (ext_pos == std::string::npos) {
52  LOG_ERROR << "Invalid pattern";
53  url = "";
54  return false;
55  }
56 
57  url.replace(pat_pos + ext_pos, std::string(".json").size(), "_" + flavor_ + ".json");
58  return true;
59  }
60 
61  virtual HttpResponse handle_event(const std::string &url, const Json::Value &data) {
62  (void)url;
63  (void)data;
64  // do something in child instances
65  return HttpResponse("", 400, CURLE_OK, "");
66  }
67 
68  HttpResponse get(const std::string &url, int64_t maxsize) override {
69  (void)maxsize;
70  std::cout << "URL requested: " << url << "\n";
71 
72  std::string new_url = url;
73  if (!flavor_.empty()) {
74  rewrite(new_url, "director/targets.json") || rewrite(new_url, "repo/timestamp.json") ||
75  rewrite(new_url, "repo/targets.json") || rewrite(new_url, "snapshot.json");
76 
77  if (new_url != url) {
78  std::cout << "Rewritten to: " << new_url << "\n";
79  }
80  }
81 
82  const boost::filesystem::path path = meta_dir / new_url.substr(tls_server.size());
83 
84  std::cout << "file served: " << path << "\n";
85 
86  if (boost::filesystem::exists(path)) {
87  return HttpResponse(Utils::readFile(path), 200, CURLE_OK, "");
88  } else {
89  std::cout << "not found: " << path << "\n";
90  return HttpResponse({}, 404, CURLE_OK, "");
91  }
92  }
93 
94  HttpResponse post(const std::string &url, const std::string &content_type, const std::string &data) override {
95  (void)url;
96  (void)content_type;
97  (void)data;
98  return HttpResponse({}, 200, CURLE_OK, "");
99  }
100 
101  HttpResponse post(const std::string &url, const Json::Value &data) override {
102  if (url.find("/devices") != std::string::npos || url.find("/director/ecus") != std::string::npos || url.empty()) {
103  Utils::writeFile((test_dir / "post.json").string(), data);
104  return HttpResponse(Utils::readFile("tests/test_data/cred.p12"), 200, CURLE_OK, "");
105  } else if (url.find("/events") != std::string::npos) {
106  return handle_event(url, data);
107  }
108  return HttpResponse("", 400, CURLE_OK, "");
109  }
110 
111  HttpResponse put(const std::string &url, const std::string &content_type, const std::string &data) override {
112  (void)url;
113  (void)content_type;
114  (void)data;
115  return HttpResponse({}, 200, CURLE_OK, "");
116  }
117 
118  HttpResponse put(const std::string &url, const Json::Value &data) override {
119  last_manifest = data;
120  return HttpResponse(url, 200, CURLE_OK, "");
121  }
122 
123  std::future<HttpResponse> downloadAsync(const std::string &url, curl_write_callback write_cb,
124  curl_xferinfo_callback progress_cb, void *userp, curl_off_t from,
125  CurlHandler *easyp) override {
126  (void)userp;
127  (void)from;
128  (void)easyp;
129  (void)progress_cb;
130 
131  std::cout << "URL requested: " << url << "\n";
132  const boost::filesystem::path path = meta_dir / url.substr(tls_server.size());
133  std::cout << "file served: " << path << "\n";
134 
135  std::promise<HttpResponse> resp_promise;
136  auto resp_future = resp_promise.get_future();
137  std::thread(
138  [path, write_cb, progress_cb, userp, url](std::promise<HttpResponse> promise) {
139  const std::string content = Utils::readFile(path.string());
140  for (unsigned int i = 0; i < content.size(); ++i) {
141  write_cb(const_cast<char *>(&content[i]), 1, 1, userp);
142  progress_cb(userp, 0, 0, 0, 0);
143  if (url.find("downloads/repo/targets/primary_firmware.txt") != std::string::npos) {
144  std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Simulate big file
145  }
146  }
147  promise.set_value(HttpResponse(content, 200, CURLE_OK, ""));
148  },
149  std::move(resp_promise))
150  .detach();
151 
152  return resp_future;
153  }
154 
155  HttpResponse download(const std::string &url, curl_write_callback write_cb, curl_xferinfo_callback progress_cb,
156  void *userp, curl_off_t from) override {
157  return downloadAsync(url, write_cb, progress_cb, userp, from, nullptr).get();
158  }
159 
160  const std::string tls_server = "https://tlsserver.com";
161  Json::Value last_manifest;
162 
163  protected:
164  boost::filesystem::path test_dir;
165  std::string flavor_;
166  boost::filesystem::path meta_dir;
167  TemporaryDirectory temp_meta_dir;
168 };
169 
170 #endif // HTTPFAKE_H_
HttpFake
Definition: httpfake.h:20
HttpInterface
Definition: httpinterface.h:38
data
General data structures.
Definition: types.h:217
HttpResponse
Definition: httpinterface.h:17
TemporaryDirectory
Definition: utils.h:82
MetaFake
Definition: metafake.h:14