Merge pull request #887 from mapbox/fix-libzip

Fix libzip on Android
This commit is contained in:
Konstantin Käfer
2015-02-17 14:02:01 -08:00
19 changed files with 337 additions and 59 deletions

View File

@@ -165,6 +165,9 @@ Makefile/android-%: config/android-%.gypi
android-lib-%: Makefile/android-%
@$(ENV_android-$*) ./scripts/flock.py -v build/Android.lock $(MAKE) -C build/android-$* BUILDTYPE=$(BUILDTYPE) androidapp
# Builds the selected/default Android library
android-lib: android-lib-$(ANDROID_ABI)
# Builds the selected/default Android library
android: android-lib-$(ANDROID_ABI)
cd android/java && ./gradlew --parallel-threads=$(JOBS) build

View File

@@ -201,7 +201,7 @@ public class MainActivity extends ActionBarActivity {
// Handles location updates from GPS
private void updateLocation(Location location) {
if (location != null) {
LatLngZoom coordinate = new LatLngZoom(location.getLongitude(), location.getLatitude(), 16);
LatLngZoom coordinate = new LatLngZoom(location.getLatitude(), location.getLongitude(), 16);
mMapFragment.getMap().setCenterCoordinate(coordinate, true);
}
}
@@ -214,7 +214,7 @@ public class MainActivity extends ActionBarActivity {
switch(position) {
// Bright
case 0:
mMapFragment.getMap().setStyleUrl("asset://styles/styles/bright-v7.json");
mMapFragment.getMap().setStyleUrl("asset://styles/bright-v7.json");
mMapFragment.getMap().removeAllClasses();
mClassSpinner.setVisibility(View.INVISIBLE);
mClassSpinner.setAdapter(null);
@@ -223,7 +223,7 @@ public class MainActivity extends ActionBarActivity {
// Basic
case 1:
mMapFragment.getMap().setStyleUrl("asset://styles/styles/basic-v7.json");
mMapFragment.getMap().setStyleUrl("asset://styles/basic-v7.json");
mMapFragment.getMap().removeAllClasses();
mClassSpinner.setVisibility(View.INVISIBLE);
mClassSpinner.setAdapter(null);
@@ -232,7 +232,7 @@ public class MainActivity extends ActionBarActivity {
// Outdoors
case 2:
mMapFragment.getMap().setStyleUrl("asset://styles/styles/outdoors-v7.json");
mMapFragment.getMap().setStyleUrl("asset://styles/outdoors-v7.json");
mMapFragment.getMap().removeAllClasses();
mClassSpinner.setVisibility(View.VISIBLE);
mClassSpinner.setAdapter(mOutdoorsClassAdapter);
@@ -241,7 +241,7 @@ public class MainActivity extends ActionBarActivity {
// Satellite
case 3:
mMapFragment.getMap().setStyleUrl("asset://styles/styles/satellite-v7.json");
mMapFragment.getMap().setStyleUrl("asset://styles/satellite-v7.json");
mMapFragment.getMap().removeAllClasses();
mClassSpinner.setVisibility(View.VISIBLE);
mClassSpinner.setAdapter(mSatelliteClassAdapter);
@@ -250,7 +250,7 @@ public class MainActivity extends ActionBarActivity {
// Pencil
case 4:
mMapFragment.getMap().setStyleUrl("asset://styles/styles/pencil-v7.json");
mMapFragment.getMap().setStyleUrl("asset://styles/pencil-v7.json");
mMapFragment.getMap().removeAllClasses();
mClassSpinner.setVisibility(View.INVISIBLE);
mClassSpinner.setAdapter(null);
@@ -259,7 +259,7 @@ public class MainActivity extends ActionBarActivity {
// Empty
case 5:
mMapFragment.getMap().setStyleUrl("asset://styles/styles/empty-v7.json");
mMapFragment.getMap().setStyleUrl("asset://styles/empty-v7.json");
mMapFragment.getMap().removeAllClasses();
mClassSpinner.setVisibility(View.INVISIBLE);
mClassSpinner.setAdapter(null);

View File

@@ -25,6 +25,9 @@
'libraries': [
'<@(uv_static_libs)',
],
'defines': [
'-DMBGL_ASSET_FS'
],
},
'conditions': [
@@ -37,6 +40,20 @@
}],
],
'direct_dependent_settings': {
'conditions': [
['OS == "mac"', {
'xcode_settings': {
'OTHER_CFLAGS': [ '<@(defines)' ],
'OTHER_CPLUSPLUSFLAGS': [ '<@(defines)' ],
}
}, {
'cflags': [ '<@(defines)' ],
'cflags_cc': [ '<@(defines)' ],
}]
],
},
'link_settings': {
'conditions': [
['OS == "mac"', {

View File

@@ -33,6 +33,9 @@
'<@(uv_static_libs)',
'<@(zip_static_libs)',
],
'defines': [
'-DMBGL_ASSET_ZIP'
],
},
'conditions': [
@@ -47,6 +50,20 @@
}],
],
'direct_dependent_settings': {
'conditions': [
['OS == "mac"', {
'xcode_settings': {
'OTHER_CFLAGS': [ '<@(defines)' ],
'OTHER_CPLUSPLUSFLAGS': [ '<@(defines)' ],
}
}, {
'cflags': [ '<@(defines)' ],
'cflags_cc': [ '<@(defines)' ],
}]
],
},
'link_settings': {
'conditions': [
['OS == "mac"', {

View File

@@ -14,6 +14,24 @@
'../include',
],
'conditions': [
['host == "android"', {
'variables': {
# Android uses libzip and openssl to set CURL's CA bundle.
'cflags_cc': [ '<@(zip_cflags)', '<@(openssl_cflags)' ],
'ldflags': [ '<@(zip_ldflags)', ],
'libraries': [ '<@(zip_static_libs)', ],
},
}],
['OS == "mac"', {
'xcode_settings': {
'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags_cc)' ],
},
}, {
'cflags_cc': [ '<@(cflags_cc)' ],
}],
],
'variables': {
'cflags_cc': [
'<@(uv_cflags)',
@@ -30,16 +48,6 @@
],
},
'conditions': [
['OS == "mac"', {
'xcode_settings': {
'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags_cc)' ],
},
}, {
'cflags_cc': [ '<@(cflags_cc)' ],
}],
],
'link_settings': {
'conditions': [
['OS == "mac"', {

View File

@@ -78,8 +78,10 @@ protected:
public:
const Resource resource;
private:
protected:
DefaultFileSource *source = nullptr;
private:
std::set<Request *> observers;
};

View File

@@ -18,8 +18,8 @@ class SharedRequestBase;
class DefaultFileSource : public FileSource {
public:
DefaultFileSource(FileCache *cache);
DefaultFileSource(FileCache *cache, uv_loop_t *loop);
DefaultFileSource(FileCache *cache, const std::string &root = "");
DefaultFileSource(FileCache *cache, uv_loop_t *loop, const std::string &root = "");
~DefaultFileSource();
Request *request(const Resource &resource, uv_loop_t *loop, Callback callback);
@@ -30,6 +30,9 @@ public:
void notify(SharedRequestBase *sharedRequest, const std::set<Request *> &observers,
std::shared_ptr<const Response> response, FileCache::Hint hint);
public:
const std::string assetRoot;
private:
struct ActionDispatcher;
struct AddRequestAction;

View File

@@ -3,7 +3,6 @@
#include <mbgl/util/std.hpp>
#include <mbgl/util/util.hpp>
#include <mbgl/util/uv.hpp>
#include <mbgl/platform/platform.hpp>
#include <uv.h>
@@ -63,7 +62,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::assetRoot() + "/" + url.substr(8);
path = request->source->assetRoot + "/" + url.substr(8);
}
uv_fs_open(loop, &req, path.c_str(), O_RDONLY, S_IRUSR, fileOpened);

View File

@@ -2,8 +2,8 @@
#include <mbgl/storage/default/thread_context.hpp>
#include <mbgl/android/jni.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/platform/platform.hpp>
#include "uv_zip.h"
@@ -26,12 +26,12 @@ public:
AssetZipContext(uv_loop_t *loop);
~AssetZipContext();
uv_zip_t *getHandle();
void returnHandle(uv_zip_t *zip);
uv_zip_t *getHandle(const std::string &path);
void returnHandle(const std::string &path, 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;
std::map<std::string, std::forward_list<uv_zip_t *>> handles;
};
// -------------------------------------------------------------------------------------------------
@@ -42,28 +42,30 @@ template<> pthread_once_t ThreadContext<AssetZipContext>::once = PTHREAD_ONCE_IN
AssetZipContext::AssetZipContext(uv_loop_t *loop_) : ThreadContext(loop_) {
}
uv_zip_t *AssetZipContext::getHandle() {
if (!handles.empty()) {
auto zip = handles.front();
handles.pop_front();
uv_zip_t *AssetZipContext::getHandle(const std::string &path) {
auto &list = handles[path];
if (!list.empty()) {
auto zip = list.front();
list.pop_front();
return zip;
} else {
return nullptr;
}
}
void AssetZipContext::returnHandle(uv_zip_t *zip) {
uv_zip_cleanup(zip);
handles.push_front(zip);
void AssetZipContext::returnHandle(const std::string &path, uv_zip_t *zip) {
handles[path].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_;
});
for (auto &list : handles) {
for (auto zip : list.second) {
uv_zip_discard(loop, zip, [](uv_zip_t *zip_) {
uv_zip_cleanup(zip_);
delete zip_;
});
}
}
handles.clear();
}
@@ -82,6 +84,7 @@ public:
private:
AssetZipContext &context;
AssetRequest *request = nullptr;
const std::string root;
const std::string path;
std::unique_ptr<Response> response;
uv_buf_t buffer;
@@ -111,8 +114,9 @@ AssetRequestImpl::~AssetRequestImpl() {
AssetRequestImpl::AssetRequestImpl(AssetRequest *request_, uv_loop_t *loop)
: context(*AssetZipContext::Get(loop)),
request(request_),
path(request->resource.url.substr(8)) {
auto zip = context.getHandle();
root(request->source->assetRoot),
path(std::string { "assets/" } + request->resource.url.substr(8)) {
auto zip = context.getHandle(root);
if (zip) {
archiveOpened(zip);
} else {
@@ -124,9 +128,12 @@ void AssetRequestImpl::openZipArchive() {
uv_fs_t *req = new uv_fs_t();
req->data = this;
assert(request);
assert(request->source);
// 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) {
uv_fs_open(context.loop, req, root.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));
@@ -191,6 +198,10 @@ void AssetRequestImpl::fileStated(uv_zip_t *zip) {
response->modified = zip->stat->mtime;
}
if (zip->stat->valid & ZIP_STAT_INDEX) {
response->etag = std::to_string(zip->stat->index);
}
uv_zip_fopen(context.loop, zip, path.c_str(), 0, INVOKE_MEMBER(fileOpened));
}
}
@@ -239,7 +250,7 @@ void AssetRequestImpl::fileClosed(uv_zip_t *zip) {
void AssetRequestImpl::cleanup(uv_zip_t *zip) {
MBGL_VERIFY_THREAD(tid);
context.returnHandle(zip);
context.returnHandle(root, zip);
delete this;
}

View File

@@ -1,11 +1,18 @@
#include <mbgl/storage/default/http_request.hpp>
#include <mbgl/storage/default/http_context.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/time.hpp>
#include <curl/curl.h>
#ifdef __ANDROID__
#include <mbgl/android/jni.hpp>
#include <zip.h>
#include <openssl/ssl.h>
#endif
#include <queue>
#include <map>
#include <cassert>
@@ -125,6 +132,8 @@ private:
int attempts = 0;
static const int maxAttempts = 4;
char error[CURL_ERROR_SIZE];
};
@@ -317,6 +326,104 @@ int HTTPCURLContext::startTimeout(CURLM * /* multi */, long timeout_ms, void *us
// -------------------------------------------------------------------------------------------------
#ifdef __ANDROID__
// This function is called to load the CA bundle
// from http://curl.haxx.se/libcurl/c/cacertinmem.html¯
static CURLcode sslctx_function(CURL * /* curl */, void *sslctx, void * /* parm */) {
int error = 0;
struct zip *apk = zip_open(mbgl::android::apkPath.c_str(), 0, &error);
if (apk == nullptr) {
return CURLE_SSL_CACERT_BADFILE;
}
struct zip_file *apkFile = zip_fopen(apk, "assets/ca-bundle.crt", ZIP_FL_NOCASE);
if (apkFile == nullptr) {
zip_close(apk);
apk = nullptr;
return CURLE_SSL_CACERT_BADFILE;
}
struct zip_stat stat;
if (zip_stat(apk, "assets/ca-bundle.crt", ZIP_FL_NOCASE, &stat) != 0) {
zip_fclose(apkFile);
apkFile = nullptr;
zip_close(apk);
apk = nullptr;
return CURLE_SSL_CACERT_BADFILE;
}
if (stat.size > std::numeric_limits<int>::max()) {
zip_fclose(apkFile);
apkFile = nullptr;
zip_close(apk);
apk = nullptr;
return CURLE_SSL_CACERT_BADFILE;
}
const auto pem = util::make_unique<char[]>(stat.size);
if (static_cast<zip_uint64_t>(zip_fread(apkFile, reinterpret_cast<void *>(pem.get()), stat.size)) != stat.size) {
zip_fclose(apkFile);
apkFile = nullptr;
zip_close(apk);
apk = nullptr;
return CURLE_SSL_CACERT_BADFILE;
}
// get a pointer to the X509 certificate store (which may be empty!)
X509_STORE *store = SSL_CTX_get_cert_store((SSL_CTX *)sslctx);
if (store == nullptr) {
return CURLE_SSL_CACERT_BADFILE;
}
// get a BIO
BIO *bio = BIO_new_mem_buf(pem.get(), static_cast<int>(stat.size));
if (bio == nullptr) {
store = nullptr;
return CURLE_SSL_CACERT_BADFILE;
}
// use it to read the PEM formatted certificate from memory into an X509
// structure that SSL can use
X509 *cert = nullptr;
while (PEM_read_bio_X509(bio, &cert, 0, nullptr) != nullptr) {
if (cert == nullptr) {
BIO_free(bio);
bio = nullptr;
store = nullptr;
return CURLE_SSL_CACERT_BADFILE;
}
// add our certificate to this store
if (X509_STORE_add_cert(store, cert) == 0) {
X509_free(cert);
cert = nullptr;
BIO_free(bio);
bio = nullptr;
store = nullptr;
return CURLE_SSL_CACERT_BADFILE;
}
X509_free(cert);
cert = nullptr;
}
// decrease reference counts
BIO_free(bio);
bio = nullptr;
zip_fclose(apkFile);
apkFile = nullptr;
zip_close(apk);
apk = nullptr;
// all set to go
return CURLE_OK;
}
#endif
HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop, std::unique_ptr<Response> response_)
: context(HTTPCURLContext::Get(loop)),
request(request_),
@@ -325,6 +432,9 @@ HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop, std::un
assert(request);
context->addRequest(request);
// Zero out the error buffer.
memset(error, 0, sizeof(error));
// If there's already a response, set the correct etags/modified headers to make sure we are
// getting a 304 response if possible. This avoids redownloading unchanged data.
if (existingResponse) {
@@ -343,7 +453,13 @@ HTTPRequestImpl::HTTPRequestImpl(HTTPRequest *request_, uv_loop_t *loop, std::un
}
handleError(curl_easy_setopt(handle, CURLOPT_PRIVATE, this));
handleError(curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, error));
#ifdef __ANDROID__
handleError(curl_easy_setopt(handle, CURLOPT_SSLCERTTYPE, "PEM"));
handleError(curl_easy_setopt(handle, CURLOPT_SSL_CTX_FUNCTION, sslctx_function));
#else
handleError(curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt"));
#endif
handleError(curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1));
handleError(curl_easy_setopt(handle, CURLOPT_URL, request->resource.url.c_str()));
handleError(curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeCallback));
@@ -550,21 +666,19 @@ void HTTPRequestImpl::handleResult(CURLcode code) {
// Add human-readable error code
if (code != CURLE_OK) {
response->message = curl_easy_strerror(code);
response->status = Response::Error;
response->message = std::string { curl_easy_strerror(code) } + ": " + error;
switch (code) {
case CURLE_COULDNT_RESOLVE_PROXY:
case CURLE_COULDNT_RESOLVE_HOST:
case CURLE_COULDNT_CONNECT:
response->status = Response::Error;
return finish(ResponseStatus::ConnectionError);
case CURLE_OPERATION_TIMEDOUT:
response->status = Response::Error;
return finish(ResponseStatus::TemporaryError);
default:
response->status = Response::Error;
return finish(ResponseStatus::PermanentError);
}
} else {

View File

@@ -6,7 +6,8 @@
void uv__zip_open_error(uv_zip_t *zip, int error) {
zip->result = -error;
assert(zip);
zip->result = -error;
if (zip->message) {
free((char *)zip->message);
zip->message = NULL;
@@ -17,16 +18,18 @@ void uv__zip_open_error(uv_zip_t *zip, int error) {
}
void uv__zip_store_error(uv_zip_t *zip, const char *message) {
assert(zip);
if (zip->message) {
free((char *)zip->message);
zip->message = NULL;
}
const unsigned long length = strlen(message);
const unsigned long length = strlen(message) + 1;
zip->message = malloc(length);
strncpy((char *)zip->message, message, length);
}
void uv__zip_error(uv_zip_t *zip) {
assert(zip);
int error;
zip_error_get(zip->archive, &error, NULL);
zip->result = -error;
@@ -34,6 +37,7 @@ void uv__zip_error(uv_zip_t *zip) {
}
void uv__zip_file_error(uv_zip_t *zip) {
assert(zip);
int error;
zip_file_error_get(zip->file, &error, NULL);
zip->result = -error;
@@ -42,6 +46,7 @@ void uv__zip_file_error(uv_zip_t *zip) {
void uv__zip_work_open(uv_work_t *req) {
uv_zip_t *zip = (uv_zip_t *)req->data;
assert(zip);
assert(!zip->archive);
int error;
@@ -55,6 +60,7 @@ void uv__zip_work_open(uv_work_t *req) {
void uv__zip_work_fdopen(uv_work_t *req) {
uv_zip_t *zip = (uv_zip_t *)req->data;
assert(zip);
assert(!zip->archive);
// extract the fd
@@ -73,6 +79,7 @@ void uv__zip_work_fdopen(uv_work_t *req) {
void uv__zip_work_stat(uv_work_t *req) {
uv_zip_t *zip = (uv_zip_t *)req->data;
assert(zip);
assert(zip->archive);
if (!zip->stat) {
zip->stat = malloc(sizeof(struct zip_stat));
@@ -85,6 +92,7 @@ void uv__zip_work_stat(uv_work_t *req) {
void uv__zip_work_fopen(uv_work_t *req) {
uv_zip_t *zip = (uv_zip_t *)req->data;
assert(zip);
assert(zip->archive);
zip->file = zip_fopen(zip->archive, zip->path, zip->flags);
if (!zip->file) {
@@ -94,6 +102,7 @@ void uv__zip_work_fopen(uv_work_t *req) {
void uv__zip_work_fread(uv_work_t *req) {
uv_zip_t *zip = (uv_zip_t *)req->data;
assert(zip);
assert(zip->file);
assert(zip->buf);
zip->result = zip_fread(zip->file, zip->buf->base, zip->buf->len);
@@ -104,10 +113,13 @@ void uv__zip_work_fread(uv_work_t *req) {
void uv__zip_work_fclose(uv_work_t *req) {
uv_zip_t *zip = (uv_zip_t *)req->data;
assert(zip);
assert(zip->file);
zip->result = zip_fclose(zip->file);
if (zip->result != 0) {
uv__zip_file_error(zip);
} else {
zip->file = NULL;
}
}
@@ -121,12 +133,14 @@ void uv__zip_work_discard(uv_work_t *req) {
void uv__zip_after_work(uv_work_t *req, int status) {
uv_zip_t *zip = (uv_zip_t *)req->data;
assert(zip);
if (zip->cb) {
zip->cb(zip);
}
}
void uv_zip_init(uv_zip_t *zip) {
assert(zip);
zip->work.data = zip;
zip->message = NULL;
zip->stat = NULL;
@@ -134,6 +148,7 @@ void uv_zip_init(uv_zip_t *zip) {
}
void uv_zip_cleanup(uv_zip_t *zip) {
assert(zip);
zip->data = NULL;
zip->flags = 0;
zip->result = 0;
@@ -155,6 +170,11 @@ 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) {
assert(loop);
assert(zip);
assert(path);
assert(strlen(path));
zip->result = 0;
zip->path = path;
zip->flags = flags;
zip->cb = cb;
@@ -162,6 +182,10 @@ int uv_zip_open(uv_loop_t* loop, uv_zip_t *zip, const char *path, zip_flags_t fl
}
int uv_zip_fdopen(uv_loop_t* loop, uv_zip_t *zip, uv_file fd, int flags, uv_zip_cb cb) {
assert(loop);
assert(zip);
assert(fd);
zip->result = 0;
zip->path = malloc(sizeof(uv_file));
*(uv_file *)zip->path = fd;
zip->flags = flags;
@@ -170,6 +194,12 @@ int uv_zip_fdopen(uv_loop_t* loop, uv_zip_t *zip, uv_file fd, int flags, uv_zip_
}
int uv_zip_stat(uv_loop_t* loop, uv_zip_t *zip, const char *fname, zip_flags_t flags, uv_zip_cb cb) {
assert(loop);
assert(zip);
assert(fname);
assert(strlen(fname));
assert(zip->archive);
zip->result = 0;
zip->path = fname;
zip->flags = flags;
zip->cb = cb;
@@ -177,19 +207,37 @@ int uv_zip_stat(uv_loop_t* loop, uv_zip_t *zip, const char *fname, zip_flags_t f
}
int uv_zip_fopen(uv_loop_t* loop, uv_zip_t *zip, const char *fname, zip_flags_t flags, uv_zip_cb cb) {
assert(loop);
assert(zip);
assert(fname);
assert(strlen(fname));
assert(zip->archive);
zip->result = 0;
zip->path = fname;
zip->flags = flags;
zip->file = NULL;
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) {
assert(loop);
assert(zip);
assert(file);
assert(zip->archive);
zip->result = 0;
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) {
assert(loop);
assert(zip);
assert(file);
assert(buf);
assert(zip->archive);
zip->result = 0;
zip->file = file;
zip->buf = buf;
zip->cb = cb;
@@ -197,6 +245,10 @@ int uv_zip_fread(uv_loop_t* loop, uv_zip_t *zip, struct zip_file *file, uv_buf_t
}
int uv_zip_discard(uv_loop_t* loop, uv_zip_t *zip, uv_zip_cb cb) {
assert(loop);
assert(zip);
assert(!zip->file);
zip->result = 0;
zip->cb = cb;
return uv_queue_work(loop, &zip->work, uv__zip_work_discard, uv__zip_after_work);
}

View File

@@ -43,7 +43,7 @@ void Source::load(Map& map, FileSource& fileSource) {
const std::string url = util::mapbox::normalizeSourceURL(info.url, map.getAccessToken());
fileSource.request({ Resource::Kind::JSON, url }, **map.loop, [source, &map](const Response &res) {
if (res.status != Response::Successful) {
Log::Warning(Event::General, "failed to load source TileJSON");
Log::Warning(Event::General, "Failed to load source TileJSON: %s", res.message.c_str());
return;
}
@@ -51,7 +51,7 @@ void Source::load(Map& map, FileSource& fileSource) {
d.Parse<0>(res.data.c_str());
if (d.HasParseError()) {
Log::Warning(Event::General, "invalid source TileJSON");
Log::Warning(Event::General, "Invalid source TileJSON; Parse Error at %d: %s", d.GetErrorOffset(), d.GetParseError());
return;
}

View File

@@ -82,7 +82,7 @@ void Sprite::load(FileSource& fileSource) {
sprite->parseImage();
sprite->complete();
} else {
Log::Warning(Event::Sprite, "Failed to load sprite image: Error %s", res.message.c_str());
Log::Warning(Event::Sprite, "Failed to load sprite image: %s", res.message.c_str());
if (!sprite->future.valid()) {
sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
}

View File

@@ -4,11 +4,13 @@
#include <mbgl/storage/default/http_request.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/util/async_queue.hpp>
#include <mbgl/util/util.hpp>
#include <mbgl/util/variant.hpp>
#include <mbgl/platform/log.hpp>
#pragma GCC diagnostic push
#ifndef __clang__
@@ -49,8 +51,9 @@ struct DefaultFileSource::StopAction {
};
DefaultFileSource::DefaultFileSource(FileCache *cache_)
: loop(uv_loop_new()),
DefaultFileSource::DefaultFileSource(FileCache *cache_, const std::string &root)
: assetRoot(root.empty() ? platform::assetRoot() : root),
loop(uv_loop_new()),
cache(cache_),
queue(new Queue(loop, [this](Action &action) {
mapbox::util::apply_visitor(ActionDispatcher{*this}, action);
@@ -63,8 +66,9 @@ DefaultFileSource::DefaultFileSource(FileCache *cache_)
}) {
}
DefaultFileSource::DefaultFileSource(FileCache *cache_, uv_loop_t *loop_)
: loop(loop_),
DefaultFileSource::DefaultFileSource(FileCache *cache_, uv_loop_t *loop_, const std::string &root)
: assetRoot(root.empty() ? platform::assetRoot() : root),
loop(loop_),
cache(cache_),
queue(new Queue(loop, [this](Action &action) {
mapbox::util::apply_visitor(ActionDispatcher{*this}, action);

BIN
test/fixtures/storage/assets.zip vendored Normal file

Binary file not shown.

1
test/fixtures/storage/nonempty vendored Normal file
View File

@@ -0,0 +1 @@
content is here

View File

@@ -4,12 +4,16 @@
#include <mbgl/storage/default_file_source.hpp>
TEST_F(Storage, ReadDirectory) {
TEST_F(Storage, AssetReadDirectory) {
SCOPED_TEST(ReadDirectory)
using namespace mbgl;
#ifdef MBGL_ASSET_ZIP
DefaultFileSource fs(nullptr, uv_default_loop(), "test/fixtures/storage/assets.zip");
#else
DefaultFileSource fs(nullptr, uv_default_loop());
#endif
fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage" }, uv_default_loop(), [&](const Response &res) {
EXPECT_EQ(Response::Error, res.status);
@@ -17,7 +21,11 @@ TEST_F(Storage, ReadDirectory) {
EXPECT_EQ(0, res.expires);
EXPECT_EQ(0, res.modified);
EXPECT_EQ("", res.etag);
#ifdef MBGL_ASSET_ZIP
EXPECT_EQ("No such file", res.message);
#elif MBGL_ASSET_FS
EXPECT_EQ("illegal operation on a directory", res.message);
#endif
ReadDirectory.finish();
});

View File

@@ -5,12 +5,16 @@
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/platform/platform.hpp>
TEST_F(Storage, EmptyFile) {
TEST_F(Storage, AssetEmptyFile) {
SCOPED_TEST(EmptyFile)
using namespace mbgl;
#ifdef MBGL_ASSET_ZIP
DefaultFileSource fs(nullptr, uv_default_loop(), "test/fixtures/storage/assets.zip");
#else
DefaultFileSource fs(nullptr, uv_default_loop());
#endif
fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/empty" },
uv_default_loop(),
@@ -27,12 +31,43 @@ TEST_F(Storage, EmptyFile) {
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
}
TEST_F(Storage, NonExistentFile) {
TEST_F(Storage, AssetNonEmptyFile) {
SCOPED_TEST(NonEmptyFile)
using namespace mbgl;
#ifdef MBGL_ASSET_ZIP
DefaultFileSource fs(nullptr, uv_default_loop(), "test/fixtures/storage/assets.zip");
#else
DefaultFileSource fs(nullptr, uv_default_loop());
#endif
fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/nonempty" },
uv_default_loop(),
[&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ(16ul, res.data.size());
EXPECT_EQ(0, res.expires);
EXPECT_LT(1420000000, res.modified);
EXPECT_NE("", res.etag);
EXPECT_EQ("", res.message);
EXPECT_EQ("content is here\n", res.data);
NonEmptyFile.finish();
});
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
}
TEST_F(Storage, AssetNonExistentFile) {
SCOPED_TEST(NonExistentFile)
using namespace mbgl;
#ifdef MBGL_ASSET_ZIP
DefaultFileSource fs(nullptr, uv_default_loop(), "test/fixtures/storage/assets.zip");
#else
DefaultFileSource fs(nullptr, uv_default_loop());
#endif
fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/does_not_exist" },
uv_default_loop(),
@@ -42,7 +77,11 @@ TEST_F(Storage, NonExistentFile) {
EXPECT_EQ(0, res.expires);
EXPECT_EQ(0, res.modified);
EXPECT_EQ("", res.etag);
#ifdef MBGL_ASSET_ZIP
EXPECT_EQ("No such file", res.message);
#elif MBGL_ASSET_FS
EXPECT_EQ("no such file or directory", res.message);
#endif
NonExistentFile.finish();
});

View File

@@ -44,7 +44,7 @@ TEST_F(Storage, HTTPError) {
EXPECT_LT(1.5, duration) << "Resource wasn't retried the correct number of times";
EXPECT_GT(1.7, duration) << "Resource wasn't retried the correct number of times";
EXPECT_EQ(Response::Error, res.status);
EXPECT_TRUE(res.message == "Couldn't connect to server" || res.message == "The operation couldn’t be completed. (NSURLErrorDomain error -1004.)");
EXPECT_TRUE(res.message == "Couldn't connect to server: couldn't connect to host" || res.message == "The operation couldn’t be completed. (NSURLErrorDomain error -1004.)") << res.message;
EXPECT_EQ("", res.data);
EXPECT_EQ(0, res.expires);
EXPECT_EQ(0, res.modified);