194 lines
6.4 KiB
C++
194 lines
6.4 KiB
C++
#include <mbgl/storage/http_file_source.hpp>
|
|
#include <mbgl/storage/resource.hpp>
|
|
#include <mbgl/storage/response.hpp>
|
|
#include <mbgl/platform/log.hpp>
|
|
|
|
#include <mbgl/util/async_task.hpp>
|
|
#include <mbgl/util/util.hpp>
|
|
#include <mbgl/util/string.hpp>
|
|
#include <mbgl/util/http_header.hpp>
|
|
|
|
#include <jni/jni.hpp>
|
|
#include "attach_env.hpp"
|
|
|
|
namespace mbgl {
|
|
|
|
class HTTPFileSource::Impl {
|
|
public:
|
|
android::UniqueEnv env { android::AttachEnv() };
|
|
};
|
|
|
|
class HTTPRequest : public AsyncRequest {
|
|
public:
|
|
static constexpr auto Name() { return "com/mapbox/mapboxsdk/http/HTTPRequest"; };
|
|
|
|
HTTPRequest(jni::JNIEnv&, const Resource&, FileSource::Callback);
|
|
~HTTPRequest();
|
|
|
|
void onFailure(jni::JNIEnv&, int type, jni::String message);
|
|
void onResponse(jni::JNIEnv&, int code,
|
|
jni::String etag, jni::String modified,
|
|
jni::String cacheControl, jni::String expires,
|
|
jni::String retryAfter, jni::String xRateLimitReset,
|
|
jni::Array<jni::jbyte> body);
|
|
|
|
static jni::Class<HTTPRequest> javaClass;
|
|
jni::UniqueObject<HTTPRequest> javaRequest;
|
|
|
|
private:
|
|
Resource resource;
|
|
FileSource::Callback callback;
|
|
Response response;
|
|
|
|
util::AsyncTask async { [this] {
|
|
// Calling `callback` may result in deleting `this`. Copy data to temporaries first.
|
|
auto callback_ = callback;
|
|
auto response_ = response;
|
|
callback_(response_);
|
|
} };
|
|
|
|
static const int connectionError = 0;
|
|
static const int temporaryError = 1;
|
|
static const int permanentError = 2;
|
|
};
|
|
|
|
jni::Class<HTTPRequest> HTTPRequest::javaClass;
|
|
|
|
namespace android {
|
|
|
|
void RegisterNativeHTTPRequest(jni::JNIEnv& env) {
|
|
HTTPRequest::javaClass = *jni::Class<HTTPRequest>::Find(env).NewGlobalRef(env).release();
|
|
|
|
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
|
|
|
|
jni::RegisterNativePeer<HTTPRequest>(env, HTTPRequest::javaClass, "mNativePtr",
|
|
METHOD(&HTTPRequest::onFailure, "nativeOnFailure"),
|
|
METHOD(&HTTPRequest::onResponse, "nativeOnResponse"));
|
|
}
|
|
|
|
} // namespace android
|
|
|
|
HTTPRequest::HTTPRequest(jni::JNIEnv& env, const Resource& resource_, FileSource::Callback callback_)
|
|
: resource(resource_),
|
|
callback(callback_) {
|
|
std::string etagStr;
|
|
std::string modifiedStr;
|
|
|
|
if (resource.priorEtag) {
|
|
etagStr = *resource.priorEtag;
|
|
} else if (resource.priorModified) {
|
|
modifiedStr = util::rfc1123(*resource.priorModified);
|
|
}
|
|
|
|
jni::UniqueLocalFrame frame = jni::PushLocalFrame(env, 10);
|
|
|
|
static auto constructor =
|
|
javaClass.GetConstructor<jni::jlong, jni::String, jni::String, jni::String>(env);
|
|
|
|
javaRequest = javaClass.New(env, constructor,
|
|
reinterpret_cast<jlong>(this),
|
|
jni::Make<jni::String>(env, resource.url),
|
|
jni::Make<jni::String>(env, etagStr),
|
|
jni::Make<jni::String>(env, modifiedStr)).NewGlobalRef(env);
|
|
}
|
|
|
|
HTTPRequest::~HTTPRequest() {
|
|
android::UniqueEnv env = android::AttachEnv();
|
|
|
|
static auto cancel = javaClass.GetMethod<void ()>(*env, "cancel");
|
|
|
|
javaRequest->Call(*env, cancel);
|
|
}
|
|
|
|
void HTTPRequest::onResponse(jni::JNIEnv& env, int code,
|
|
jni::String etag, jni::String modified,
|
|
jni::String cacheControl, jni::String expires,
|
|
jni::String jRetryAfter, jni::String jXRateLimitReset,
|
|
jni::Array<jni::jbyte> body) {
|
|
|
|
using Error = Response::Error;
|
|
|
|
if (etag) {
|
|
response.etag = jni::Make<std::string>(env, etag);
|
|
}
|
|
|
|
if (modified) {
|
|
response.modified = util::parseTimestamp(jni::Make<std::string>(env, modified).c_str());
|
|
}
|
|
|
|
if (cacheControl) {
|
|
response.expires = http::CacheControl::parse(jni::Make<std::string>(env, cacheControl).c_str()).toTimePoint();
|
|
}
|
|
|
|
if (expires) {
|
|
response.expires = util::parseTimestamp(jni::Make<std::string>(env, expires).c_str());
|
|
}
|
|
|
|
if (code == 200) {
|
|
if (body) {
|
|
auto data = std::make_shared<std::string>(body.Length(env), char());
|
|
jni::GetArrayRegion(env, *body, 0, data->size(), reinterpret_cast<jbyte*>(&(*data)[0]));
|
|
response.data = data;
|
|
} else {
|
|
response.data = std::make_shared<std::string>();
|
|
}
|
|
} else if (code == 204 || (code == 404 && resource.kind == Resource::Kind::Tile)) {
|
|
response.noContent = true;
|
|
} else if (code == 304) {
|
|
response.notModified = true;
|
|
} else if (code == 404) {
|
|
response.error = std::make_unique<Error>(Error::Reason::NotFound, "HTTP status code 404");
|
|
} else if (code == 429) {
|
|
optional<std::string> retryAfter;
|
|
optional<std::string> xRateLimitReset;
|
|
if (jRetryAfter) {
|
|
retryAfter = jni::Make<std::string>(env, jRetryAfter);
|
|
}
|
|
if (jXRateLimitReset) {
|
|
xRateLimitReset = jni::Make<std::string>(env, jXRateLimitReset);
|
|
}
|
|
response.error = std::make_unique<Error>(Error::Reason::RateLimit, "HTTP status code 429", http::parseRetryHeaders(retryAfter, xRateLimitReset));
|
|
} else if (code >= 500 && code < 600) {
|
|
response.error = std::make_unique<Error>(Error::Reason::Server, std::string{ "HTTP status code " } + std::to_string(code));
|
|
} else {
|
|
response.error = std::make_unique<Error>(Error::Reason::Other, std::string{ "HTTP status code " } + std::to_string(code));
|
|
}
|
|
|
|
async.send();
|
|
}
|
|
|
|
void HTTPRequest::onFailure(jni::JNIEnv& env, int type, jni::String message) {
|
|
std::string messageStr = jni::Make<std::string>(env, message);
|
|
|
|
using Error = Response::Error;
|
|
|
|
switch (type) {
|
|
case connectionError:
|
|
response.error = std::make_unique<Error>(Error::Reason::Connection, messageStr);
|
|
break;
|
|
case temporaryError:
|
|
response.error = std::make_unique<Error>(Error::Reason::Server, messageStr);
|
|
break;
|
|
default:
|
|
response.error = std::make_unique<Error>(Error::Reason::Other, messageStr);
|
|
}
|
|
|
|
async.send();
|
|
}
|
|
|
|
HTTPFileSource::HTTPFileSource()
|
|
: impl(std::make_unique<Impl>()) {
|
|
}
|
|
|
|
HTTPFileSource::~HTTPFileSource() = default;
|
|
|
|
std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, Callback callback) {
|
|
return std::make_unique<HTTPRequest>(*impl->env, resource, callback);
|
|
}
|
|
|
|
uint32_t HTTPFileSource::maximumConcurrentRequests() {
|
|
return 20;
|
|
}
|
|
|
|
} // namespace mbgl
|