diff options
author | Konstantin Käfer <mail@kkaefer.com> | 2015-01-29 18:30:46 +0100 |
---|---|---|
committer | Konstantin Käfer <mail@kkaefer.com> | 2015-02-04 10:49:09 +0100 |
commit | 29baacf3a5bb773d94d08d16b81c3cda45a44eb6 (patch) | |
tree | 1dc3ca456151138ee5e8b7cf88b3afcecc3df1db /platform | |
parent | 3d51e116a84ee168975bcee8377e9156f77e2731 (diff) | |
download | qtlocation-mapboxgl-29baacf3a5bb773d94d08d16b81c3cda45a44eb6.tar.gz |
refactor makefile
Diffstat (limited to 'platform')
-rw-r--r-- | platform/android/asset_request_libzip.cpp | 202 | ||||
-rw-r--r-- | platform/darwin/asset_root.mm | 18 | ||||
-rw-r--r-- | platform/darwin/http_request_nsurl.mm (renamed from platform/darwin/http_request_cocoa.mm) | 20 | ||||
-rw-r--r-- | platform/default/asset_request_fs.cpp (renamed from platform/default/asset_request_libuv.cpp) | 2 | ||||
-rw-r--r-- | platform/default/asset_request_zip.cpp | 295 | ||||
-rw-r--r-- | platform/default/asset_root.cpp | 28 | ||||
-rw-r--r-- | platform/default/default_file_source.cpp | 239 | ||||
-rw-r--r-- | platform/default/http_request_curl.cpp | 12 | ||||
-rw-r--r-- | platform/default/uv_zip.c | 202 | ||||
-rw-r--r-- | platform/default/uv_zip.h | 45 |
10 files changed, 609 insertions, 454 deletions
diff --git a/platform/android/asset_request_libzip.cpp b/platform/android/asset_request_libzip.cpp deleted file mode 100644 index 77f9ae7da1..0000000000 --- a/platform/android/asset_request_libzip.cpp +++ /dev/null @@ -1,202 +0,0 @@ -#include <mbgl/android/jni.hpp> -#include <mbgl/storage/asset_request.hpp> -#include <mbgl/storage/response.hpp> -#include <mbgl/util/uv_detail.hpp> -#include <mbgl/util/std.hpp> - -#include <cerrno> -// NOTE a bug in the Android NDK breaks std::errno -// See https://code.google.com/p/android/issues/detail?id=72349 -// After this is fixed change usage errno to std::errno -#include <zip.h> - -namespace mbgl { - -struct AssetRequestBaton { - AssetRequestBaton(AssetRequest *request_, const std::string &path, uv_loop_t *loop); - ~AssetRequestBaton(); - - const std::thread::id threadId; - AssetRequest *request = nullptr; - std::unique_ptr<uv::async> asyncRun; - std::string path; - bool canceled = false; - - void cancel(); - static void notifyError(AssetRequestBaton *ptr, const int code, const char *message); - static void notifySuccess(AssetRequestBaton *ptr, const std::string body); - static void cleanup(AssetRequestBaton *ptr); - static void run(AssetRequestBaton *ptr); -}; - -AssetRequestBaton::AssetRequestBaton(AssetRequest *request_, const std::string &path_, uv_loop_t *loop) -: threadId(std::this_thread::get_id()), -request(request_), -path(path_) { - - asyncRun = mbgl::util::make_unique<uv::async>(loop, [this]() { - run(this); - }); - - asyncRun->send(); -} - -AssetRequestBaton::~AssetRequestBaton() { -} - -void AssetRequestBaton::cancel() { - canceled = true; -} - -void AssetRequestBaton::notifyError(AssetRequestBaton *ptr, const int code, const char *message) { - assert(std::this_thread::get_id() == ptr->threadId); - - if (ptr->request && !ptr->canceled) { - ptr->request->response = std::unique_ptr<Response>(new Response); - ptr->request->response->code = code; - ptr->request->response->message = message; - ptr->request->notify(); - } -} - -void AssetRequestBaton::notifySuccess(AssetRequestBaton *ptr, const std::string body) { - assert(std::this_thread::get_id() == ptr->threadId); - - if (ptr->request && !ptr->canceled) { - ptr->request->response = std::unique_ptr<Response>(new Response); - ptr->request->response->code = 200; - ptr->request->response->data = body; - ptr->request->notify(); - } -} - -void AssetRequestBaton::cleanup(AssetRequestBaton *ptr) { - assert(std::this_thread::get_id() == ptr->threadId); - - if (ptr->request) { - ptr->request->ptr = nullptr; - } - - ptr->asyncRun.reset(); - - delete ptr; - ptr = nullptr; -} - -void AssetRequestBaton::run(AssetRequestBaton *ptr) { - assert(std::this_thread::get_id() == ptr->threadId); - - if (ptr->canceled || !ptr->request) { - // Either the AssetRequest object has been destructed, or the - // request was canceled. - cleanup(ptr); - return; - } - - int error = 0; - struct zip *apk = zip_open(mbgl::android::apkPath.c_str(), 0, &error); - if ((apk == nullptr) || ptr->canceled || !ptr->request) { - // Opening the APK failed or was canceled. There isn't much left we can do. - const int messageSize = zip_error_to_str(nullptr, 0, error, errno); - const std::unique_ptr<char[]> message = mbgl::util::make_unique<char[]>(messageSize); - zip_error_to_str(message.get(), 0, error, errno); - notifyError(ptr, 500, message.get()); - cleanup(ptr); - return; - } - - std::string apkFilePath = "assets/" + ptr->path; - struct zip_file *apkFile = zip_fopen(apk, apkFilePath.c_str(), ZIP_FL_NOCASE); - if ((apkFile == nullptr) || ptr->canceled || !ptr->request) { - // Opening the asset failed or was canceled. We already have an open file handle - // though, which we'll have to close. - zip_error_get(apk, &error, nullptr); - notifyError(ptr, error == ZIP_ER_NOENT ? 404 : 500, zip_strerror(apk)); - zip_close(apk); - apk = nullptr; - cleanup(ptr); - return; - } - - struct zip_stat stat; - if ((zip_stat(apk, apkFilePath.c_str(), ZIP_FL_NOCASE, &stat) != 0) || ptr->canceled || !ptr->request) { - // Stating failed or was canceled. We already have an open file handle - // though, which we'll have to close. - notifyError(ptr, 500, zip_strerror(apk)); - zip_fclose(apkFile); - apkFile = nullptr; - zip_close(apk); - apk = nullptr; - cleanup(ptr); - return; - } - - const std::unique_ptr<char[]> data = mbgl::util::make_unique<char[]>(stat.size); - - if (static_cast<zip_uint64_t>(zip_fread(apkFile, reinterpret_cast<void *>(data.get()), stat.size)) != stat.size || ptr->canceled || !ptr->request) { - // Reading failed or was canceled. We already have an open file handle - // though, which we'll have to close. - notifyError(ptr, 500, zip_file_strerror(apkFile)); - zip_fclose(apkFile); - apkFile = nullptr; - zip_close(apk); - apk = nullptr; - cleanup(ptr); - return; - } - - std::string body(data.get(), stat.size); - notifySuccess(ptr, body); - - if (zip_fclose(apkFile) != 0) { - // Closing the asset failed. But there isn't anything we can do. - } - apkFile = nullptr; - - if (zip_close(apk) != 0) { - // Closing the APK failed. But there isn't anything we can do. - } - apk = nullptr; - - cleanup(ptr); -} - -AssetRequest::AssetRequest(const std::string &path_, uv_loop_t *loop) -: BaseRequest(path_) { - if (!path.empty() && path[0] == '/') { - // This is an absolute path. We don't allow this. Note that this is not a way to absolutely - // prevent access to resources outside the application bundle; e.g. there could be symlinks - // in the application bundle that link to outside. We don't care about these. - response = util::make_unique<Response>(); - response->code = 403; - response->message = "Path is outside the application bundle"; - notify(); - } else { - // Note: The AssetRequestBaton object is deleted in AssetRequestBaton::cleanup(). - ptr = new AssetRequestBaton(this, path, loop); - } -} - -void AssetRequest::cancel() { - assert(std::this_thread::get_id() == threadId); - - if (ptr) { - ptr->cancel(); - - // When deleting a AssetRequest object with a uv_fs_* call is in progress, we are making sure - // that the callback doesn't accidentally reference this object again. - ptr->request = nullptr; - ptr = nullptr; - } - - notify(); -} - -AssetRequest::~AssetRequest() { - assert(std::this_thread::get_id() == threadId); - cancel(); - - // Note: The AssetRequestBaton object is deleted in AssetRequestBaton::cleanup(). -} - -} diff --git a/platform/darwin/asset_root.mm b/platform/darwin/asset_root.mm new file mode 100644 index 0000000000..375975a84b --- /dev/null +++ b/platform/darwin/asset_root.mm @@ -0,0 +1,18 @@ +#import <Foundation/Foundation.h> + +#include <mbgl/platform/platform.hpp> + +namespace mbgl { +namespace platform { + +// Returns the path to the root folder of the application. +const std::string &assetRoot() { + static const std::string root = []() -> std::string { + NSString *path = [[[NSBundle mainBundle] resourceURL] path]; + return {[path cStringUsingEncoding : NSUTF8StringEncoding], + [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding]}; + }(); + return root; +} +} +} diff --git a/platform/darwin/http_request_cocoa.mm b/platform/darwin/http_request_nsurl.mm index efcc910488..83c010f8b8 100644 --- a/platform/darwin/http_request_cocoa.mm +++ b/platform/darwin/http_request_nsurl.mm @@ -43,7 +43,7 @@ enum class ResponseStatus : uint8_t { // ------------------------------------------------------------------------------------------------- -class HTTPCocoaContext; +class HTTPNSURLContext; class HTTPRequestImpl { public: @@ -62,7 +62,7 @@ public: static void restart(uv_timer_t *timer, int); private: - HTTPCocoaContext *context = nullptr; + HTTPNSURLContext *context = nullptr; HTTPRequest *request = nullptr; NSURLSessionDataTask *task = nullptr; std::unique_ptr<Response> response; @@ -78,19 +78,19 @@ private: // ------------------------------------------------------------------------------------------------- -class HTTPCocoaContext : public HTTPContext<HTTPCocoaContext> { +class HTTPNSURLContext : public HTTPContext<HTTPNSURLContext> { public: - HTTPCocoaContext(uv_loop_t *loop); - ~HTTPCocoaContext(); + HTTPNSURLContext(uv_loop_t *loop); + ~HTTPNSURLContext(); NSURLSession *session = nil; NSString *userAgent = nil; }; -template<> pthread_key_t HTTPContext<HTTPCocoaContext>::key{}; -template<> pthread_once_t HTTPContext<HTTPCocoaContext>::once = PTHREAD_ONCE_INIT; +template<> pthread_key_t ThreadContext<HTTPNSURLContext>::key{}; +template<> pthread_once_t ThreadContext<HTTPNSURLContext>::once = PTHREAD_ONCE_INIT; -HTTPCocoaContext::HTTPCocoaContext(uv_loop_t *loop_) : HTTPContext(loop_) { +HTTPNSURLContext::HTTPNSURLContext(uv_loop_t *loop_) : HTTPContext(loop_) { @autoreleasepool { NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; @@ -107,7 +107,7 @@ HTTPCocoaContext::HTTPCocoaContext(uv_loop_t *loop_) : HTTPContext(loop_) { } } -HTTPCocoaContext::~HTTPCocoaContext() { +HTTPNSURLContext::~HTTPNSURLContext() { [session release]; session = nullptr; @@ -119,7 +119,7 @@ HTTPCocoaContext::~HTTPCocoaContext() { HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop, std::unique_ptr<Response> existingResponse_) - : context(HTTPCocoaContext::Get(loop)), + : context(HTTPNSURLContext::Get(loop)), request(request_), existingResponse(std::move(existingResponse_)), async(new uv_async_t) { diff --git a/platform/default/asset_request_libuv.cpp b/platform/default/asset_request_fs.cpp index 4a6fc88eb6..e3875a08e8 100644 --- a/platform/default/asset_request_libuv.cpp +++ b/platform/default/asset_request_fs.cpp @@ -56,7 +56,7 @@ AssetRequestImpl::AssetRequestImpl(AssetRequest *request_, uv_loop_t *loop) : re path = url.substr(8); } else { // This is a relative path. Prefix with the application root. - path = platform::applicationRoot() + "/" + url.substr(8); + path = platform::assetRoot() + "/" + url.substr(8); } uv_fs_open(loop, &req, path.c_str(), O_RDONLY, S_IRUSR, fileOpened); diff --git a/platform/default/asset_request_zip.cpp b/platform/default/asset_request_zip.cpp new file mode 100644 index 0000000000..9c1093c5ed --- /dev/null +++ b/platform/default/asset_request_zip.cpp @@ -0,0 +1,295 @@ +#include <mbgl/storage/default/asset_request.hpp> +#include <mbgl/storage/default/thread_context.hpp> +#include <mbgl/android/jni.hpp> +#include <mbgl/storage/response.hpp> +#include <mbgl/util/std.hpp> +#include <mbgl/platform/platform.hpp> + +#include "uv_zip.h" + +#include <boost/algorithm/string.hpp> + +#include <forward_list> + +namespace algo = boost::algorithm; + +namespace mbgl { + +class AssetZipContext : public ThreadContext<AssetZipContext> { +public: + AssetZipContext(uv_loop_t *loop); + ~AssetZipContext(); + + uv_zip_t *getHandle(); + void returnHandle(uv_zip_t *zip); + +private: + // A list of resuable uv_zip handles to avoid creating and destroying them all the time. + std::forward_list<uv_zip_t *> handles; +}; + +// ------------------------------------------------------------------------------------------------- + +template<> pthread_key_t ThreadContext<AssetZipContext>::key{}; +template<> pthread_once_t ThreadContext<AssetZipContext>::once = PTHREAD_ONCE_INIT; + +AssetZipContext::AssetZipContext(uv_loop_t *loop_) : ThreadContext(loop_) { +} + +uv_zip_t *AssetZipContext::getHandle() { + if (!handles.empty()) { + auto zip = handles.front(); + handles.pop_front(); + return zip; + } else { + return nullptr; + } +} + +void AssetZipContext::returnHandle(uv_zip_t *zip) { + uv_zip_cleanup(zip); + handles.push_front(zip); +} + +AssetZipContext::~AssetZipContext() { + // Close all zip handles + for (auto zip : handles) { + uv_zip_discard(loop, zip, [](uv_zip_t *zip_) { + uv_zip_cleanup(zip_); + delete zip_; + }); + } + handles.clear(); +} + +// ------------------------------------------------------------------------------------------------- + +class AssetRequestImpl { + MBGL_STORE_THREAD(tid) + +public: + AssetRequestImpl(AssetRequest *request, uv_loop_t *loop); + ~AssetRequestImpl(); + + void cancel(); + +private: + AssetZipContext &context; + AssetRequest *request = nullptr; + const std::string path; + std::unique_ptr<Response> response; + uv_buf_t buffer; + +private: + void openZipArchive(); + void archiveOpened(uv_zip_t *zip); + void fileStated(uv_zip_t *zip); + void fileOpened(uv_zip_t *zip); + void fileRead(uv_zip_t *zip); + void fileClosed(uv_zip_t *zip); + void cleanup(uv_zip_t *zip); + + void notifyError(const char *message); +}; + +// ------------------------------------------------------------------------------------------------- + +AssetRequestImpl::~AssetRequestImpl() { + MBGL_VERIFY_THREAD(tid); + + if (request) { + request->ptr = nullptr; + } +} + +AssetRequestImpl::AssetRequestImpl(AssetRequest *request_, uv_loop_t *loop) + : context(*AssetZipContext::Get(loop)), + request(request_), + path(request->resource.url.substr(8)) { + auto zip = context.getHandle(); + if (zip) { + archiveOpened(zip); + } else { + openZipArchive(); + } +} + +void AssetRequestImpl::openZipArchive() { + uv_fs_t *req = new uv_fs_t(); + req->data = this; + + // We're using uv_fs_open first to obtain a file descriptor. Then, uv_zip_fdopen will operate + // on a read-only file. + uv_fs_open(context.loop, req, platform::assetRoot().c_str(), O_RDONLY, S_IRUSR, [](uv_fs_t *fsReq) { + if (fsReq->result < 0) { + auto impl = reinterpret_cast<AssetRequestImpl *>(fsReq->data); + impl->notifyError(uv::getFileRequestError(fsReq)); + delete impl; + } else { + uv_zip_t *zip = new uv_zip_t(); + uv_zip_init(zip); + zip->data = fsReq->data; + uv_zip_fdopen(fsReq->loop, zip, uv_file(fsReq->result), 0, [](uv_zip_t *openZip) { + auto impl = reinterpret_cast<AssetRequestImpl *>(openZip->data); + if (openZip->result < 0) { + impl->notifyError(openZip->message); + delete openZip; + delete impl; + } else { + impl->archiveOpened(openZip); + } + }); + } + + uv_fs_req_cleanup(fsReq); + delete fsReq; + fsReq = nullptr; + }); +} + +#define INVOKE_MEMBER(name) \ + [](uv_zip_t *zip_) { \ + assert(zip_->data); \ + reinterpret_cast<AssetRequestImpl *>(zip_->data)->name(zip_); \ + } + +void AssetRequestImpl::archiveOpened(uv_zip_t *zip) { + MBGL_VERIFY_THREAD(tid); + + zip->data = this; + uv_zip_stat(context.loop, zip, path.c_str(), 0, INVOKE_MEMBER(fileStated)); +} + +void AssetRequestImpl::fileStated(uv_zip_t *zip) { + MBGL_VERIFY_THREAD(tid); + + if (!request || zip->result < 0) { + // Stat failed, probably because the file doesn't exist. + if (zip->result < 0) { + notifyError(zip->message); + } + cleanup(zip); + } else if (!(zip->stat->valid & ZIP_STAT_SIZE)) { + // We couldn't obtain the size of the file. + notifyError("Could not determine file size in zip file"); + cleanup(zip); + } else { + response = util::make_unique<Response>(); + + // Allocate the space for reading the data. + response->data.resize(zip->stat->size); + buffer = uv_buf_init(const_cast<char *>(response->data.data()), zip->stat->size); + + // Get the modification time in case we have one. + if (zip->stat->valid & ZIP_STAT_MTIME) { + response->modified = zip->stat->mtime; + } + + uv_zip_fopen(context.loop, zip, path.c_str(), 0, INVOKE_MEMBER(fileOpened)); + } +} + +void AssetRequestImpl::fileOpened(uv_zip_t *zip) { + MBGL_VERIFY_THREAD(tid); + + if (zip->result < 0) { + // Opening failed. + notifyError(zip->message); + cleanup(zip); + } else if (!request) { + // The request was canceled. Close the file again. + uv_zip_fclose(context.loop, zip, zip->file, INVOKE_MEMBER(fileClosed)); + } else { + uv_zip_fread(context.loop, zip, zip->file, &buffer, INVOKE_MEMBER(fileRead)); + } +} + +void AssetRequestImpl::fileRead(uv_zip_t *zip) { + MBGL_VERIFY_THREAD(tid); + + if (zip->result < 0) { + // Reading failed. We still have an open file handle though. + notifyError(zip->message); + } else if (request) { + response->status = Response::Successful; + request->notify(std::move(response), FileCache::Hint::No); + delete request; + assert(request == nullptr); + } + + uv_zip_fclose(context.loop, zip, zip->file, INVOKE_MEMBER(fileClosed)); +} + +void AssetRequestImpl::fileClosed(uv_zip_t *zip) { + MBGL_VERIFY_THREAD(tid); + + if (zip->result < 0) { + // Closing the file failed. But there isn't anything we can do. + } + + cleanup(zip); +} + +void AssetRequestImpl::cleanup(uv_zip_t *zip) { + MBGL_VERIFY_THREAD(tid); + + context.returnHandle(zip); + delete this; +} + +void AssetRequestImpl::notifyError(const char *message) { + MBGL_VERIFY_THREAD(tid); + + if (request) { + response = util::make_unique<Response>(); + response->status = Response::Error; + response->message = message; + request->notify(std::move(response), FileCache::Hint::No); + delete request; + assert(request == nullptr); + } else { + // The request was already canceled and deleted. + } +} + +void AssetRequestImpl::cancel() { + request = nullptr; +} + +// ------------------------------------------------------------------------------------------------- + +AssetRequest::AssetRequest(DefaultFileSource *source_, const Resource &resource_) + : SharedRequestBase(source_, resource_) { + assert(algo::starts_with(resource.url, "asset://")); +} + +AssetRequest::~AssetRequest() { + MBGL_VERIFY_THREAD(tid); + + if (ptr) { + reinterpret_cast<AssetRequestImpl *>(ptr)->cancel(); + } +} + +void AssetRequest::start(uv_loop_t *loop, std::unique_ptr<Response> response) { + MBGL_VERIFY_THREAD(tid); + + // We're ignoring the existing response if any. + (void(response)); + + assert(!ptr); + ptr = new AssetRequestImpl(this, loop); + // Note: the AssetRequestImpl deletes itself. +} + +void AssetRequest::cancel() { + MBGL_VERIFY_THREAD(tid); + + if (ptr) { + reinterpret_cast<AssetRequestImpl *>(ptr)->cancel(); + } + + delete this; +} + +} diff --git a/platform/default/asset_root.cpp b/platform/default/asset_root.cpp new file mode 100644 index 0000000000..2b20018c40 --- /dev/null +++ b/platform/default/asset_root.cpp @@ -0,0 +1,28 @@ +#include <mbgl/platform/platform.hpp> + +#include <uv.h> +#include <libgen.h> + +namespace mbgl { +namespace platform { + +// Returns the path to the root folder of the application. +const std::string &assetRoot() { + static const std::string root = []() -> std::string { + size_t max = 0; + std::string dir; + do { + // Gradually increase the length of the string in case the path was truncated. + max += 256; + dir.resize(max); + uv_exepath(const_cast<char *>(dir.data()), &max); + } while (max == dir.size()); + dir.resize(max - 1); + dir = dirname(const_cast<char *>(dir.c_str())); + return dir; + }(); + return root; +} + +} +} diff --git a/platform/default/default_file_source.cpp b/platform/default/default_file_source.cpp deleted file mode 100644 index 60633c789e..0000000000 --- a/platform/default/default_file_source.cpp +++ /dev/null @@ -1,239 +0,0 @@ -#include <mbgl/storage/default/default_file_source.hpp> -#include <mbgl/storage/default/request.hpp> -#include <mbgl/storage/default/asset_request.hpp> -#include <mbgl/storage/default/http_request.hpp> - -#include <mbgl/storage/response.hpp> - -#include <mbgl/util/async_queue.hpp> -#include <mbgl/util/util.hpp> - -#include <mbgl/util/variant.hpp> - -#include <boost/algorithm/string.hpp> - -#include <thread> -#include <algorithm> -#include <cassert> - - -namespace algo = boost::algorithm; - -namespace mbgl { - -struct DefaultFileSource::ActionDispatcher { - DefaultFileSource &fileSource; - template <typename T> void operator()(T &t) { fileSource.process(t); } -}; - -struct DefaultFileSource::AddRequestAction { - Request *const request; -}; - -struct DefaultFileSource::RemoveRequestAction { - Request *const request; -}; - -struct DefaultFileSource::ResultAction { - const Resource resource; - std::unique_ptr<Response> response; -}; - -struct DefaultFileSource::StopAction { -}; - - -DefaultFileSource::DefaultFileSource(FileCache *cache_) - : loop(uv_loop_new()), - cache(cache_), - queue(new Queue(loop, [this](Action &action) { - mapbox::util::apply_visitor(ActionDispatcher{*this}, action); - })), - thread([this]() { -#ifdef __APPLE__ - pthread_setname_np("FileSource"); -#endif - uv_run(loop, UV_RUN_DEFAULT); - }) { -} - -DefaultFileSource::DefaultFileSource(FileCache *cache_, uv_loop_t *loop_) - : loop(loop_), - cache(cache_), - queue(new Queue(loop, [this](Action &action) { - mapbox::util::apply_visitor(ActionDispatcher{*this}, action); - })) { - // Make sure that the queue doesn't block the loop from exiting. - queue->unref(); -} - -DefaultFileSource::~DefaultFileSource() { - MBGL_VERIFY_THREAD(tid); - - if (thread.joinable()) { - if (queue) { - queue->send(StopAction{ }); - } - thread.join(); - uv_loop_delete(loop); - } else { - // Assume that the loop we received is running in the current thread. - StopAction action {}; - process(action); - } -} - -SharedRequestBase *DefaultFileSource::find(const Resource &resource) { - // We're using a set of pointers here instead of a map between url and SharedRequestBase because - // we need to find the requests both by pointer and by URL. Given that the number of requests - // is generally very small (typically < 10 at a time), hashing by URL incurs too much overhead - // anyway. - const auto it = pending.find(resource); - if (it != pending.end()) { - return it->second; - } - return nullptr; -} - -Request *DefaultFileSource::request(const Resource &resource, uv_loop_t *l, Callback callback) { - auto req = new Request(resource, l, std::move(callback)); - - // This function can be called from any thread. Make sure we're executing the actual call in the - // file source loop by sending it over the queue. It will be processed in processAction(). - queue->send(AddRequestAction{ req }); - return req; -} - -void DefaultFileSource::request(const Resource &resource, Callback callback) { - auto req = new Request(resource, nullptr, std::move(callback)); - - // This function can be called from any thread. Make sure we're executing the actual call in the - // file source loop by sending it over the queue. It will be processed in processAction(). - queue->send(AddRequestAction{ req }); -} - -void DefaultFileSource::cancel(Request *req) { - req->cancel(); - - // This function can be called from any thread. Make sure we're executing the actual call in the - // file source loop by sending it over the queue. It will be processed in processAction(). - queue->send(RemoveRequestAction{ req }); -} - -void DefaultFileSource::process(AddRequestAction &action) { - const Resource &resource = action.request->resource; - - // We're adding a new Request. - SharedRequestBase *sharedRequest = find(resource); - if (!sharedRequest) { - // There is no request for this URL yet. Create a new one and start it. - if (algo::starts_with(resource.url, "asset://")) { - sharedRequest = new AssetRequest(this, resource); - } else { - sharedRequest = new HTTPRequest(this, resource); - } - - // Make sure the loop stays alive when we're not running the file source in it's own thread. - if (!thread.joinable() && pending.empty()) { - queue->ref(); - } - - const bool inserted = pending.emplace(resource, sharedRequest).second; - assert(inserted); - (void (inserted)); // silence unused variable warning on Release builds. - - // But first, we're going to start querying the database if it exists. - if (!cache) { - sharedRequest->start(loop); - } else { - // Otherwise, first check the cache for existing data so that we can potentially - // revalidate the information without having to redownload everything. - cache->get(resource, [this, resource](std::unique_ptr<Response> response) { - queue->send(ResultAction { resource, std::move(response) }); - }); - } - } - sharedRequest->subscribe(action.request); -} - -void DefaultFileSource::process(RemoveRequestAction &action) { - SharedRequestBase *sharedRequest = find(action.request->resource); - if (sharedRequest) { - // If the number of dependent requests of the SharedRequestBase drops to zero, the - // unsubscribe callback triggers the removal of the SharedRequestBase pointer from the list - // of pending requests and initiates cancelation. - sharedRequest->unsubscribe(action.request); - } else { - // There is no request for this URL anymore. Likely, the request already completed - // before we got around to process the cancelation request. - } - - // Send a message back to the requesting thread and notify it that this request has been - // canceled and is now safe to be deleted. - action.request->destruct(); -} - -void DefaultFileSource::process(ResultAction &action) { - SharedRequestBase *sharedRequest = find(action.resource); - if (sharedRequest) { - if (action.response) { - // This entry was stored in the cache. Now determine if we need to revalidate. - const int64_t now = std::chrono::duration_cast<std::chrono::seconds>( - std::chrono::system_clock::now().time_since_epoch()).count(); - if (action.response->expires > now) { - // The response is fresh. We're good to notify the caller. - sharedRequest->notify(std::move(action.response), FileCache::Hint::No); - sharedRequest->cancel(); - return; - } else { - // The cached response is stale. Now run the real request. - sharedRequest->start(loop, std::move(action.response)); - } - } else { - // There is no response. Now run the real request. - sharedRequest->start(loop); - } - } else { - // There is no request for this URL anymore. Likely, the request was canceled - // before we got around to process the cache result. - } -} - -void DefaultFileSource::process(StopAction &) { - // Cancel all remaining requests. - for (auto it : pending) { - it.second->unsubscribeAll(); - } - pending.clear(); - - assert(queue); - queue->stop(); - queue = nullptr; -} - -void DefaultFileSource::notify(SharedRequestBase *sharedRequest, - const std::set<Request *> &observers, - std::shared_ptr<const Response> response, FileCache::Hint hint) { - // First, remove the request, since it might be destructed at any point now. - assert(find(sharedRequest->resource) == sharedRequest); - pending.erase(sharedRequest->resource); - - if (response) { - if (cache) { - // Store response in database - cache->put(sharedRequest->resource, response, hint); - } - - // Notify all observers. - for (auto it : observers) { - it->notify(response); - } - } - - if (!thread.joinable() && pending.empty()) { - // When there are no pending requests, we're going to allow the queue to stop. - queue->unref(); - } -} - -} diff --git a/platform/default/http_request_curl.cpp b/platform/default/http_request_curl.cpp index 9218563ee4..411c5ee470 100644 --- a/platform/default/http_request_curl.cpp +++ b/platform/default/http_request_curl.cpp @@ -99,7 +99,11 @@ private: static size_t writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp); void retry(uint64_t timeout); +#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 static void restart(uv_timer_t *timer, int); +#else + static void restart(uv_timer_t *timer); +#endif void finish(ResponseStatus status); void start(); @@ -161,8 +165,8 @@ private: // ------------------------------------------------------------------------------------------------- -template<> pthread_key_t HTTPContext<HTTPCURLContext>::key{}; -template<> pthread_once_t HTTPContext<HTTPCURLContext>::once = PTHREAD_ONCE_INIT; +template<> pthread_key_t ThreadContext<HTTPCURLContext>::key{}; +template<> pthread_once_t ThreadContext<HTTPCURLContext>::once = PTHREAD_ONCE_INIT; HTTPCURLContext::HTTPCURLContext(uv_loop_t *loop_) : HTTPContext(loop_) { if (curl_global_init(CURL_GLOBAL_ALL)) { @@ -491,7 +495,11 @@ void HTTPRequestImpl::retryImmediately() { } } +#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 void HTTPRequestImpl::restart(uv_timer_t *timer, int) { +#else +void HTTPRequestImpl::restart(uv_timer_t *timer) { +#endif // Restart the request. auto baton = reinterpret_cast<HTTPRequestImpl *>(timer->data); diff --git a/platform/default/uv_zip.c b/platform/default/uv_zip.c new file mode 100644 index 0000000000..b6d2c2ebe0 --- /dev/null +++ b/platform/default/uv_zip.c @@ -0,0 +1,202 @@ +#include "uv_zip.h" + +#include <assert.h> +#include <errno.h> +#include <string.h> + + +void uv__zip_open_error(uv_zip_t *zip, int error) { + zip->result = -error; + if (zip->message) { + free((char *)zip->message); + zip->message = NULL; + } + const zip_uint64_t size = zip_error_to_str(NULL, 0, error, errno) + 1; + zip->message = malloc(size); + zip_error_to_str((char *)zip->message, size, error, errno); +} + +void uv__zip_store_error(uv_zip_t *zip, const char *message) { + if (zip->message) { + free((char *)zip->message); + zip->message = NULL; + } + const unsigned long length = strlen(message); + zip->message = malloc(length); + strncpy((char *)zip->message, message, length); +} + +void uv__zip_error(uv_zip_t *zip) { + int error; + zip_error_get(zip->archive, &error, NULL); + zip->result = -error; + uv__zip_store_error(zip, zip_strerror(zip->archive)); +} + +void uv__zip_file_error(uv_zip_t *zip) { + int error; + zip_file_error_get(zip->file, &error, NULL); + zip->result = -error; + uv__zip_store_error(zip, zip_file_strerror(zip->file)); +} + +void uv__zip_work_open(uv_work_t *req) { + uv_zip_t *zip = (uv_zip_t *)req->data; + assert(!zip->archive); + + int error; + zip->archive = zip_open(zip->path, zip->flags, &error); + if (!zip->archive) { + uv__zip_open_error(zip, error); + } else { + zip->result = 0; + } +} + +void uv__zip_work_fdopen(uv_work_t *req) { + uv_zip_t *zip = (uv_zip_t *)req->data; + assert(!zip->archive); + + // extract the fd + uv_file fd = *(uv_file *)zip->path; + free((uv_file *)zip->path); + zip->path = NULL; + + int error; + zip->archive = zip_fdopen(fd, zip->flags, &error); + if (!zip->archive) { + uv__zip_open_error(zip, error); + } else { + zip->result = 0; + } +} + +void uv__zip_work_stat(uv_work_t *req) { + uv_zip_t *zip = (uv_zip_t *)req->data; + assert(zip->archive); + if (!zip->stat) { + zip->stat = malloc(sizeof(struct zip_stat)); + zip_stat_init(zip->stat); + } + if (0 != zip_stat(zip->archive, zip->path, zip->flags, zip->stat)) { + uv__zip_error(zip); + } +} + +void uv__zip_work_fopen(uv_work_t *req) { + uv_zip_t *zip = (uv_zip_t *)req->data; + assert(zip->archive); + zip->file = zip_fopen(zip->archive, zip->path, zip->flags); + if (!zip->file) { + uv__zip_error(zip); + } +} + +void uv__zip_work_fread(uv_work_t *req) { + uv_zip_t *zip = (uv_zip_t *)req->data; + assert(zip->file); + assert(zip->buf); + zip->result = zip_fread(zip->file, zip->buf->base, zip->buf->len); + if (zip->result < 0) { + uv__zip_file_error(zip); + } +} + +void uv__zip_work_fclose(uv_work_t *req) { + uv_zip_t *zip = (uv_zip_t *)req->data; + assert(zip->file); + zip->result = zip_fclose(zip->file); + if (zip->result != 0) { + uv__zip_file_error(zip); + } +} + +void uv__zip_work_discard(uv_work_t *req) { + uv_zip_t *zip = (uv_zip_t *)req->data; + assert(zip->archive); + zip_discard(zip->archive); + zip->archive = NULL; + zip->result = 0; +} + +void uv__zip_after_work(uv_work_t *req, int status) { + uv_zip_t *zip = (uv_zip_t *)req->data; + if (zip->cb) { + zip->cb(zip); + } +} + +void uv_zip_init(uv_zip_t *zip) { + zip->work.data = zip; + zip->message = NULL; + zip->stat = NULL; + uv_zip_cleanup(zip); +} + +void uv_zip_cleanup(uv_zip_t *zip) { + zip->data = NULL; + zip->flags = 0; + zip->result = 0; + zip->path = NULL; + zip->cb = NULL; + zip->archive = NULL; + zip->file = NULL; + zip->buf = NULL; + + if (zip->message) { + free((char *)zip->message); + zip->message = NULL; + } + + if (zip->stat) { + free(zip->stat); + zip->stat = NULL; + } +} + +int uv_zip_open(uv_loop_t* loop, uv_zip_t *zip, const char *path, zip_flags_t flags, uv_zip_cb cb) { + zip->path = path; + zip->flags = flags; + zip->cb = cb; + return uv_queue_work(loop, &zip->work, uv__zip_work_open, uv__zip_after_work); +} + +int uv_zip_fdopen(uv_loop_t* loop, uv_zip_t *zip, uv_file fd, int flags, uv_zip_cb cb) { + zip->path = malloc(sizeof(uv_file)); + *(uv_file *)zip->path = fd; + zip->flags = flags; + zip->cb = cb; + return uv_queue_work(loop, &zip->work, uv__zip_work_fdopen, uv__zip_after_work); +} + +int uv_zip_stat(uv_loop_t* loop, uv_zip_t *zip, const char *fname, zip_flags_t flags, uv_zip_cb cb) { + zip->path = fname; + zip->flags = flags; + zip->cb = cb; + return uv_queue_work(loop, &zip->work, uv__zip_work_stat, uv__zip_after_work); +} + +int uv_zip_fopen(uv_loop_t* loop, uv_zip_t *zip, const char *fname, zip_flags_t flags, uv_zip_cb cb) { + zip->path = fname; + zip->flags = flags; + zip->cb = cb; + return uv_queue_work(loop, &zip->work, uv__zip_work_fopen, uv__zip_after_work); +} + +int uv_zip_fclose(uv_loop_t* loop, uv_zip_t *zip, struct zip_file *file, uv_zip_cb cb) { + zip->file = file; + zip->cb = cb; + return uv_queue_work(loop, &zip->work, uv__zip_work_fclose, uv__zip_after_work); +} + +int uv_zip_fread(uv_loop_t* loop, uv_zip_t *zip, struct zip_file *file, uv_buf_t *buf, uv_zip_cb cb) { + zip->file = file; + zip->buf = buf; + zip->cb = cb; + return uv_queue_work(loop, &zip->work, uv__zip_work_fread, uv__zip_after_work); +} + +int uv_zip_discard(uv_loop_t* loop, uv_zip_t *zip, uv_zip_cb cb) { + zip->cb = cb; + return uv_queue_work(loop, &zip->work, uv__zip_work_discard, uv__zip_after_work); +} diff --git a/platform/default/uv_zip.h b/platform/default/uv_zip.h new file mode 100644 index 0000000000..5908763f09 --- /dev/null +++ b/platform/default/uv_zip.h @@ -0,0 +1,45 @@ +#ifndef UV_ZIP +#define UV_ZIP + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include <uv.h> +#include <zip.h> + +typedef struct uv_zip_s uv_zip_t; + +typedef void (*uv_zip_cb)(uv_zip_t* req); + +struct uv_zip_s { + uv_work_t work; + ssize_t result; + const char *message; + struct zip *archive; + struct zip_file *file; + struct zip_stat *stat; + void *data; + zip_flags_t flags; + const char *path; + uv_zip_cb cb; + uv_buf_t *buf; +}; + +void uv_zip_init(uv_zip_t *zip); +void uv_zip_cleanup(uv_zip_t *zip); + +int uv_zip_open(uv_loop_t* loop, uv_zip_t *zip, const char *path, zip_flags_t flags, uv_zip_cb cb); +int uv_zip_fdopen(uv_loop_t* loop, uv_zip_t *zip, uv_file fd, int flags, uv_zip_cb cb); +int uv_zip_stat(uv_loop_t* loop, uv_zip_t *zip, const char *fname, zip_flags_t flags, uv_zip_cb cb); +int uv_zip_fopen(uv_loop_t* loop, uv_zip_t *zip, const char *fname, zip_flags_t flags, uv_zip_cb cb); +int uv_zip_fread(uv_loop_t* loop, uv_zip_t *zip, struct zip_file *file, uv_buf_t *buf, uv_zip_cb cb); +int uv_zip_fclose(uv_loop_t* loop, uv_zip_t *zip, struct zip_file *file, uv_zip_cb cb); +int uv_zip_discard(uv_loop_t* loop, uv_zip_t *zip, uv_zip_cb cb); + +#ifdef __cplusplus +} +#endif + +#endif // UV_ZIP |