1#include <mbgl/storage/default_file_source.hpp>
2#include <mbgl/storage/asset_file_source.hpp>
3#include <mbgl/storage/file_source_request.hpp>
4#include <mbgl/storage/local_file_source.hpp>
5#include <mbgl/storage/online_file_source.hpp>
6#include <mbgl/storage/offline_database.hpp>
7#include <mbgl/storage/offline_download.hpp>
8#include <mbgl/storage/resource_transform.hpp>
9
10#include <mbgl/util/platform.hpp>
11#include <mbgl/util/url.hpp>
12#include <mbgl/util/thread.hpp>
13#include <mbgl/util/work_request.hpp>
14#include <mbgl/util/stopwatch.hpp>
15
16#include <cassert>
17
18namespace mbgl {
19
20class DefaultFileSource::Impl {
21public:
22 Impl(std::shared_ptr<FileSource> assetFileSource_, std::string cachePath, uint64_t maximumCacheSize)
23 : assetFileSource(assetFileSource_)
24 , localFileSource(std::make_unique<LocalFileSource>())
25 , offlineDatabase(std::make_unique<OfflineDatabase>(args&: cachePath, args&: maximumCacheSize)) {
26 }
27
28 void setAPIBaseURL(const std::string& url) {
29 onlineFileSource.setAPIBaseURL(url);
30 }
31
32 std::string getAPIBaseURL() const{
33 return onlineFileSource.getAPIBaseURL();
34 }
35
36 void setAccessToken(const std::string& accessToken) {
37 onlineFileSource.setAccessToken(accessToken);
38 }
39
40 std::string getAccessToken() const {
41 return onlineFileSource.getAccessToken();
42 }
43
44 void setResourceTransform(optional<ActorRef<ResourceTransform>>&& transform) {
45 onlineFileSource.setResourceTransform(std::move(transform));
46 }
47
48 void listRegions(std::function<void (std::exception_ptr, optional<std::vector<OfflineRegion>>)> callback) {
49 try {
50 callback({}, offlineDatabase->listRegions());
51 } catch (...) {
52 callback(std::current_exception(), {});
53 }
54 }
55
56 void createRegion(const OfflineRegionDefinition& definition,
57 const OfflineRegionMetadata& metadata,
58 std::function<void (std::exception_ptr, optional<OfflineRegion>)> callback) {
59 try {
60 callback({}, offlineDatabase->createRegion(definition, metadata));
61 } catch (...) {
62 callback(std::current_exception(), {});
63 }
64 }
65
66 void updateMetadata(const int64_t regionID,
67 const OfflineRegionMetadata& metadata,
68 std::function<void (std::exception_ptr, optional<OfflineRegionMetadata>)> callback) {
69 try {
70 callback({}, offlineDatabase->updateMetadata(regionID, metadata));
71 } catch (...) {
72 callback(std::current_exception(), {});
73 }
74 }
75
76 void getRegionStatus(int64_t regionID, std::function<void (std::exception_ptr, optional<OfflineRegionStatus>)> callback) {
77 try {
78 callback({}, getDownload(regionID).getStatus());
79 } catch (...) {
80 callback(std::current_exception(), {});
81 }
82 }
83
84 void deleteRegion(OfflineRegion&& region, std::function<void (std::exception_ptr)> callback) {
85 try {
86 downloads.erase(x: region.getID());
87 offlineDatabase->deleteRegion(std::move(region));
88 callback({});
89 } catch (...) {
90 callback(std::current_exception());
91 }
92 }
93
94 void setRegionObserver(int64_t regionID, std::unique_ptr<OfflineRegionObserver> observer) {
95 getDownload(regionID).setObserver(std::move(observer));
96 }
97
98 void setRegionDownloadState(int64_t regionID, OfflineRegionDownloadState state) {
99 getDownload(regionID).setState(state);
100 }
101
102 void request(AsyncRequest* req, Resource resource, ActorRef<FileSourceRequest> ref) {
103 auto callback = [ref] (const Response& res) mutable {
104 ref.invoke(fn: &FileSourceRequest::setResponse, args: res);
105 };
106
107 if (AssetFileSource::acceptsURL(url: resource.url)) {
108 //Asset request
109 tasks[req] = assetFileSource->request(resource, callback);
110 } else if (LocalFileSource::acceptsURL(url: resource.url)) {
111 //Local file request
112 tasks[req] = localFileSource->request(resource, callback);
113 } else {
114 // Try the offline database
115 if (resource.hasLoadingMethod(method: Resource::LoadingMethod::Cache)) {
116 auto offlineResponse = offlineDatabase->get(resource);
117
118 if (resource.loadingMethod == Resource::LoadingMethod::CacheOnly) {
119 if (!offlineResponse) {
120 // Ensure there's always a response that we can send, so the caller knows that
121 // there's no optional data available in the cache, when it's the only place
122 // we're supposed to load from.
123 offlineResponse.emplace();
124 offlineResponse->noContent = true;
125 offlineResponse->error = std::make_unique<Response::Error>(
126 args: Response::Error::Reason::NotFound, args: "Not found in offline database");
127 } else if (!offlineResponse->isUsable()) {
128 // Don't return resources the server requested not to show when they're stale.
129 // Even if we can't directly use the response, we may still use it to send a
130 // conditional HTTP request, which is why we're saving it above.
131 offlineResponse->error = std::make_unique<Response::Error>(
132 args: Response::Error::Reason::NotFound, args: "Cached resource is unusable");
133 }
134 callback(*offlineResponse);
135 } else if (offlineResponse) {
136 // Copy over the fields so that we can use them when making a refresh request.
137 resource.priorModified = offlineResponse->modified;
138 resource.priorExpires = offlineResponse->expires;
139 resource.priorEtag = offlineResponse->etag;
140 resource.priorData = offlineResponse->data;
141
142 if (offlineResponse->isUsable()) {
143 callback(*offlineResponse);
144 }
145 }
146 }
147
148 // Get from the online file source
149 if (resource.hasLoadingMethod(method: Resource::LoadingMethod::Network)) {
150 MBGL_TIMING_START(watch);
151 tasks[req] = onlineFileSource.request(resource, [=] (Response onlineResponse) mutable {
152 this->offlineDatabase->put(resource, onlineResponse);
153 if (resource.kind == Resource::Kind::Tile) {
154 // onlineResponse.data will be null if data not modified
155 MBGL_TIMING_FINISH(watch,
156 " Action: " << "Requesting," <<
157 " URL: " << resource.url.c_str() <<
158 " Size: " << (onlineResponse.data != nullptr ? onlineResponse.data->size() : 0) << "B," <<
159 " Time")
160 }
161 callback(onlineResponse);
162 });
163 }
164 }
165 }
166
167 void cancel(AsyncRequest* req) {
168 tasks.erase(x: req);
169 }
170
171 void setOfflineMapboxTileCountLimit(uint64_t limit) {
172 offlineDatabase->setOfflineMapboxTileCountLimit(limit);
173 }
174
175 void setOnlineStatus(const bool status) {
176 onlineFileSource.setOnlineStatus(status);
177 }
178
179 void put(const Resource& resource, const Response& response) {
180 offlineDatabase->put(resource, response);
181 }
182
183private:
184 OfflineDownload& getDownload(int64_t regionID) {
185 auto it = downloads.find(x: regionID);
186 if (it != downloads.end()) {
187 return *it->second;
188 }
189 return *downloads.emplace(args&: regionID,
190 args: std::make_unique<OfflineDownload>(args&: regionID, args: offlineDatabase->getRegionDefinition(regionID), args&: *offlineDatabase, args&: onlineFileSource)).first->second;
191 }
192
193 // shared so that destruction is done on the creating thread
194 const std::shared_ptr<FileSource> assetFileSource;
195 const std::unique_ptr<FileSource> localFileSource;
196 std::unique_ptr<OfflineDatabase> offlineDatabase;
197 OnlineFileSource onlineFileSource;
198 std::unordered_map<AsyncRequest*, std::unique_ptr<AsyncRequest>> tasks;
199 std::unordered_map<int64_t, std::unique_ptr<OfflineDownload>> downloads;
200};
201
202DefaultFileSource::DefaultFileSource(const std::string& cachePath,
203 const std::string& assetRoot,
204 uint64_t maximumCacheSize)
205 : DefaultFileSource(cachePath, std::make_unique<AssetFileSource>(args: assetRoot), maximumCacheSize) {
206}
207
208DefaultFileSource::DefaultFileSource(const std::string& cachePath,
209 std::unique_ptr<FileSource>&& assetFileSource_,
210 uint64_t maximumCacheSize)
211 : assetFileSource(std::move(assetFileSource_))
212 , impl(std::make_unique<util::Thread<Impl>>(args: "DefaultFileSource", args: assetFileSource, args: cachePath, args&: maximumCacheSize)) {
213}
214
215DefaultFileSource::~DefaultFileSource() = default;
216
217void DefaultFileSource::setAPIBaseURL(const std::string& baseURL) {
218 impl->actor().invoke(fn: &Impl::setAPIBaseURL, args: baseURL);
219
220 {
221 std::lock_guard<std::mutex> lock(cachedBaseURLMutex);
222 cachedBaseURL = baseURL;
223 }
224}
225
226std::string DefaultFileSource::getAPIBaseURL() {
227 std::lock_guard<std::mutex> lock(cachedBaseURLMutex);
228 return cachedBaseURL;
229}
230
231void DefaultFileSource::setAccessToken(const std::string& accessToken) {
232 impl->actor().invoke(fn: &Impl::setAccessToken, args: accessToken);
233
234 {
235 std::lock_guard<std::mutex> lock(cachedAccessTokenMutex);
236 cachedAccessToken = accessToken;
237 }
238}
239
240std::string DefaultFileSource::getAccessToken() {
241 std::lock_guard<std::mutex> lock(cachedAccessTokenMutex);
242 return cachedAccessToken;
243}
244
245void DefaultFileSource::setResourceTransform(optional<ActorRef<ResourceTransform>>&& transform) {
246 impl->actor().invoke(fn: &Impl::setResourceTransform, args: std::move(transform));
247}
248
249std::unique_ptr<AsyncRequest> DefaultFileSource::request(const Resource& resource, Callback callback) {
250 auto req = std::make_unique<FileSourceRequest>(args: std::move(callback));
251
252 req->onCancel(callback: [fs = impl->actor(), req = req.get()] () mutable { fs.invoke(fn: &Impl::cancel, args&: req); });
253
254 impl->actor().invoke(fn: &Impl::request, args: req.get(), args: resource, args: req->actor());
255
256 return std::move(req);
257}
258
259void DefaultFileSource::listOfflineRegions(std::function<void (std::exception_ptr, optional<std::vector<OfflineRegion>>)> callback) {
260 impl->actor().invoke(fn: &Impl::listRegions, args&: callback);
261}
262
263void DefaultFileSource::createOfflineRegion(const OfflineRegionDefinition& definition,
264 const OfflineRegionMetadata& metadata,
265 std::function<void (std::exception_ptr, optional<OfflineRegion>)> callback) {
266 impl->actor().invoke(fn: &Impl::createRegion, args: definition, args: metadata, args&: callback);
267}
268
269void DefaultFileSource::updateOfflineMetadata(const int64_t regionID,
270 const OfflineRegionMetadata& metadata,
271 std::function<void (std::exception_ptr, optional<OfflineRegionMetadata>)> callback) {
272 impl->actor().invoke(fn: &Impl::updateMetadata, args: regionID, args: metadata, args&: callback);
273}
274
275void DefaultFileSource::deleteOfflineRegion(OfflineRegion&& region, std::function<void (std::exception_ptr)> callback) {
276 impl->actor().invoke(fn: &Impl::deleteRegion, args: std::move(region), args&: callback);
277}
278
279void DefaultFileSource::setOfflineRegionObserver(OfflineRegion& region, std::unique_ptr<OfflineRegionObserver> observer) {
280 impl->actor().invoke(fn: &Impl::setRegionObserver, args: region.getID(), args: std::move(observer));
281}
282
283void DefaultFileSource::setOfflineRegionDownloadState(OfflineRegion& region, OfflineRegionDownloadState state) {
284 impl->actor().invoke(fn: &Impl::setRegionDownloadState, args: region.getID(), args&: state);
285}
286
287void DefaultFileSource::getOfflineRegionStatus(OfflineRegion& region, std::function<void (std::exception_ptr, optional<OfflineRegionStatus>)> callback) const {
288 impl->actor().invoke(fn: &Impl::getRegionStatus, args: region.getID(), args&: callback);
289}
290
291void DefaultFileSource::setOfflineMapboxTileCountLimit(uint64_t limit) const {
292 impl->actor().invoke(fn: &Impl::setOfflineMapboxTileCountLimit, args&: limit);
293}
294
295void DefaultFileSource::pause() {
296 impl->pause();
297}
298
299void DefaultFileSource::resume() {
300 impl->resume();
301}
302
303// For testing only:
304
305void DefaultFileSource::setOnlineStatus(const bool status) {
306 impl->actor().invoke(fn: &Impl::setOnlineStatus, args: status);
307}
308
309void DefaultFileSource::put(const Resource& resource, const Response& response) {
310 impl->actor().invoke(fn: &Impl::put, args: resource, args: response);
311}
312
313} // namespace mbgl
314

source code of qtlocation/src/3rdparty/mapbox-gl-native/platform/default/default_file_source.cpp