summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
authorAlexander Shalamov <alexander.shalamov@mapbox.com>2019-10-04 15:02:01 +0300
committerAlexander Shalamov <alexander.shalamov@mapbox.com>2020-01-13 10:57:23 +0200
commit879c44f661c5eb762c93a721b657859a71aabfc7 (patch)
tree3a542777434e0d685811ce1c66b752dc9ca36e92 /platform
parent86a360534994cb37d3dddc53b71a2858d97419c3 (diff)
downloadqtlocation-mapboxgl-879c44f661c5eb762c93a721b657859a71aabfc7.tar.gz
[core] Modularize FileSource codebase (#15768)
* [core] Introduce FileSourceManager and use it for default platform impl - Add `FileSourceManager` interface that provides access to `FileSource` instances and means of registering / unregistering `FileSource` factories - Split `DefaultFileSource` into smaller parts - Add `DatabaseFileSource` interface and it's default implementation - Remove inter-dependencies between concrete `FileSource` classes * [build] Add files to next build system * [core] Add generic property setters / getters * [core] Remove setOnlineStatus from OnlineFileSource interface * [core] Hide threading implementation details from DatabaseFileSource interface * [core] Make DB file source methods virtual * [core] Add documentation for DatabaseFileSource and rename one method * [core] Use simple callback instead of ActorRef * [core] Remove ActorRef from OnlineFileSource public header * [core] Add callback to FileSource::forward async API * [core] Pass OfflineRegionDefinition by value * [core] Update tests to use modular file sources * [core] Update unit tests * [core] Update unit tests after rebase * [core] Backport low prio fix for cached requests * [core] Backport pack database * [core] Return removed factory from unRegisterFileSourceFactory * [core] Rename shadowed args in onlinefilesource * [core] Remove simple std::function callback aliases * [core] Expose online file source property keys in public header file * [test-runner] Add proxy file source test runner * [cache] Update mbgl-cache utility to use new file source * [metrics] Rebaseline binary size metrics * [offline] Update offline utility * [core] Update changelog
Diffstat (limited to 'platform')
-rw-r--r--platform/android/src/test/http_file_source_test_stub.cpp2
-rw-r--r--platform/darwin/filesource-files.json1
-rw-r--r--platform/default/filesource-files.json8
-rw-r--r--platform/default/include/mbgl/storage/offline_download.hpp6
-rw-r--r--platform/default/src/mbgl/storage/asset_file_source.cpp23
-rw-r--r--platform/default/src/mbgl/storage/database_file_source.cpp280
-rw-r--r--platform/default/src/mbgl/storage/default_file_source.cpp398
-rw-r--r--platform/default/src/mbgl/storage/file_source.cpp15
-rw-r--r--platform/default/src/mbgl/storage/file_source_manager.cpp43
-rw-r--r--platform/default/src/mbgl/storage/local_file_source.cpp24
-rw-r--r--platform/default/src/mbgl/storage/main_resource_loader.cpp223
-rw-r--r--platform/default/src/mbgl/storage/offline_download.cpp15
-rw-r--r--platform/default/src/mbgl/storage/online_file_source.cpp369
-rw-r--r--platform/glfw/main.cpp44
-rw-r--r--platform/linux/filesource-files.json1
15 files changed, 881 insertions, 571 deletions
diff --git a/platform/android/src/test/http_file_source_test_stub.cpp b/platform/android/src/test/http_file_source_test_stub.cpp
index 930a20907a..48e6a965ff 100644
--- a/platform/android/src/test/http_file_source_test_stub.cpp
+++ b/platform/android/src/test/http_file_source_test_stub.cpp
@@ -1,6 +1,6 @@
#include <mbgl/storage/http_file_source.hpp>
#include <mbgl/storage/resource.hpp>
-
+#include <mbgl/util/async_request.hpp>
#include <mbgl/util/async_task.hpp>
namespace mbgl {
diff --git a/platform/darwin/filesource-files.json b/platform/darwin/filesource-files.json
index 62043a0dcd..5d72549f73 100644
--- a/platform/darwin/filesource-files.json
+++ b/platform/darwin/filesource-files.json
@@ -5,6 +5,7 @@
"platform/darwin/src/MGLNetworkConfiguration.m",
"platform/darwin/src/http_file_source.mm",
"platform/default/src/mbgl/storage/file_source.cpp",
+ "platform/default/src/mbgl/storage/file_source_manager.cpp",
"platform/default/src/mbgl/storage/sqlite3.cpp"
],
"public_headers": {},
diff --git a/platform/default/filesource-files.json b/platform/default/filesource-files.json
index f61aa6a335..72e76670b8 100644
--- a/platform/default/filesource-files.json
+++ b/platform/default/filesource-files.json
@@ -2,19 +2,22 @@
"//": "This file can be edited manually and is the canonical source.",
"sources": [
"platform/default/src/mbgl/storage/asset_file_source.cpp",
- "platform/default/src/mbgl/storage/default_file_source.cpp",
+ "platform/default/src/mbgl/storage/database_file_source.cpp",
+ "platform/default/src/mbgl/storage/file_source_manager.cpp",
"platform/default/src/mbgl/storage/file_source_request.cpp",
"platform/default/src/mbgl/storage/local_file_request.cpp",
"platform/default/src/mbgl/storage/local_file_source.cpp",
+ "platform/default/src/mbgl/storage/main_resource_loader.cpp",
"platform/default/src/mbgl/storage/offline.cpp",
"platform/default/src/mbgl/storage/offline_database.cpp",
"platform/default/src/mbgl/storage/offline_download.cpp",
"platform/default/src/mbgl/storage/online_file_source.cpp"
],
"public_headers": {
- "mbgl/storage/default_file_source.hpp": "include/mbgl/storage/default_file_source.hpp",
+ "mbgl/storage/offline_file_source.hpp": "include/mbgl/storage/database_file_source.hpp",
"mbgl/storage/offline.hpp": "include/mbgl/storage/offline.hpp",
"mbgl/storage/online_file_source.hpp": "include/mbgl/storage/online_file_source.hpp",
+ "mbgl/storage/file_source_manager.hpp": "include/mbgl/storage/file_source_manager.hpp",
"mbgl/storage/file_source_request.hpp": "platform/default/include/mbgl/storage/file_source_request.hpp",
"mbgl/storage/local_file_request.hpp": "platform/default/include/mbgl/storage/local_file_request.hpp",
"mbgl/storage/merge_sideloaded.hpp": "platform/default/include/mbgl/storage/merge_sideloaded.hpp",
@@ -24,6 +27,7 @@
"mbgl/storage/sqlite3.hpp": "platform/default/include/mbgl/storage/sqlite3.hpp"
},
"private_headers": {
+ "mbgl/storage/main_resource_loader.hpp": "src/mbgl/storage/main_resource_loader.hpp",
"mbgl/storage/asset_file_source.hpp": "src/mbgl/storage/asset_file_source.hpp",
"mbgl/storage/http_file_source.hpp": "src/mbgl/storage/http_file_source.hpp",
"mbgl/storage/local_file_source.hpp": "src/mbgl/storage/local_file_source.hpp"
diff --git a/platform/default/include/mbgl/storage/offline_download.hpp b/platform/default/include/mbgl/storage/offline_download.hpp
index 53b42ae9d1..3a5159470a 100644
--- a/platform/default/include/mbgl/storage/offline_download.hpp
+++ b/platform/default/include/mbgl/storage/offline_download.hpp
@@ -1,8 +1,8 @@
#pragma once
+#include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/offline.hpp>
#include <mbgl/storage/resource.hpp>
-#include <mbgl/storage/online_file_source.hpp>
#include <list>
#include <unordered_set>
@@ -28,7 +28,7 @@ class Parser;
*/
class OfflineDownload {
public:
- OfflineDownload(int64_t id, OfflineRegionDefinition&&, OfflineDatabase& offline, OnlineFileSource& online);
+ OfflineDownload(int64_t id, OfflineRegionDefinition, OfflineDatabase& offline, FileSource& online);
~OfflineDownload();
void setObserver(std::unique_ptr<OfflineRegionObserver>);
@@ -53,7 +53,7 @@ private:
int64_t id;
OfflineRegionDefinition definition;
OfflineDatabase& offlineDatabase;
- OnlineFileSource& onlineFileSource;
+ FileSource& onlineFileSource;
OfflineRegionStatus status;
std::unique_ptr<OfflineRegionObserver> observer;
diff --git a/platform/default/src/mbgl/storage/asset_file_source.cpp b/platform/default/src/mbgl/storage/asset_file_source.cpp
index b14d73045f..7abd609b19 100644
--- a/platform/default/src/mbgl/storage/asset_file_source.cpp
+++ b/platform/default/src/mbgl/storage/asset_file_source.cpp
@@ -1,15 +1,17 @@
#include <mbgl/storage/asset_file_source.hpp>
#include <mbgl/storage/file_source_request.hpp>
#include <mbgl/storage/local_file_request.hpp>
+#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
+#include <mbgl/util/constants.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/thread.hpp>
#include <mbgl/util/url.hpp>
namespace {
-
-const std::string assetProtocol = "asset://";
-
+bool acceptsURL(const std::string& url) {
+ return 0 == url.rfind(mbgl::util::ASSET_PROTOCOL, 0);
+}
} // namespace
namespace mbgl {
@@ -30,7 +32,8 @@ public:
}
// Cut off the protocol and prefix with path.
- const auto path = root + "/" + mbgl::util::percentDecode(url.substr(assetProtocol.size()));
+ const auto path =
+ root + "/" + mbgl::util::percentDecode(url.substr(std::char_traits<char>::length(util::ASSET_PROTOCOL)));
requestLocalFile(path, std::move(req));
}
@@ -52,8 +55,16 @@ std::unique_ptr<AsyncRequest> AssetFileSource::request(const Resource& resource,
return std::move(req);
}
-bool AssetFileSource::acceptsURL(const std::string& url) {
- return 0 == url.rfind(assetProtocol, 0);
+bool AssetFileSource::canRequest(const Resource& resource) const {
+ return acceptsURL(resource.url);
+}
+
+void AssetFileSource::pause() {
+ impl->pause();
+}
+
+void AssetFileSource::resume() {
+ impl->resume();
}
} // namespace mbgl
diff --git a/platform/default/src/mbgl/storage/database_file_source.cpp b/platform/default/src/mbgl/storage/database_file_source.cpp
new file mode 100644
index 0000000000..ba8ba7b31d
--- /dev/null
+++ b/platform/default/src/mbgl/storage/database_file_source.cpp
@@ -0,0 +1,280 @@
+#include <mbgl/storage/database_file_source.hpp>
+#include <mbgl/storage/file_source_manager.hpp>
+#include <mbgl/storage/file_source_request.hpp>
+#include <mbgl/storage/offline_database.hpp>
+#include <mbgl/storage/offline_download.hpp>
+#include <mbgl/storage/resource_options.hpp>
+#include <mbgl/storage/response.hpp>
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/logging.hpp>
+#include <mbgl/util/thread.hpp>
+
+namespace mbgl {
+
+// For testing use only
+constexpr const char* READ_ONLY_MODE_KEY = "read-only-mode";
+
+class DatabaseFileSourceThread {
+public:
+ DatabaseFileSourceThread(std::shared_ptr<FileSource> onlineFileSource_, std::string cachePath)
+ : db(std::make_unique<OfflineDatabase>(cachePath)), onlineFileSource(onlineFileSource_) {}
+
+ void request(const Resource& resource, ActorRef<FileSourceRequest> req) {
+ auto offlineResponse = db->get(resource);
+ if (!offlineResponse) {
+ offlineResponse.emplace();
+ offlineResponse->noContent = true;
+ offlineResponse->error =
+ std::make_unique<Response::Error>(Response::Error::Reason::NotFound, "Not found in offline database");
+ } else if (!offlineResponse->isUsable()) {
+ offlineResponse->error =
+ std::make_unique<Response::Error>(Response::Error::Reason::NotFound, "Cached resource is unusable");
+ }
+ req.invoke(&FileSourceRequest::setResponse, *offlineResponse);
+ }
+
+ void setDatabasePath(const std::string& path, std::function<void()> callback) {
+ db->changePath(path);
+ if (callback) {
+ callback();
+ }
+ }
+
+ void forward(const Resource& resource, const Response& response, std::function<void()> callback) {
+ db->put(resource, response);
+ if (callback) {
+ callback();
+ }
+ }
+
+ void resetDatabase(std::function<void(std::exception_ptr)> callback) { callback(db->resetDatabase()); }
+
+ void packDatabase(std::function<void(std::exception_ptr)> callback) { callback(db->pack()); }
+
+ void runPackDatabaseAutomatically(bool autopack) { db->runPackDatabaseAutomatically(autopack); }
+
+ void put(const Resource& resource, const Response& response) { db->put(resource, response); }
+
+ void invalidateAmbientCache(std::function<void(std::exception_ptr)> callback) {
+ callback(db->invalidateAmbientCache());
+ }
+
+ void clearAmbientCache(std::function<void(std::exception_ptr)> callback) { callback(db->clearAmbientCache()); }
+
+ void setMaximumAmbientCacheSize(uint64_t size, std::function<void(std::exception_ptr)> callback) {
+ callback(db->setMaximumAmbientCacheSize(size));
+ }
+
+ void listRegions(std::function<void(expected<OfflineRegions, std::exception_ptr>)> callback) {
+ callback(db->listRegions());
+ }
+
+ void createRegion(const OfflineRegionDefinition& definition,
+ const OfflineRegionMetadata& metadata,
+ std::function<void(expected<OfflineRegion, std::exception_ptr>)> callback) {
+ callback(db->createRegion(definition, metadata));
+ }
+
+ void mergeOfflineRegions(const std::string& sideDatabasePath,
+ std::function<void(expected<OfflineRegions, std::exception_ptr>)> callback) {
+ callback(db->mergeDatabase(sideDatabasePath));
+ }
+
+ void updateMetadata(const int64_t regionID,
+ const OfflineRegionMetadata& metadata,
+ std::function<void(expected<OfflineRegionMetadata, std::exception_ptr>)> callback) {
+ callback(db->updateMetadata(regionID, metadata));
+ }
+
+ void getRegionStatus(int64_t regionID,
+ std::function<void(expected<OfflineRegionStatus, std::exception_ptr>)> callback) {
+ if (auto download = getDownload(regionID)) {
+ callback(download.value()->getStatus());
+ } else {
+ callback(unexpected<std::exception_ptr>(download.error()));
+ }
+ }
+
+ void deleteRegion(OfflineRegion region, std::function<void(std::exception_ptr)> callback) {
+ downloads.erase(region.getID());
+ callback(db->deleteRegion(std::move(region)));
+ }
+
+ void invalidateRegion(int64_t regionID, std::function<void(std::exception_ptr)> callback) {
+ callback(db->invalidateRegion(regionID));
+ }
+
+ void setRegionObserver(int64_t regionID, std::unique_ptr<OfflineRegionObserver> observer) {
+ if (auto download = getDownload(regionID)) {
+ download.value()->setObserver(std::move(observer));
+ }
+ }
+
+ void setRegionDownloadState(int64_t regionID, OfflineRegionDownloadState state) {
+ if (auto download = getDownload(regionID)) {
+ download.value()->setState(state);
+ }
+ }
+
+ void setOfflineMapboxTileCountLimit(uint64_t limit) { db->setOfflineMapboxTileCountLimit(limit); }
+
+ void reopenDatabaseReadOnlyForTesting() { db->reopenDatabaseReadOnlyForTesting(); }
+
+private:
+ expected<OfflineDownload*, std::exception_ptr> getDownload(int64_t regionID) {
+ if (!onlineFileSource) {
+ return unexpected<std::exception_ptr>(
+ std::make_exception_ptr(std::runtime_error("Network file source unavailable.")));
+ }
+
+ auto it = downloads.find(regionID);
+ if (it != downloads.end()) {
+ return it->second.get();
+ }
+ auto definition = db->getRegionDefinition(regionID);
+ if (!definition) {
+ return unexpected<std::exception_ptr>(definition.error());
+ }
+ auto download =
+ std::make_unique<OfflineDownload>(regionID, std::move(definition.value()), *db, *onlineFileSource);
+ return downloads.emplace(regionID, std::move(download)).first->second.get();
+ }
+
+ std::unique_ptr<OfflineDatabase> db;
+ std::unordered_map<int64_t, std::unique_ptr<OfflineDownload>> downloads;
+ std::shared_ptr<FileSource> onlineFileSource;
+};
+
+class DatabaseFileSource::Impl {
+public:
+ Impl(std::shared_ptr<FileSource> onlineFileSource, const std::string& cachePath)
+ : thread(std::make_unique<util::Thread<DatabaseFileSourceThread>>(
+ "DatabaseFileSource", std::move(onlineFileSource), cachePath)) {}
+
+ ActorRef<DatabaseFileSourceThread> actor() const { return thread->actor(); }
+
+private:
+ const std::unique_ptr<util::Thread<DatabaseFileSourceThread>> thread;
+};
+
+DatabaseFileSource::DatabaseFileSource(const ResourceOptions& options)
+ : impl(std::make_unique<Impl>(FileSourceManager::get()->getFileSource(FileSourceType::Network, options),
+ options.cachePath())) {}
+
+DatabaseFileSource::~DatabaseFileSource() = default;
+
+std::unique_ptr<AsyncRequest> DatabaseFileSource::request(const Resource& resource, Callback callback) {
+ auto req = std::make_unique<FileSourceRequest>(std::move(callback));
+ impl->actor().invoke(&DatabaseFileSourceThread::request, resource, req->actor());
+ return std::move(req);
+}
+
+void DatabaseFileSource::forward(const Resource& res, const Response& response, std::function<void()> callback) {
+ std::function<void()> wrapper;
+ if (callback) {
+ wrapper = Scheduler::GetCurrent()->bindOnce(std::move(callback));
+ }
+ impl->actor().invoke(&DatabaseFileSourceThread::forward, res, response, std::move(wrapper));
+}
+
+bool DatabaseFileSource::canRequest(const Resource& resource) const {
+ return resource.hasLoadingMethod(Resource::LoadingMethod::Cache) &&
+ resource.url.rfind(mbgl::util::ASSET_PROTOCOL, 0) == std::string::npos &&
+ resource.url.rfind(mbgl::util::FILE_PROTOCOL, 0) == std::string::npos;
+}
+
+void DatabaseFileSource::setDatabasePath(const std::string& path, std::function<void()> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::setDatabasePath, path, std::move(callback));
+}
+
+void DatabaseFileSource::resetDatabase(std::function<void(std::exception_ptr)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::resetDatabase, std::move(callback));
+}
+
+void DatabaseFileSource::packDatabase(std::function<void(std::exception_ptr)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::packDatabase, std::move(callback));
+}
+
+void DatabaseFileSource::runPackDatabaseAutomatically(bool autopack) {
+ impl->actor().invoke(&DatabaseFileSourceThread::runPackDatabaseAutomatically, autopack);
+}
+
+void DatabaseFileSource::put(const Resource& resource, const Response& response) {
+ impl->actor().invoke(&DatabaseFileSourceThread::put, resource, response);
+}
+
+void DatabaseFileSource::invalidateAmbientCache(std::function<void(std::exception_ptr)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::invalidateAmbientCache, std::move(callback));
+}
+
+void DatabaseFileSource::clearAmbientCache(std::function<void(std::exception_ptr)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::clearAmbientCache, std::move(callback));
+}
+
+void DatabaseFileSource::setMaximumAmbientCacheSize(uint64_t size, std::function<void(std::exception_ptr)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::setMaximumAmbientCacheSize, size, std::move(callback));
+}
+
+void DatabaseFileSource::listOfflineRegions(
+ std::function<void(expected<OfflineRegions, std::exception_ptr>)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::listRegions, callback);
+}
+
+void DatabaseFileSource::createOfflineRegion(
+ const OfflineRegionDefinition& definition,
+ const OfflineRegionMetadata& metadata,
+ std::function<void(expected<OfflineRegion, std::exception_ptr>)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::createRegion, definition, metadata, callback);
+}
+
+void DatabaseFileSource::mergeOfflineRegions(
+ const std::string& sideDatabasePath, std::function<void(expected<OfflineRegions, std::exception_ptr>)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::mergeOfflineRegions, sideDatabasePath, callback);
+}
+
+void DatabaseFileSource::updateOfflineMetadata(
+ const int64_t regionID,
+ const OfflineRegionMetadata& metadata,
+ std::function<void(expected<OfflineRegionMetadata, std::exception_ptr>)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::updateMetadata, regionID, metadata, callback);
+}
+
+void DatabaseFileSource::deleteOfflineRegion(OfflineRegion region, std::function<void(std::exception_ptr)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::deleteRegion, std::move(region), callback);
+}
+
+void DatabaseFileSource::invalidateOfflineRegion(OfflineRegion& region,
+ std::function<void(std::exception_ptr)> callback) {
+ impl->actor().invoke(&DatabaseFileSourceThread::invalidateRegion, region.getID(), callback);
+}
+
+void DatabaseFileSource::setOfflineRegionObserver(OfflineRegion& region,
+ std::unique_ptr<OfflineRegionObserver> observer) {
+ impl->actor().invoke(&DatabaseFileSourceThread::setRegionObserver, region.getID(), std::move(observer));
+}
+
+void DatabaseFileSource::setOfflineRegionDownloadState(OfflineRegion& region, OfflineRegionDownloadState state) {
+ impl->actor().invoke(&DatabaseFileSourceThread::setRegionDownloadState, region.getID(), state);
+}
+
+void DatabaseFileSource::getOfflineRegionStatus(
+ OfflineRegion& region, std::function<void(expected<OfflineRegionStatus, std::exception_ptr>)> callback) const {
+ impl->actor().invoke(&DatabaseFileSourceThread::getRegionStatus, region.getID(), callback);
+}
+
+void DatabaseFileSource::setOfflineMapboxTileCountLimit(uint64_t limit) const {
+ impl->actor().invoke(&DatabaseFileSourceThread::setOfflineMapboxTileCountLimit, limit);
+}
+
+void DatabaseFileSource::setProperty(const std::string& key, const mapbox::base::Value& value) {
+ if (key == READ_ONLY_MODE_KEY && value.getBool()) {
+ if (*value.getBool()) {
+ impl->actor().invoke(&DatabaseFileSourceThread::reopenDatabaseReadOnlyForTesting);
+ }
+ } else {
+ std::string message = "Resource provider does not support property " + key;
+ Log::Error(Event::General, message.c_str());
+ }
+}
+
+} // namespace mbgl
diff --git a/platform/default/src/mbgl/storage/default_file_source.cpp b/platform/default/src/mbgl/storage/default_file_source.cpp
deleted file mode 100644
index 2d96a5a9a2..0000000000
--- a/platform/default/src/mbgl/storage/default_file_source.cpp
+++ /dev/null
@@ -1,398 +0,0 @@
-#include <mbgl/storage/default_file_source.hpp>
-#include <mbgl/storage/asset_file_source.hpp>
-#include <mbgl/storage/file_source_request.hpp>
-#include <mbgl/storage/local_file_source.hpp>
-#include <mbgl/storage/online_file_source.hpp>
-#include <mbgl/storage/offline_database.hpp>
-#include <mbgl/storage/offline_download.hpp>
-#include <mbgl/storage/resource_transform.hpp>
-
-#include <mbgl/util/platform.hpp>
-#include <mbgl/util/url.hpp>
-#include <mbgl/util/thread.hpp>
-#include <mbgl/util/work_request.hpp>
-#include <mbgl/util/stopwatch.hpp>
-
-#include <cassert>
-#include <utility>
-
-namespace mbgl {
-
-class DefaultFileSource::Impl {
-public:
- Impl(std::shared_ptr<FileSource> assetFileSource_, std::string cachePath)
- : assetFileSource(std::move(assetFileSource_))
- , localFileSource(std::make_unique<LocalFileSource>())
- , offlineDatabase(std::make_unique<OfflineDatabase>(std::move(cachePath))) {
- }
-
- void setAPIBaseURL(const std::string& url) {
- onlineFileSource.setAPIBaseURL(url);
- }
-
- std::string getAPIBaseURL() const{
- return onlineFileSource.getAPIBaseURL();
- }
-
- void setAccessToken(const std::string& accessToken) {
- onlineFileSource.setAccessToken(accessToken);
- }
-
- std::string getAccessToken() const {
- return onlineFileSource.getAccessToken();
- }
-
- void setResourceTransform(optional<ActorRef<ResourceTransform>>&& transform) {
- onlineFileSource.setResourceTransform(std::move(transform));
- }
-
- void setResourceCachePath(const std::string& path, optional<ActorRef<PathChangeCallback>>&& callback) {
- offlineDatabase->changePath(path);
- if (callback) {
- callback->invoke(&PathChangeCallback::operator());
- }
- }
-
- void listRegions(std::function<void (expected<OfflineRegions, std::exception_ptr>)> callback) {
- callback(offlineDatabase->listRegions());
- }
-
- void createRegion(const OfflineRegionDefinition& definition,
- const OfflineRegionMetadata& metadata,
- std::function<void (expected<OfflineRegion, std::exception_ptr>)> callback) {
- callback(offlineDatabase->createRegion(definition, metadata));
- }
-
- void mergeOfflineRegions(const std::string& sideDatabasePath,
- std::function<void (expected<OfflineRegions, std::exception_ptr>)> callback) {
- callback(offlineDatabase->mergeDatabase(sideDatabasePath));
- }
-
- void updateMetadata(const int64_t regionID,
- const OfflineRegionMetadata& metadata,
- std::function<void (expected<OfflineRegionMetadata, std::exception_ptr>)> callback) {
- callback(offlineDatabase->updateMetadata(regionID, metadata));
- }
-
- void getRegionStatus(int64_t regionID, std::function<void (expected<OfflineRegionStatus, std::exception_ptr>)> callback) {
- if (auto download = getDownload(regionID)) {
- callback(download.value()->getStatus());
- } else {
- callback(unexpected<std::exception_ptr>(download.error()));
- }
- }
-
- void deleteRegion(OfflineRegion&& region, std::function<void(std::exception_ptr)> callback) {
- downloads.erase(region.getID());
- callback(offlineDatabase->deleteRegion(std::move(region)));
- }
-
- void invalidateRegion(int64_t regionID, std::function<void (std::exception_ptr)> callback) {
- callback(offlineDatabase->invalidateRegion(regionID));
- }
-
- void setRegionObserver(int64_t regionID, std::unique_ptr<OfflineRegionObserver> observer) {
- if (auto download = getDownload(regionID)) {
- download.value()->setObserver(std::move(observer));
- }
- }
-
- void setRegionDownloadState(int64_t regionID, OfflineRegionDownloadState state) {
- if (auto download = getDownload(regionID)) {
- download.value()->setState(state);
- }
- }
-
- void request(AsyncRequest* req, Resource resource, ActorRef<FileSourceRequest> ref) {
- auto callback = [ref] (const Response& res) {
- ref.invoke(&FileSourceRequest::setResponse, res);
- };
-
- if (AssetFileSource::acceptsURL(resource.url)) {
- //Asset request
- tasks[req] = assetFileSource->request(resource, callback);
- } else if (LocalFileSource::acceptsURL(resource.url)) {
- //Local file request
- tasks[req] = localFileSource->request(resource, callback);
- } else {
- // Try the offline database
- if (resource.hasLoadingMethod(Resource::LoadingMethod::Cache)) {
- auto offlineResponse = offlineDatabase->get(resource);
-
- if (resource.loadingMethod == Resource::LoadingMethod::CacheOnly) {
- if (!offlineResponse) {
- // Ensure there's always a response that we can send, so the caller knows that
- // there's no optional data available in the cache, when it's the only place
- // we're supposed to load from.
- offlineResponse.emplace();
- offlineResponse->noContent = true;
- offlineResponse->error = std::make_unique<Response::Error>(
- Response::Error::Reason::NotFound, "Not found in offline database");
- } else if (!offlineResponse->isUsable()) {
- // Don't return resources the server requested not to show when they're stale.
- // Even if we can't directly use the response, we may still use it to send a
- // conditional HTTP request, which is why we're saving it above.
- offlineResponse->error = std::make_unique<Response::Error>(
- Response::Error::Reason::NotFound, "Cached resource is unusable");
- }
- callback(*offlineResponse);
- } else if (offlineResponse) {
- // Copy over the fields so that we can use them when making a refresh request.
- resource.priorModified = offlineResponse->modified;
- resource.priorExpires = offlineResponse->expires;
- resource.priorEtag = offlineResponse->etag;
- resource.priorData = offlineResponse->data;
-
- if (offlineResponse->isUsable()) {
- callback(*offlineResponse);
- // Set the priority of existing resource to low if it's expired but usable.
- resource.setPriority(Resource::Priority::Low);
- }
- }
- }
-
- // Get from the online file source
- if (resource.hasLoadingMethod(Resource::LoadingMethod::Network)) {
- MBGL_TIMING_START(watch);
- tasks[req] = onlineFileSource.request(resource, [=] (Response onlineResponse) {
- this->offlineDatabase->put(resource, onlineResponse);
- if (resource.kind == Resource::Kind::Tile) {
- // onlineResponse.data will be null if data not modified
- MBGL_TIMING_FINISH(watch,
- " Action: " << "Requesting," <<
- " URL: " << resource.url.c_str() <<
- " Size: " << (onlineResponse.data != nullptr ? onlineResponse.data->size() : 0) << "B," <<
- " Time")
- }
- callback(onlineResponse);
- });
- }
- }
- }
-
- void cancel(AsyncRequest* req) {
- tasks.erase(req);
- }
-
- void setOfflineMapboxTileCountLimit(uint64_t limit) {
- offlineDatabase->setOfflineMapboxTileCountLimit(limit);
- }
-
- void setOnlineStatus(const bool status) {
- onlineFileSource.setOnlineStatus(status);
- }
-
- void reopenDatabaseReadOnlyForTesting() { offlineDatabase->reopenDatabaseReadOnlyForTesting(); }
-
- void setMaximumConcurrentRequests(uint32_t maximumConcurrentRequests_) {
- onlineFileSource.setMaximumConcurrentRequests(maximumConcurrentRequests_);
- }
-
- void put(const Resource& resource, const Response& response) {
- offlineDatabase->put(resource, response);
- }
-
- void resetDatabase(std::function<void (std::exception_ptr)> callback) {
- callback(offlineDatabase->resetDatabase());
- }
-
- void invalidateAmbientCache(std::function<void (std::exception_ptr)> callback) {
- callback(offlineDatabase->invalidateAmbientCache());
- }
-
- void clearAmbientCache(std::function<void (std::exception_ptr)> callback) {
- callback(offlineDatabase->clearAmbientCache());
- }
-
- void setMaximumAmbientCacheSize(uint64_t size, std::function<void (std::exception_ptr)> callback) {
- callback(offlineDatabase->setMaximumAmbientCacheSize(size));
- }
-
- void packDatabase(std::function<void(std::exception_ptr)> callback) { callback(offlineDatabase->pack()); }
-
- void runPackDatabaseAutomatically(bool autopack) { offlineDatabase->runPackDatabaseAutomatically(autopack); }
-
-private:
- expected<OfflineDownload*, std::exception_ptr> getDownload(int64_t regionID) {
- auto it = downloads.find(regionID);
- if (it != downloads.end()) {
- return it->second.get();
- }
- auto definition = offlineDatabase->getRegionDefinition(regionID);
- if (!definition) {
- return unexpected<std::exception_ptr>(definition.error());
- }
- auto download = std::make_unique<OfflineDownload>(regionID, std::move(definition.value()),
- *offlineDatabase, onlineFileSource);
- return downloads.emplace(regionID, std::move(download)).first->second.get();
- }
-
- // shared so that destruction is done on the creating thread
- const std::shared_ptr<FileSource> assetFileSource;
- const std::unique_ptr<FileSource> localFileSource;
- std::unique_ptr<OfflineDatabase> offlineDatabase;
- OnlineFileSource onlineFileSource;
- std::unordered_map<AsyncRequest*, std::unique_ptr<AsyncRequest>> tasks;
- std::unordered_map<int64_t, std::unique_ptr<OfflineDownload>> downloads;
-};
-
-DefaultFileSource::DefaultFileSource(const std::string& cachePath, const std::string& assetPath, bool supportCacheOnlyRequests_)
- : DefaultFileSource(cachePath, std::make_unique<AssetFileSource>(assetPath), supportCacheOnlyRequests_) {
-}
-
-DefaultFileSource::DefaultFileSource(const std::string& cachePath, std::unique_ptr<FileSource>&& assetFileSource_, bool supportCacheOnlyRequests_)
- : assetFileSource(std::move(assetFileSource_))
- , impl(std::make_unique<util::Thread<Impl>>("DefaultFileSource", assetFileSource, cachePath))
- , supportCacheOnlyRequests(supportCacheOnlyRequests_) {
-}
-
-DefaultFileSource::~DefaultFileSource() = default;
-
-bool DefaultFileSource::supportsCacheOnlyRequests() const {
- return supportCacheOnlyRequests;
-}
-
-void DefaultFileSource::setAPIBaseURL(const std::string& baseURL) {
- impl->actor().invoke(&Impl::setAPIBaseURL, baseURL);
-
- {
- std::lock_guard<std::mutex> lock(cachedBaseURLMutex);
- cachedBaseURL = baseURL;
- }
-}
-
-std::string DefaultFileSource::getAPIBaseURL() {
- std::lock_guard<std::mutex> lock(cachedBaseURLMutex);
- return cachedBaseURL;
-}
-
-void DefaultFileSource::setAccessToken(const std::string& accessToken) {
- impl->actor().invoke(&Impl::setAccessToken, accessToken);
-
- {
- std::lock_guard<std::mutex> lock(cachedAccessTokenMutex);
- cachedAccessToken = accessToken;
- }
-}
-
-std::string DefaultFileSource::getAccessToken() {
- std::lock_guard<std::mutex> lock(cachedAccessTokenMutex);
- return cachedAccessToken;
-}
-
-void DefaultFileSource::setResourceTransform(optional<ActorRef<ResourceTransform>>&& transform) {
- impl->actor().invoke(&Impl::setResourceTransform, std::move(transform));
-}
-
-void DefaultFileSource::setResourceCachePath(const std::string& path, optional<ActorRef<PathChangeCallback>>&& callback) {
- impl->actor().invoke(&Impl::setResourceCachePath, path, std::move(callback));
-}
-
-std::unique_ptr<AsyncRequest> DefaultFileSource::request(const Resource& resource, Callback callback) {
- auto req = std::make_unique<FileSourceRequest>(std::move(callback));
-
- req->onCancel([fs = impl->actor(), req = req.get()] () { fs.invoke(&Impl::cancel, req); });
-
- impl->actor().invoke(&Impl::request, req.get(), resource, req->actor());
-
- return std::move(req);
-}
-
-void DefaultFileSource::listOfflineRegions(std::function<void (expected<OfflineRegions, std::exception_ptr>)> callback) {
- impl->actor().invoke(&Impl::listRegions, callback);
-}
-
-void DefaultFileSource::createOfflineRegion(const OfflineRegionDefinition& definition,
- const OfflineRegionMetadata& metadata,
- std::function<void (expected<OfflineRegion, std::exception_ptr>)> callback) {
- impl->actor().invoke(&Impl::createRegion, definition, metadata, callback);
-}
-
-void DefaultFileSource::mergeOfflineRegions(const std::string& sideDatabasePath,
- std::function<void (expected<OfflineRegions, std::exception_ptr>)> callback) {
- impl->actor().invoke(&Impl::mergeOfflineRegions, sideDatabasePath, callback);
-}
-
-void DefaultFileSource::updateOfflineMetadata(const int64_t regionID,
- const OfflineRegionMetadata& metadata,
- std::function<void (expected<OfflineRegionMetadata,
- std::exception_ptr>)> callback) {
- impl->actor().invoke(&Impl::updateMetadata, regionID, metadata, callback);
-}
-
-void DefaultFileSource::deleteOfflineRegion(OfflineRegion&& region, std::function<void(std::exception_ptr)> callback) {
- impl->actor().invoke(&Impl::deleteRegion, std::move(region), callback);
-}
-
-void DefaultFileSource::invalidateOfflineRegion(OfflineRegion& region,
- std::function<void(std::exception_ptr)> callback) {
- impl->actor().invoke(&Impl::invalidateRegion, region.getID(), callback);
-}
-
-void DefaultFileSource::setOfflineRegionObserver(OfflineRegion& region, std::unique_ptr<OfflineRegionObserver> observer) {
- impl->actor().invoke(&Impl::setRegionObserver, region.getID(), std::move(observer));
-}
-
-void DefaultFileSource::setOfflineRegionDownloadState(OfflineRegion& region, OfflineRegionDownloadState state) {
- impl->actor().invoke(&Impl::setRegionDownloadState, region.getID(), state);
-}
-
-void DefaultFileSource::getOfflineRegionStatus(OfflineRegion& region, std::function<void (expected<OfflineRegionStatus, std::exception_ptr>)> callback) const {
- impl->actor().invoke(&Impl::getRegionStatus, region.getID(), callback);
-}
-
-void DefaultFileSource::setOfflineMapboxTileCountLimit(uint64_t limit) const {
- impl->actor().invoke(&Impl::setOfflineMapboxTileCountLimit, limit);
-}
-
-void DefaultFileSource::pause() {
- impl->pause();
-}
-
-void DefaultFileSource::resume() {
- impl->resume();
-}
-
-void DefaultFileSource::put(const Resource& resource, const Response& response) {
- impl->actor().invoke(&Impl::put, resource, response);
-}
-
-void DefaultFileSource::resetDatabase(std::function<void (std::exception_ptr)> callback) {
- impl->actor().invoke(&Impl::resetDatabase, std::move(callback));
-}
-
-void DefaultFileSource::packDatabase(std::function<void(std::exception_ptr)> callback) {
- impl->actor().invoke(&Impl::packDatabase, std::move(callback));
-}
-
-void DefaultFileSource::runPackDatabaseAutomatically(bool autopack) {
- impl->actor().invoke(&Impl::runPackDatabaseAutomatically, autopack);
-}
-
-void DefaultFileSource::invalidateAmbientCache(std::function<void (std::exception_ptr)> callback) {
- impl->actor().invoke(&Impl::invalidateAmbientCache, std::move(callback));
-}
-
-void DefaultFileSource::clearAmbientCache(std::function<void(std::exception_ptr)> callback) {
- impl->actor().invoke(&Impl::clearAmbientCache, std::move(callback));
-}
-
-void DefaultFileSource::setMaximumAmbientCacheSize(uint64_t size, std::function<void (std::exception_ptr)> callback) {
- impl->actor().invoke(&Impl::setMaximumAmbientCacheSize, size, std::move(callback));
-}
-
-// For testing only:
-
-void DefaultFileSource::setOnlineStatus(const bool status) {
- impl->actor().invoke(&Impl::setOnlineStatus, status);
-}
-
-void DefaultFileSource::reopenDatabaseReadOnlyForTesting() {
- impl->actor().invoke(&Impl::reopenDatabaseReadOnlyForTesting);
-}
-
-void DefaultFileSource::setMaximumConcurrentRequests(uint32_t maximumConcurrentRequests_) {
- impl->actor().invoke(&Impl::setMaximumConcurrentRequests, maximumConcurrentRequests_);
-}
-
-} // namespace mbgl
diff --git a/platform/default/src/mbgl/storage/file_source.cpp b/platform/default/src/mbgl/storage/file_source.cpp
deleted file mode 100644
index 4e800cc8f4..0000000000
--- a/platform/default/src/mbgl/storage/file_source.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <mbgl/storage/resource_options.hpp>
-#include <mbgl/storage/default_file_source.hpp>
-
-#include <memory>
-
-namespace mbgl {
-
-std::shared_ptr<FileSource> FileSource::createPlatformFileSource(const ResourceOptions& options) {
- auto fileSource = std::make_shared<DefaultFileSource>(options.cachePath(), options.assetPath(), options.supportsCacheOnlyRequests());
- fileSource->setAccessToken(options.accessToken());
- fileSource->setAPIBaseURL(options.baseURL());
- return fileSource;
-}
-
-} // namespace mbgl
diff --git a/platform/default/src/mbgl/storage/file_source_manager.cpp b/platform/default/src/mbgl/storage/file_source_manager.cpp
new file mode 100644
index 0000000000..2981096dac
--- /dev/null
+++ b/platform/default/src/mbgl/storage/file_source_manager.cpp
@@ -0,0 +1,43 @@
+#include <mbgl/storage/asset_file_source.hpp>
+#include <mbgl/storage/database_file_source.hpp>
+#include <mbgl/storage/file_source_manager.hpp>
+#include <mbgl/storage/local_file_source.hpp>
+#include <mbgl/storage/main_resource_loader.hpp>
+#include <mbgl/storage/online_file_source.hpp>
+#include <mbgl/storage/resource_options.hpp>
+
+namespace mbgl {
+
+class DefaultFileSourceManagerImpl final : public FileSourceManager {
+public:
+ DefaultFileSourceManagerImpl() {
+ registerFileSourceFactory(FileSourceType::ResourceLoader, [](const ResourceOptions& options) {
+ return std::make_unique<MainResourceLoader>(options);
+ });
+
+ registerFileSourceFactory(FileSourceType::Asset, [](const ResourceOptions& options) {
+ return std::make_unique<AssetFileSource>(options.assetPath());
+ });
+
+ registerFileSourceFactory(FileSourceType::Database, [](const ResourceOptions& options) {
+ return std::make_unique<DatabaseFileSource>(options);
+ });
+
+ registerFileSourceFactory(FileSourceType::FileSystem,
+ [](const ResourceOptions&) { return std::make_unique<LocalFileSource>(); });
+
+ registerFileSourceFactory(FileSourceType::Network, [](const ResourceOptions& options) {
+ auto networkSource = std::make_unique<OnlineFileSource>();
+ networkSource->setProperty(ACCESS_TOKEN_KEY, options.accessToken());
+ networkSource->setProperty(API_BASE_URL_KEY, options.baseURL());
+ return networkSource;
+ });
+ }
+};
+
+FileSourceManager* FileSourceManager::get() noexcept {
+ static DefaultFileSourceManagerImpl instance;
+ return &instance;
+}
+
+} // namespace mbgl
diff --git a/platform/default/src/mbgl/storage/local_file_source.cpp b/platform/default/src/mbgl/storage/local_file_source.cpp
index ca2eedc7ba..54f12baf79 100644
--- a/platform/default/src/mbgl/storage/local_file_source.cpp
+++ b/platform/default/src/mbgl/storage/local_file_source.cpp
@@ -1,15 +1,17 @@
-#include <mbgl/storage/local_file_source.hpp>
#include <mbgl/storage/file_source_request.hpp>
#include <mbgl/storage/local_file_request.hpp>
+#include <mbgl/storage/local_file_source.hpp>
+#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
+#include <mbgl/util/constants.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/thread.hpp>
#include <mbgl/util/url.hpp>
namespace {
-
-const std::string fileProtocol = "file://";
-
+bool acceptsURL(const std::string& url) {
+ return 0 == url.rfind(mbgl::util::FILE_PROTOCOL, 0);
+}
} // namespace
namespace mbgl {
@@ -28,7 +30,7 @@ public:
}
// Cut off the protocol and prefix with path.
- const auto path = mbgl::util::percentDecode(url.substr(fileProtocol.size()));
+ const auto path = mbgl::util::percentDecode(url.substr(std::char_traits<char>::length(util::FILE_PROTOCOL)));
requestLocalFile(path, std::move(req));
}
};
@@ -47,8 +49,16 @@ std::unique_ptr<AsyncRequest> LocalFileSource::request(const Resource& resource,
return std::move(req);
}
-bool LocalFileSource::acceptsURL(const std::string& url) {
- return 0 == url.rfind(fileProtocol, 0);
+bool LocalFileSource::canRequest(const Resource& resource) const {
+ return acceptsURL(resource.url);
+}
+
+void LocalFileSource::pause() {
+ impl->pause();
+}
+
+void LocalFileSource::resume() {
+ impl->resume();
}
} // namespace mbgl
diff --git a/platform/default/src/mbgl/storage/main_resource_loader.cpp b/platform/default/src/mbgl/storage/main_resource_loader.cpp
new file mode 100644
index 0000000000..e39ca8ef47
--- /dev/null
+++ b/platform/default/src/mbgl/storage/main_resource_loader.cpp
@@ -0,0 +1,223 @@
+#include <mbgl/actor/actor.hpp>
+#include <mbgl/actor/scheduler.hpp>
+#include <mbgl/storage/file_source_manager.hpp>
+#include <mbgl/storage/file_source_request.hpp>
+#include <mbgl/storage/main_resource_loader.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/resource_options.hpp>
+#include <mbgl/util/stopwatch.hpp>
+
+#include <cassert>
+
+namespace mbgl {
+
+class ResourceLoaderRequestor {
+public:
+ explicit ResourceLoaderRequestor(MainResourceLoader::Impl& impl_);
+ void request(AsyncRequest*, Resource, ActorRef<FileSourceRequest>);
+ void cancel(AsyncRequest*);
+ void pause();
+ void resume();
+
+private:
+ MainResourceLoader::Impl& impl;
+};
+
+class MainResourceLoader::Impl {
+public:
+ Impl(std::shared_ptr<FileSource> assetFileSource_,
+ std::shared_ptr<FileSource> databaseFileSource_,
+ std::shared_ptr<FileSource> localFileSource_,
+ std::shared_ptr<FileSource> onlineFileSource_)
+ : assetFileSource(std::move(assetFileSource_)),
+ databaseFileSource(std::move(databaseFileSource_)),
+ localFileSource(std::move(localFileSource_)),
+ onlineFileSource(std::move(onlineFileSource_)),
+ supportsCacheOnlyRequests_(bool(databaseFileSource)),
+ requestor(std::make_unique<Actor<ResourceLoaderRequestor>>(*Scheduler::GetCurrent(), *this)) {}
+
+ std::unique_ptr<AsyncRequest> request(const Resource& resource, Callback callback) {
+ auto req = std::make_unique<FileSourceRequest>(std::move(callback));
+ req->onCancel([actorRef = requestor->self(), req = req.get()]() {
+ actorRef.invoke(&ResourceLoaderRequestor::cancel, req);
+ });
+ requestor->self().invoke(&ResourceLoaderRequestor::request, req.get(), resource, req->actor());
+ return std::move(req);
+ }
+
+ bool canRequest(const Resource& resource) const {
+ return (assetFileSource && assetFileSource->canRequest(resource)) ||
+ (localFileSource && localFileSource->canRequest(resource)) ||
+ (databaseFileSource && databaseFileSource->canRequest(resource)) ||
+ (onlineFileSource && onlineFileSource->canRequest(resource));
+ }
+
+ bool supportsCacheOnlyRequests() const { return supportsCacheOnlyRequests_; }
+
+ void pause() { requestor->self().invoke(&ResourceLoaderRequestor::pause); }
+
+ void resume() { requestor->self().invoke(&ResourceLoaderRequestor::resume); }
+
+private:
+ void request(AsyncRequest* req, Resource resource, ActorRef<FileSourceRequest> ref) {
+ auto callback = [ref](const Response& res) { ref.invoke(&FileSourceRequest::setResponse, res); };
+
+ auto requestFromNetwork = [=](const Resource& res,
+ std::unique_ptr<AsyncRequest> parent) -> std::unique_ptr<AsyncRequest> {
+ if (!onlineFileSource || !onlineFileSource->canRequest(resource)) {
+ return parent;
+ }
+
+ // Keep parent request alive while chained request is being processed.
+ std::shared_ptr<AsyncRequest> parentKeepAlive = std::move(parent);
+
+ MBGL_TIMING_START(watch);
+ return onlineFileSource->request(res, [=, ptr = parentKeepAlive](Response response) {
+ if (databaseFileSource) {
+ databaseFileSource->forward(res, response);
+ }
+ if (res.kind == Resource::Kind::Tile) {
+ // onlineResponse.data will be null if data not modified
+ MBGL_TIMING_FINISH(watch,
+ " Action: "
+ << "Requesting,"
+ << " URL: " << res.url.c_str() << " Size: "
+ << (response.data != nullptr ? response.data->size() : 0) << "B,"
+ << " Time")
+ }
+ callback(response);
+ });
+ };
+
+ // Initial tasksSize is used to check whether any of
+ // the sources were able to request a resource.
+ const std::size_t tasksSize = tasks.size();
+
+ // Waterfall resource request processing and return early once resource was requested.
+ if (assetFileSource && assetFileSource->canRequest(resource)) {
+ // Asset request
+ tasks[req] = assetFileSource->request(resource, callback);
+ } else if (localFileSource && localFileSource->canRequest(resource)) {
+ // Local file request
+ tasks[req] = localFileSource->request(resource, callback);
+ } else if (databaseFileSource && databaseFileSource->canRequest(resource)) {
+ // Try cache only request if needed.
+ if (resource.loadingMethod == Resource::LoadingMethod::CacheOnly) {
+ tasks[req] = databaseFileSource->request(resource, callback);
+ } else {
+ // Cache request with fallback to network with cache control
+ tasks[req] = databaseFileSource->request(resource, [=](Response response) {
+ Resource res = resource;
+
+ // Resource is in the cache
+ if (!response.noContent) {
+ if (response.isUsable()) {
+ callback(response);
+ // Set the priority of existing resource to low if it's expired but usable.
+ res.setPriority(Resource::Priority::Low);
+ }
+
+ // Copy response fields for cache control request
+ res.priorModified = response.modified;
+ res.priorExpires = response.expires;
+ res.priorEtag = response.etag;
+ res.priorData = response.data;
+ }
+
+ tasks[req] = requestFromNetwork(res, std::move(tasks[req]));
+ });
+ }
+ } else if (auto networkReq = requestFromNetwork(resource, nullptr)) {
+ // Get from the online file source
+ tasks[req] = std::move(networkReq);
+ }
+
+ // If no new tasks were added, notify client that request cannot be processed.
+ if (tasks.size() == tasksSize) {
+ Response response;
+ response.noContent = true;
+ response.error =
+ std::make_unique<Response::Error>(Response::Error::Reason::Other, "Unsupported resource request.");
+ callback(response);
+ }
+ }
+
+ void pauseInternal() {
+ if (assetFileSource) assetFileSource->pause();
+ if (databaseFileSource) databaseFileSource->pause();
+ if (localFileSource) localFileSource->pause();
+ if (onlineFileSource) onlineFileSource->pause();
+ }
+
+ void resumeInternal() {
+ if (assetFileSource) assetFileSource->resume();
+ if (databaseFileSource) databaseFileSource->resume();
+ if (localFileSource) localFileSource->resume();
+ if (onlineFileSource) onlineFileSource->resume();
+ }
+
+ void cancel(AsyncRequest* req) {
+ assert(req);
+ tasks.erase(req);
+ }
+
+private:
+ friend class ResourceLoaderRequestor;
+ const std::shared_ptr<FileSource> assetFileSource;
+ const std::shared_ptr<FileSource> databaseFileSource;
+ const std::shared_ptr<FileSource> localFileSource;
+ const std::shared_ptr<FileSource> onlineFileSource;
+ const bool supportsCacheOnlyRequests_;
+ std::unique_ptr<Actor<ResourceLoaderRequestor>> requestor;
+ std::unordered_map<AsyncRequest*, std::unique_ptr<AsyncRequest>> tasks;
+};
+
+ResourceLoaderRequestor::ResourceLoaderRequestor(MainResourceLoader::Impl& impl_) : impl(impl_) {}
+
+void ResourceLoaderRequestor::request(AsyncRequest* req, Resource resource, ActorRef<FileSourceRequest> ref) {
+ assert(req);
+ impl.request(req, std::move(resource), std::move(ref));
+}
+
+void ResourceLoaderRequestor::cancel(AsyncRequest* req) {
+ assert(req);
+ impl.cancel(req);
+}
+
+void ResourceLoaderRequestor::pause() {
+ impl.pauseInternal();
+}
+
+void ResourceLoaderRequestor::resume() {
+ impl.resumeInternal();
+}
+
+MainResourceLoader::MainResourceLoader(const ResourceOptions& options)
+ : impl(std::make_unique<Impl>(FileSourceManager::get()->getFileSource(FileSourceType::Asset, options),
+ FileSourceManager::get()->getFileSource(FileSourceType::Database, options),
+ FileSourceManager::get()->getFileSource(FileSourceType::FileSystem, options),
+ FileSourceManager::get()->getFileSource(FileSourceType::Network, options))) {}
+
+MainResourceLoader::~MainResourceLoader() = default;
+
+bool MainResourceLoader::supportsCacheOnlyRequests() const {
+ return impl->supportsCacheOnlyRequests();
+}
+
+std::unique_ptr<AsyncRequest> MainResourceLoader::request(const Resource& resource, Callback callback) {
+ return impl->request(resource, std::move(callback));
+}
+
+bool MainResourceLoader::canRequest(const Resource& resource) const {
+ return impl->canRequest(resource);
+}
+
+void MainResourceLoader::pause() {
+ impl->pause();
+}
+
+void MainResourceLoader::resume() {
+ impl->resume();
+}
+
+} // namespace mbgl
diff --git a/platform/default/src/mbgl/storage/offline_download.cpp b/platform/default/src/mbgl/storage/offline_download.cpp
index 98eb1d3884..32fcb4f625 100644
--- a/platform/default/src/mbgl/storage/offline_download.cpp
+++ b/platform/default/src/mbgl/storage/offline_download.cpp
@@ -1,4 +1,3 @@
-#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/storage/offline_database.hpp>
#include <mbgl/storage/offline_download.hpp>
#include <mbgl/storage/resource.hpp>
@@ -90,11 +89,11 @@ uint64_t tileCount(const OfflineRegionDefinition& definition, style::SourceType
// OfflineDownload
OfflineDownload::OfflineDownload(int64_t id_,
- OfflineRegionDefinition&& definition_,
+ OfflineRegionDefinition definition_,
OfflineDatabase& offlineDatabase_,
- OnlineFileSource& onlineFileSource_)
+ FileSource& onlineFileSource_)
: id(id_),
- definition(definition_),
+ definition(std::move(definition_)),
offlineDatabase(offlineDatabase_),
onlineFileSource(onlineFileSource_) {
setObserver(nullptr);
@@ -369,7 +368,13 @@ void OfflineDownload::continueDownload() {
if (resourcesToBeMarkedAsUsed.size() >= kMarkBatchSize) markPendingUsedResources();
- while (!resourcesRemaining.empty() && requests.size() < onlineFileSource.getMaximumConcurrentRequests()) {
+ uint32_t maxConcurrentRequests = util::DEFAULT_MAXIMUM_CONCURRENT_REQUESTS;
+ auto value = onlineFileSource.getProperty("max-concurrent-requests");
+ if (uint64_t* maxRequests = value.getUint()) {
+ maxConcurrentRequests = static_cast<uint32_t>(*maxRequests);
+ }
+
+ while (!resourcesRemaining.empty() && requests.size() < maxConcurrentRequests) {
ensureResource(std::move(resourcesRemaining.front()));
resourcesRemaining.pop_front();
}
diff --git a/platform/default/src/mbgl/storage/online_file_source.cpp b/platform/default/src/mbgl/storage/online_file_source.cpp
index f4225bdf3f..0f5b438e1d 100644
--- a/platform/default/src/mbgl/storage/online_file_source.cpp
+++ b/platform/default/src/mbgl/storage/online_file_source.cpp
@@ -2,52 +2,59 @@
#include <mbgl/storage/http_file_source.hpp>
#include <mbgl/storage/network_status.hpp>
+#include <mbgl/storage/file_source_request.hpp>
#include <mbgl/storage/resource_transform.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/actor/mailbox.hpp>
+#include <mbgl/util/async_task.hpp>
+#include <mbgl/util/chrono.hpp>
#include <mbgl/util/constants.hpp>
-#include <mbgl/util/mapbox.hpp>
#include <mbgl/util/exception.hpp>
-#include <mbgl/util/chrono.hpp>
-#include <mbgl/util/async_task.hpp>
+#include <mbgl/util/http_timeout.hpp>
+#include <mbgl/util/mapbox.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/thread.hpp>
#include <mbgl/util/timer.hpp>
-#include <mbgl/util/http_timeout.hpp>
#include <algorithm>
#include <cassert>
#include <list>
-#include <unordered_set>
+#include <map>
#include <unordered_map>
+#include <unordered_set>
namespace mbgl {
-static uint32_t DEFAULT_MAXIMUM_CONCURRENT_REQUESTS = 20;
+// For testing only
+constexpr const char* ONLINE_STATUS_KEY = "online-status";
-class OnlineFileRequest : public AsyncRequest {
-public:
- using Callback = std::function<void (Response)>;
+class OnlineFileSourceThread;
- OnlineFileRequest(Resource, Callback, OnlineFileSource::Impl&);
- ~OnlineFileRequest() override;
+struct OnlineFileRequest {
+ using Callback = std::function<void(Response)>;
+
+ OnlineFileRequest(Resource resource_, Callback callback_, OnlineFileSourceThread& impl_);
+ ~OnlineFileRequest();
void networkIsReachableAgain();
void schedule();
void schedule(optional<Timestamp> expires);
void completed(Response);
- void setTransformedURL(const std::string&& url);
+ void setTransformedURL(const std::string& url);
ActorRef<OnlineFileRequest> actor();
+ void onCancel(std::function<void()>);
- OnlineFileSource::Impl& impl;
+ OnlineFileSourceThread& impl;
Resource resource;
std::unique_ptr<AsyncRequest> request;
util::Timer timer;
Callback callback;
+ std::function<void()> cancelCallback = nullptr;
std::shared_ptr<Mailbox> mailbox;
// Counts the number of times a response was already expired when received. We're using
@@ -62,101 +69,99 @@ public:
optional<Timestamp> retryAfter;
};
-class OnlineFileSource::Impl {
+class OnlineFileSourceThread {
public:
- Impl() {
+ OnlineFileSourceThread() {
NetworkStatus::Subscribe(&reachability);
- setMaximumConcurrentRequests(DEFAULT_MAXIMUM_CONCURRENT_REQUESTS);
+ setMaximumConcurrentRequests(util::DEFAULT_MAXIMUM_CONCURRENT_REQUESTS);
+ }
+
+ ~OnlineFileSourceThread() { NetworkStatus::Unsubscribe(&reachability); }
+
+ void request(AsyncRequest* req, Resource resource, ActorRef<FileSourceRequest> ref) {
+ auto callback = [ref](const Response& res) { ref.invoke(&FileSourceRequest::setResponse, res); };
+ tasks[req] = std::make_unique<OnlineFileRequest>(std::move(resource), std::move(callback), *this);
}
- ~Impl() {
- NetworkStatus::Unsubscribe(&reachability);
+ void cancel(AsyncRequest* req) {
+ auto it = tasks.find(req);
+ assert(it != tasks.end());
+ remove(it->second.get());
+ tasks.erase(it);
}
- void add(OnlineFileRequest* request) {
- allRequests.insert(request);
+ void add(OnlineFileRequest* req) {
+ allRequests.insert(req);
if (resourceTransform) {
// Request the ResourceTransform actor a new url and replace the resource url with the
// transformed one before proceeding to schedule the request.
- resourceTransform->invoke(&ResourceTransform::transform,
- request->resource.kind,
- std::move(request->resource.url),
- [ref = request->actor()](const std::string&& url) {
- ref.invoke(&OnlineFileRequest::setTransformedURL, url);
- });
+ resourceTransform.transform(
+ req->resource.kind, req->resource.url, [ref = req->actor()](const std::string& url) {
+ ref.invoke(&OnlineFileRequest::setTransformedURL, url);
+ });
} else {
- request->schedule();
+ req->schedule();
}
}
- void remove(OnlineFileRequest* request) {
- allRequests.erase(request);
- if (activeRequests.erase(request)) {
+ void remove(OnlineFileRequest* req) {
+ allRequests.erase(req);
+ if (activeRequests.erase(req)) {
activatePendingRequest();
} else {
- pendingRequests.remove(request);
+ pendingRequests.remove(req);
}
}
- void activateOrQueueRequest(OnlineFileRequest* request) {
- assert(allRequests.find(request) != allRequests.end());
- assert(activeRequests.find(request) == activeRequests.end());
- assert(!request->request);
+ void activateOrQueueRequest(OnlineFileRequest* req) {
+ assert(allRequests.find(req) != allRequests.end());
+ assert(activeRequests.find(req) == activeRequests.end());
+ assert(!req->request);
if (activeRequests.size() >= getMaximumConcurrentRequests()) {
- queueRequest(request);
+ queueRequest(req);
} else {
- activateRequest(request);
+ activateRequest(req);
}
}
- void queueRequest(OnlineFileRequest* request) {
- pendingRequests.insert(request);
- }
+ void queueRequest(OnlineFileRequest* req) { pendingRequests.insert(req); }
- void activateRequest(OnlineFileRequest* request) {
+ void activateRequest(OnlineFileRequest* req) {
auto callback = [=](Response response) {
- activeRequests.erase(request);
- request->request.reset();
- request->completed(response);
+ activeRequests.erase(req);
+ req->request.reset();
+ req->completed(response);
activatePendingRequest();
};
- activeRequests.insert(request);
+ activeRequests.insert(req);
if (online) {
- request->request = httpFileSource.request(request->resource, callback);
+ req->request = httpFileSource.request(req->resource, callback);
} else {
Response response;
response.error = std::make_unique<Response::Error>(Response::Error::Reason::Connection,
"Online connectivity is disabled.");
callback(response);
}
-
}
void activatePendingRequest() {
+ auto req = pendingRequests.pop();
- auto request = pendingRequests.pop();
-
- if (request) {
- activateRequest(*request);
+ if (req) {
+ activateRequest(*req);
}
}
- bool isPending(OnlineFileRequest* request) {
- return pendingRequests.contains(request);
- }
+ bool isPending(OnlineFileRequest* req) { return pendingRequests.contains(req); }
- bool isActive(OnlineFileRequest* request) {
- return activeRequests.find(request) != activeRequests.end();
- }
+ bool isActive(OnlineFileRequest* req) { return activeRequests.find(req) != activeRequests.end(); }
- void setResourceTransform(optional<ActorRef<ResourceTransform>>&& transform) {
- resourceTransform = std::move(transform);
- }
+ void setResourceTransform(ResourceTransform transform) { resourceTransform = std::move(transform); }
- void setOnlineStatus(const bool status) {
+ void setOnlineStatus(bool status) {
online = status;
if (online) {
networkIsReachableAgain();
@@ -171,20 +176,27 @@ public:
maximumConcurrentRequests = maximumConcurrentRequests_;
}
+ void setAPIBaseURL(const std::string& t) { apiBaseURL = t; }
+ std::string getAPIBaseURL() const { return apiBaseURL; }
+
+ void setAccessToken(const std::string& t) { accessToken = t; }
+ std::string getAccessToken() const { return accessToken; }
+
private:
+ friend struct OnlineFileRequest;
void networkIsReachableAgain() {
// Notify regular priority requests.
- for (auto& request : allRequests) {
- if (request->resource.priority == Resource::Priority::Regular) {
- request->networkIsReachableAgain();
+ for (auto& req : allRequests) {
+ if (req->resource.priority == Resource::Priority::Regular) {
+ req->networkIsReachableAgain();
}
}
// Notify low priority requests.
- for (auto& request : allRequests) {
- if (request->resource.priority == Resource::Priority::Low) {
- request->networkIsReachableAgain();
+ for (auto& req : allRequests) {
+ if (req->resource.priority == Resource::Priority::Low) {
+ req->networkIsReachableAgain();
}
}
}
@@ -231,7 +243,6 @@ private:
}
}
-
optional<OnlineFileRequest*> pop() {
if (queue.empty()) {
return optional<OnlineFileRequest*>();
@@ -252,7 +263,7 @@ private:
};
- optional<ActorRef<ResourceTransform>> resourceTransform;
+ ResourceTransform resourceTransform;
/**
* The lifetime of a request is:
@@ -274,56 +285,99 @@ private:
bool online = true;
uint32_t maximumConcurrentRequests;
HTTPFileSource httpFileSource;
- util::AsyncTask reachability { std::bind(&Impl::networkIsReachableAgain, this) };
+ util::AsyncTask reachability{std::bind(&OnlineFileSourceThread::networkIsReachableAgain, this)};
+ std::string accessToken;
+ std::string apiBaseURL = mbgl::util::API_BASE_URL;
+ std::map<AsyncRequest*, std::unique_ptr<OnlineFileRequest>> tasks;
};
-OnlineFileSource::OnlineFileSource()
- : impl(std::make_unique<Impl>()) {
-}
+class OnlineFileSource::Impl {
+public:
+ Impl() : thread(std::make_unique<util::Thread<OnlineFileSourceThread>>("OnlineFileSource")) {}
-OnlineFileSource::~OnlineFileSource() = default;
+ std::unique_ptr<AsyncRequest> request(Callback callback, Resource res) {
+ auto req = std::make_unique<FileSourceRequest>(std::move(callback));
+ req->onCancel(
+ [actorRef = thread->actor(), req = req.get()]() { actorRef.invoke(&OnlineFileSourceThread::cancel, req); });
+ thread->actor().invoke(&OnlineFileSourceThread::request, req.get(), std::move(res), req->actor());
+ return std::move(req);
+ }
-std::unique_ptr<AsyncRequest> OnlineFileSource::request(const Resource& resource, Callback callback) {
- Resource res = resource;
+ void pause() { thread->pause(); }
- switch (resource.kind) {
- case Resource::Kind::Unknown:
- case Resource::Kind::Image:
- break;
+ void resume() { thread->resume(); }
+
+ void setResourceTransform(ResourceTransform transform) {
+ thread->actor().invoke(&OnlineFileSourceThread::setResourceTransform, std::move(transform));
+ }
+
+ void setOnlineStatus(bool status) { thread->actor().invoke(&OnlineFileSourceThread::setOnlineStatus, status); }
- case Resource::Kind::Style:
- res.url = mbgl::util::mapbox::normalizeStyleURL(apiBaseURL, resource.url, accessToken);
- break;
+ void setAPIBaseURL(const mapbox::base::Value& value) {
+ if (auto* baseURL = value.getString()) {
+ thread->actor().invoke(&OnlineFileSourceThread::setAPIBaseURL, *baseURL);
+ {
+ std::lock_guard<std::mutex> lock(cachedBaseURLMutex);
+ cachedBaseURL = *baseURL;
+ }
+ } else {
+ Log::Error(Event::General, "Invalid api-base-url property value type.");
+ }
+ }
- case Resource::Kind::Source:
- res.url = util::mapbox::normalizeSourceURL(apiBaseURL, resource.url, accessToken);
- break;
+ std::string getAPIBaseURL() const {
+ std::lock_guard<std::mutex> lock(cachedBaseURLMutex);
+ return cachedBaseURL;
+ }
- case Resource::Kind::Glyphs:
- res.url = util::mapbox::normalizeGlyphsURL(apiBaseURL, resource.url, accessToken);
- break;
+ void setMaximumConcurrentRequests(const mapbox::base::Value& value) {
+ if (auto* maximumConcurrentRequests = value.getUint()) {
+ assert(*maximumConcurrentRequests < std::numeric_limits<uint32_t>::max());
+ const uint32_t maxConcurretnRequests = static_cast<uint32_t>(*maximumConcurrentRequests);
+ thread->actor().invoke(&OnlineFileSourceThread::setMaximumConcurrentRequests, maxConcurretnRequests);
+ {
+ std::lock_guard<std::mutex> lock(maximumConcurrentRequestsMutex);
+ cachedMaximumConcurrentRequests = maxConcurretnRequests;
+ }
+ } else {
+ Log::Error(Event::General, "Invalid max-concurrent-requests property value type.");
+ }
+ }
- case Resource::Kind::SpriteImage:
- case Resource::Kind::SpriteJSON:
- res.url = util::mapbox::normalizeSpriteURL(apiBaseURL, resource.url, accessToken);
- break;
+ uint32_t getMaximumConcurrentRequests() const {
+ std::lock_guard<std::mutex> lock(maximumConcurrentRequestsMutex);
+ return cachedMaximumConcurrentRequests;
+ }
- case Resource::Kind::Tile:
- res.url = util::mapbox::normalizeTileURL(apiBaseURL, resource.url, accessToken);
- break;
+ void setAccessToken(const mapbox::base::Value& value) {
+ if (auto* accessToken = value.getString()) {
+ thread->actor().invoke(&OnlineFileSourceThread::setAccessToken, *accessToken);
+ {
+ std::lock_guard<std::mutex> lock(cachedAccessTokenMutex);
+ cachedAccessToken = *accessToken;
+ }
+ } else {
+ Log::Error(Event::General, "Invalid access-token property value type.");
+ }
}
- return std::make_unique<OnlineFileRequest>(std::move(res), std::move(callback), *impl);
-}
+ std::string getAccessToken() const {
+ std::lock_guard<std::mutex> lock(cachedAccessTokenMutex);
+ return cachedAccessToken;
+ }
-void OnlineFileSource::setResourceTransform(optional<ActorRef<ResourceTransform>>&& transform) {
- impl->setResourceTransform(std::move(transform));
-}
+private:
+ mutable std::mutex cachedAccessTokenMutex;
+ std::string cachedAccessToken;
+ mutable std::mutex cachedBaseURLMutex;
+ std::string cachedBaseURL = util::API_BASE_URL;
+ mutable std::mutex maximumConcurrentRequestsMutex;
+ uint32_t cachedMaximumConcurrentRequests = util::DEFAULT_MAXIMUM_CONCURRENT_REQUESTS;
+ const std::unique_ptr<util::Thread<OnlineFileSourceThread>> thread;
+};
-OnlineFileRequest::OnlineFileRequest(Resource resource_, Callback callback_, OnlineFileSource::Impl& impl_)
- : impl(impl_),
- resource(std::move(resource_)),
- callback(std::move(callback_)) {
+OnlineFileRequest::OnlineFileRequest(Resource resource_, Callback callback_, OnlineFileSourceThread& impl_)
+ : impl(impl_), resource(std::move(resource_)), callback(std::move(callback_)) {
impl.add(this);
}
@@ -337,12 +391,12 @@ void OnlineFileRequest::schedule() {
}
OnlineFileRequest::~OnlineFileRequest() {
- impl.remove(this);
+ if (mailbox) {
+ mailbox->close();
+ }
}
-Timestamp interpolateExpiration(const Timestamp& current,
- optional<Timestamp> prior,
- bool& expired) {
+Timestamp interpolateExpiration(const Timestamp& current, optional<Timestamp> prior, bool& expired) {
auto now = util::now();
if (current > now) {
return current;
@@ -383,9 +437,8 @@ void OnlineFileRequest::schedule(optional<Timestamp> expires) {
// If we're not being asked for a forced refresh, calculate a timeout that depends on how many
// consecutive errors we've encountered, and on the expiration time, if present.
- Duration timeout = std::min(
- http::errorRetryTimeout(failedRequestReason, failedRequests, retryAfter),
- http::expirationTimeout(expires, expiredRequests));
+ Duration timeout = std::min(http::errorRetryTimeout(failedRequestReason, failedRequests, retryAfter),
+ http::expirationTimeout(expires, expiredRequests));
if (timeout == Duration::max()) {
return;
@@ -469,7 +522,7 @@ void OnlineFileRequest::networkIsReachableAgain() {
}
}
-void OnlineFileRequest::setTransformedURL(const std::string&& url) {
+void OnlineFileRequest::setTransformedURL(const std::string& url) {
resource.url = url;
schedule();
}
@@ -484,19 +537,95 @@ ActorRef<OnlineFileRequest> OnlineFileRequest::actor() {
return ActorRef<OnlineFileRequest>(*this, mailbox);
}
-void OnlineFileSource::setMaximumConcurrentRequests(uint32_t maximumConcurrentRequests_) {
- impl->setMaximumConcurrentRequests(maximumConcurrentRequests_);
+void OnlineFileRequest::onCancel(std::function<void()> callback_) {
+ cancelCallback = std::move(callback_);
+}
+
+OnlineFileSource::OnlineFileSource() : impl(std::make_unique<Impl>()) {}
+
+OnlineFileSource::~OnlineFileSource() = default;
+
+std::unique_ptr<AsyncRequest> OnlineFileSource::request(const Resource& resource, Callback callback) {
+ Resource res = resource;
+
+ switch (resource.kind) {
+ case Resource::Kind::Unknown:
+ case Resource::Kind::Image:
+ break;
+
+ case Resource::Kind::Style:
+ res.url =
+ mbgl::util::mapbox::normalizeStyleURL(impl->getAPIBaseURL(), resource.url, impl->getAccessToken());
+ break;
+
+ case Resource::Kind::Source:
+ res.url = util::mapbox::normalizeSourceURL(impl->getAPIBaseURL(), resource.url, impl->getAccessToken());
+ break;
+
+ case Resource::Kind::Glyphs:
+ res.url = util::mapbox::normalizeGlyphsURL(impl->getAPIBaseURL(), resource.url, impl->getAccessToken());
+ break;
+
+ case Resource::Kind::SpriteImage:
+ case Resource::Kind::SpriteJSON:
+ res.url = util::mapbox::normalizeSpriteURL(impl->getAPIBaseURL(), resource.url, impl->getAccessToken());
+ break;
+
+ case Resource::Kind::Tile:
+ res.url = util::mapbox::normalizeTileURL(impl->getAPIBaseURL(), resource.url, impl->getAccessToken());
+ break;
+ }
+
+ return impl->request(std::move(callback), std::move(res));
+}
+
+bool OnlineFileSource::canRequest(const Resource& resource) const {
+ return resource.hasLoadingMethod(Resource::LoadingMethod::Network) &&
+ resource.url.rfind(mbgl::util::ASSET_PROTOCOL, 0) == std::string::npos &&
+ resource.url.rfind(mbgl::util::FILE_PROTOCOL, 0) == std::string::npos;
}
-uint32_t OnlineFileSource::getMaximumConcurrentRequests() const {
- return impl->getMaximumConcurrentRequests();
+void OnlineFileSource::pause() {
+ impl->pause();
}
+void OnlineFileSource::resume() {
+ impl->resume();
+}
+
+void OnlineFileSource::setProperty(const std::string& key, const mapbox::base::Value& value) {
+ if (key == ACCESS_TOKEN_KEY) {
+ impl->setAccessToken(value);
+ } else if (key == API_BASE_URL_KEY) {
+ impl->setAPIBaseURL(value);
+ } else if (key == MAX_CONCURRENT_REQUESTS_KEY) {
+ impl->setMaximumConcurrentRequests(value);
+ } else if (key == ONLINE_STATUS_KEY) {
+ // For testing only
+ if (auto* boolValue = value.getBool()) {
+ impl->setOnlineStatus(*boolValue);
+ }
+ } else {
+ std::string message = "Resource provider does not support property " + key;
+ Log::Error(Event::General, message.c_str());
+ }
+}
-// For testing only:
+mapbox::base::Value OnlineFileSource::getProperty(const std::string& key) const {
+ if (key == ACCESS_TOKEN_KEY) {
+ return impl->getAccessToken();
+ } else if (key == API_BASE_URL_KEY) {
+ return impl->getAPIBaseURL();
+ } else if (key == MAX_CONCURRENT_REQUESTS_KEY) {
+ return impl->getMaximumConcurrentRequests();
+ }
+ std::string message = "Resource provider does not support property " + key;
+ Log::Error(Event::General, message.c_str());
+ return {};
+}
-void OnlineFileSource::setOnlineStatus(const bool status) {
- impl->setOnlineStatus(status);
+void OnlineFileSource::setResourceTransform(ResourceTransform transform) {
+ impl->setResourceTransform(std::move(transform));
}
} // namespace mbgl
diff --git a/platform/glfw/main.cpp b/platform/glfw/main.cpp
index 8f134804f0..ded8ee3e1f 100644
--- a/platform/glfw/main.cpp
+++ b/platform/glfw/main.cpp
@@ -3,13 +3,14 @@
#include "settings_json.hpp"
#include <mbgl/gfx/backend.hpp>
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/storage/database_file_source.hpp>
+#include <mbgl/storage/file_source_manager.hpp>
+#include <mbgl/style/style.hpp>
#include <mbgl/util/default_styles.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/util/platform.hpp>
#include <mbgl/util/string.hpp>
-#include <mbgl/storage/default_file_source.hpp>
-#include <mbgl/style/style.hpp>
-#include <mbgl/renderer/renderer.hpp>
#include <args.hxx>
@@ -106,10 +107,16 @@ int main(int argc, char *argv[]) {
mbgl::ResourceOptions resourceOptions;
resourceOptions.withCachePath(cacheDB).withAccessToken(token);
- auto fileSource = std::static_pointer_cast<mbgl::DefaultFileSource>(mbgl::FileSource::getSharedFileSource(resourceOptions));
+ auto onlineFileSource =
+ mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::Network, resourceOptions);
if (!settings.online) {
- fileSource->setOnlineStatus(false);
- mbgl::Log::Warning(mbgl::Event::Setup, "Application is offline. Press `O` to toggle online status.");
+ if (onlineFileSource) {
+ onlineFileSource->setProperty("online-status", false);
+ mbgl::Log::Warning(mbgl::Event::Setup, "Application is offline. Press `O` to toggle online status.");
+ } else {
+ mbgl::Log::Warning(mbgl::Event::Setup,
+ "Network resource provider is not available, only local requests are supported.");
+ }
}
GLFWRendererFrontend rendererFrontend { std::make_unique<mbgl::Renderer>(view->getRendererBackend(), view->getPixelRatio()), *view };
@@ -132,9 +139,14 @@ int main(int argc, char *argv[]) {
if (testDirValue) view->setTestDirectory(args::get(testDirValue));
- view->setOnlineStatusCallback([&settings, fileSource]() {
+ view->setOnlineStatusCallback([&settings, onlineFileSource]() {
+ if (!onlineFileSource) {
+ mbgl::Log::Warning(mbgl::Event::Setup,
+ "Cannot change online status. Network resource provider is not available.");
+ return;
+ }
settings.online = !settings.online;
- fileSource->setOnlineStatus(settings.online);
+ onlineFileSource->setProperty("online-status", settings.online);
mbgl::Log::Info(mbgl::Event::Setup, "Application is %s. Press `O` to toggle online status.", settings.online ? "online" : "offline");
});
@@ -152,20 +164,26 @@ int main(int argc, char *argv[]) {
mbgl::Log::Info(mbgl::Event::Setup, "Changed style to: %s", newStyle.name);
});
- view->setPauseResumeCallback([fileSource] () {
+ // Resource loader controls top-level request processing and can resume / pause all managed sources simultaneously.
+ auto resourceLoader =
+ mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::ResourceLoader, resourceOptions);
+ view->setPauseResumeCallback([resourceLoader]() {
static bool isPaused = false;
if (isPaused) {
- fileSource->resume();
+ resourceLoader->resume();
} else {
- fileSource->pause();
+ resourceLoader->pause();
}
isPaused = !isPaused;
});
- view->setResetCacheCallback([fileSource] () {
- fileSource->resetDatabase([](std::exception_ptr ex) {
+ // Database file source.
+ auto databaseFileSource = std::static_pointer_cast<mbgl::DatabaseFileSource>(
+ mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::Database, resourceOptions));
+ view->setResetCacheCallback([databaseFileSource]() {
+ databaseFileSource->resetDatabase([](std::exception_ptr ex) {
if (ex) {
mbgl::Log::Error(mbgl::Event::Database, "Failed to reset cache:: %s", mbgl::util::toString(ex).c_str());
}
diff --git a/platform/linux/filesource-files.json b/platform/linux/filesource-files.json
index 448f5f8613..669a4e612c 100644
--- a/platform/linux/filesource-files.json
+++ b/platform/linux/filesource-files.json
@@ -1,7 +1,6 @@
{
"//": "This file can be edited manually and is the canonical source.",
"sources": [
- "platform/default/src/mbgl/storage/file_source.cpp",
"platform/default/src/mbgl/storage/http_file_source.cpp",
"platform/default/src/mbgl/storage/sqlite3.cpp"
],