summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
authorJohn Firebaugh <john.firebaugh@gmail.com>2016-02-15 17:35:13 -0800
committerJohn Firebaugh <john.firebaugh@gmail.com>2016-04-14 14:18:19 -0700
commit3af3e72bb3cb3f05b33be304d59e66cc244ef4d9 (patch)
treeef1d237c3083694307081c219c9da94ae43fe540 /platform
parent6d88a23a60bc0a6dc9945bf09a659d15dd827192 (diff)
downloadqtlocation-mapboxgl-3af3e72bb3cb3f05b33be304d59e66cc244ef4d9.tar.gz
[all] Replace HTTPContextBase/HTTPRequestBase with FileSource
Diffstat (limited to 'platform')
-rw-r--r--platform/android/platform.gyp2
-rw-r--r--platform/android/src/http_file_source.cpp (renamed from platform/android/src/http_request_android.cpp)100
-rw-r--r--platform/darwin/src/http_file_source.mm228
-rw-r--r--platform/darwin/src/http_request_nsurl.mm243
-rw-r--r--platform/default/http_file_source.cpp (renamed from platform/default/http_request_curl.cpp)255
-rw-r--r--platform/default/online_file_source.cpp15
-rw-r--r--platform/ios/platform.gyp2
-rw-r--r--platform/linux/platform.gyp2
-rw-r--r--platform/osx/platform.gyp2
9 files changed, 340 insertions, 509 deletions
diff --git a/platform/android/platform.gyp b/platform/android/platform.gyp
index 4954c7031c..599aa586ce 100644
--- a/platform/android/platform.gyp
+++ b/platform/android/platform.gyp
@@ -27,7 +27,7 @@
'src/jni.cpp',
'src/attach_env.cpp',
'src/log_android.cpp',
- 'src/http_request_android.cpp',
+ 'src/http_file_source.cpp',
'src/asset_file_source.cpp',
'../default/thread.cpp',
'../default/string_stdlib.cpp',
diff --git a/platform/android/src/http_request_android.cpp b/platform/android/src/http_file_source.cpp
index 1e0039cdf8..ceb241af4e 100644
--- a/platform/android/src/http_request_android.cpp
+++ b/platform/android/src/http_file_source.cpp
@@ -1,5 +1,4 @@
-#include <mbgl/storage/http_context_base.hpp>
-#include <mbgl/storage/http_request_base.hpp>
+#include <mbgl/storage/http_file_source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/platform/log.hpp>
@@ -7,26 +6,24 @@
#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 {
-namespace android {
-class HTTPContext : public HTTPContextBase {
+class HTTPFileSource::Impl {
public:
- HTTPRequestBase* createRequest(const Resource&, HTTPRequestBase::Callback) final;
- UniqueEnv env { android::AttachEnv() };
+ android::UniqueEnv env { android::AttachEnv() };
};
-class HTTPRequest : public HTTPRequestBase {
+class HTTPRequest : public AsyncRequest {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/http/HTTPRequest"; };
- HTTPRequest(jni::JNIEnv&, const Resource&, Callback);
-
- void cancel() final;
+ HTTPRequest(jni::JNIEnv&, const Resource&, FileSource::Callback);
+ ~HTTPRequest();
void onFailure(jni::JNIEnv&, int type, jni::String message);
void onResponse(jni::JNIEnv&, int code,
@@ -38,12 +35,16 @@ public:
jni::UniqueObject<HTTPRequest> javaRequest;
private:
- void finish();
+ Resource resource;
+ FileSource::Callback callback;
+ Response response;
- std::unique_ptr<Response> response;
- const std::shared_ptr<const Response> existingResponse;
-
- util::AsyncTask async;
+ 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;
@@ -52,6 +53,8 @@ private:
jni::Class<HTTPRequest> HTTPRequest::javaClass;
+namespace android {
+
void RegisterNativeHTTPRequest(jni::JNIEnv& env) {
HTTPRequest::javaClass = *jni::Class<HTTPRequest>::Find(env).NewGlobalRef(env).release();
@@ -62,15 +65,11 @@ void RegisterNativeHTTPRequest(jni::JNIEnv& env) {
METHOD(&HTTPRequest::onResponse, "nativeOnResponse"));
}
-// -------------------------------------------------------------------------------------------------
-
-HTTPRequestBase* HTTPContext::createRequest(const Resource& resource, HTTPRequestBase::Callback callback) {
- return new HTTPRequest(*env, resource, callback);
-}
+} // namespace android
-HTTPRequest::HTTPRequest(jni::JNIEnv& env, const Resource& resource_, Callback callback_)
- : HTTPRequestBase(resource_, callback_),
- async([this] { finish(); }) {
+HTTPRequest::HTTPRequest(jni::JNIEnv& env, const Resource& resource_, FileSource::Callback callback_)
+ : resource(resource_),
+ callback(callback_) {
std::string etagStr;
std::string modifiedStr;
@@ -93,63 +92,53 @@ HTTPRequest::HTTPRequest(jni::JNIEnv& env, const Resource& resource_, Callback c
jni::Make<jni::String>(env, modifiedStr)).NewGlobalRef(env);
}
-void HTTPRequest::cancel() {
- UniqueEnv env = android::AttachEnv();
+HTTPRequest::~HTTPRequest() {
+ android::UniqueEnv env = android::AttachEnv();
static auto cancel = javaClass.GetMethod<void ()>(*env, "cancel");
javaRequest->Call(*env, cancel);
-
- delete this;
-}
-
-void HTTPRequest::finish() {
- assert(response);
- notify(*response);
-
- delete this;
}
void HTTPRequest::onResponse(jni::JNIEnv& env, int code,
jni::String etag, jni::String modified, jni::String cacheControl,
jni::String expires, jni::Array<jni::jbyte> body) {
- response = std::make_unique<Response>();
using Error = Response::Error;
if (etag) {
- response->etag = jni::Make<std::string>(env, etag);
+ response.etag = jni::Make<std::string>(env, etag);
}
if (modified) {
- response->modified = util::parseTimePoint(jni::Make<std::string>(env, modified).c_str());
+ response.modified = util::parseTimePoint(jni::Make<std::string>(env, modified).c_str());
}
if (cacheControl) {
- response->expires = parseCacheControl(jni::Make<std::string>(env, cacheControl).c_str());
+ response.expires = http::CacheControl::parse(jni::Make<std::string>(env, cacheControl).c_str()).toTimePoint();
}
if (expires) {
- response->expires = util::parseTimePoint(jni::Make<std::string>(env, expires).c_str());
+ response.expires = util::parseTimePoint(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;
+ response.data = data;
} else {
- response->data = std::make_shared<std::string>();
+ response.data = std::make_shared<std::string>();
}
} else if (code == 204 || (code == 404 && resource.kind == Resource::Kind::Tile)) {
- response->noContent = true;
+ response.noContent = true;
} else if (code == 304) {
- response->notModified = true;
+ response.notModified = true;
} else if (code == 404) {
- response->error = std::make_unique<Error>(Error::Reason::NotFound, "HTTP status code 404");
+ response.error = std::make_unique<Error>(Error::Reason::NotFound, "HTTP status code 404");
} else if (code >= 500 && code < 600) {
- response->error = std::make_unique<Error>(Error::Reason::Server, std::string{ "HTTP status code " } + std::to_string(code));
+ 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));
+ response.error = std::make_unique<Error>(Error::Reason::Other, std::string{ "HTTP status code " } + std::to_string(code));
}
async.send();
@@ -158,30 +147,33 @@ void HTTPRequest::onResponse(jni::JNIEnv& env, int code,
void HTTPRequest::onFailure(jni::JNIEnv& env, int type, jni::String message) {
std::string messageStr = jni::Make<std::string>(env, message);
- response = std::make_unique<Response>();
using Error = Response::Error;
switch (type) {
case connectionError:
- response->error = std::make_unique<Error>(Error::Reason::Connection, messageStr);
+ response.error = std::make_unique<Error>(Error::Reason::Connection, messageStr);
break;
case temporaryError:
- response->error = std::make_unique<Error>(Error::Reason::Server, messageStr);
+ response.error = std::make_unique<Error>(Error::Reason::Server, messageStr);
break;
default:
- response->error = std::make_unique<Error>(Error::Reason::Other, messageStr);
+ response.error = std::make_unique<Error>(Error::Reason::Other, messageStr);
}
async.send();
}
-} // namespace android
+HTTPFileSource::HTTPFileSource()
+ : impl(std::make_unique<Impl>()) {
+}
+
+HTTPFileSource::~HTTPFileSource() = default;
-std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext() {
- return std::make_unique<android::HTTPContext>();
+std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, Callback callback) {
+ return std::make_unique<HTTPRequest>(*impl->env, resource, callback);
}
-uint32_t HTTPContextBase::maximumConcurrentRequests() {
+uint32_t HTTPFileSource::maximumConcurrentRequests() {
return 20;
}
diff --git a/platform/darwin/src/http_file_source.mm b/platform/darwin/src/http_file_source.mm
new file mode 100644
index 0000000000..c7fdd2aae9
--- /dev/null
+++ b/platform/darwin/src/http_file_source.mm
@@ -0,0 +1,228 @@
+#include <mbgl/storage/http_file_source.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
+
+#include <mbgl/util/http_header.hpp>
+#include <mbgl/util/async_task.hpp>
+
+#import <Foundation/Foundation.h>
+
+#include <mutex>
+
+namespace mbgl {
+
+// Data that is shared between the requesting thread and the thread running the completion handler.
+class HTTPRequestShared {
+public:
+ HTTPRequestShared(Response& response_, util::AsyncTask& async_)
+ : response(response_),
+ async(async_) {
+ }
+
+ void notify(const Response& response_) {
+ std::lock_guard<std::mutex> lock(mutex);
+ if (!cancelled) {
+ response = response_;
+ async.send();
+ }
+ }
+
+ void cancel() {
+ std::lock_guard<std::mutex> lock(mutex);
+ cancelled = true;
+ }
+
+private:
+ std::mutex mutex;
+ bool cancelled = false;
+
+ Response& response;
+ util::AsyncTask& async;
+};
+
+class HTTPRequest : public AsyncRequest {
+public:
+ HTTPRequest(FileSource::Callback callback_)
+ : shared(std::make_shared<HTTPRequestShared>(response, async)),
+ callback(callback_) {
+ }
+
+ ~HTTPRequest() override {
+ shared->cancel();
+ if (task) {
+ [task cancel];
+ }
+ }
+
+ std::shared_ptr<HTTPRequestShared> shared;
+ NSURLSessionDataTask* task = nil;
+
+private:
+ 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_);
+ } };
+};
+
+class HTTPFileSource::Impl {
+public:
+ Impl() {
+ @autoreleasepool {
+ NSURLSessionConfiguration* sessionConfig =
+ [NSURLSessionConfiguration defaultSessionConfiguration];
+ sessionConfig.timeoutIntervalForResource = 30;
+ sessionConfig.HTTPMaximumConnectionsPerHost = 8;
+ sessionConfig.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
+ sessionConfig.URLCache = nil;
+
+ session = [NSURLSession sessionWithConfiguration:sessionConfig];
+
+ // Write user agent string
+ userAgent = @"MapboxGL";
+
+ accountType = [[NSUserDefaults standardUserDefaults] integerForKey:@"MGLMapboxAccountType"];
+ }
+ }
+
+ NSURLSession* session = nil;
+ NSString* userAgent = nil;
+ NSInteger accountType = 0;
+};
+
+HTTPFileSource::HTTPFileSource()
+ : impl(std::make_unique<Impl>()) {
+}
+
+HTTPFileSource::~HTTPFileSource() = default;
+
+uint32_t HTTPFileSource::maximumConcurrentRequests() {
+ return 20;
+}
+
+std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, Callback callback) {
+ auto request = std::make_unique<HTTPRequest>(callback);
+ auto shared = request->shared; // Explicit copy so that it also gets copied into the completion handler block below.
+
+ @autoreleasepool {
+ NSURL* url = [NSURL URLWithString:@(resource.url.c_str())];
+ if (impl->accountType == 0 &&
+ ([url.host isEqualToString:@"mapbox.com"] || [url.host hasSuffix:@".mapbox.com"])) {
+ NSString* absoluteString = [url.absoluteString
+ stringByAppendingFormat:(url.query ? @"&%@" : @"?%@"), @"events=true"];
+ url = [NSURL URLWithString:absoluteString];
+ }
+
+ NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url];
+ if (resource.priorEtag) {
+ [req addValue:@(resource.priorEtag->c_str())
+ forHTTPHeaderField:@"If-None-Match"];
+ } else if (resource.priorModified) {
+ [req addValue:@(util::rfc1123(*resource.priorModified).c_str())
+ forHTTPHeaderField:@"If-Modified-Since"];
+ }
+
+ [req addValue:impl->userAgent forHTTPHeaderField:@"User-Agent"];
+
+ request->task = [impl->session
+ dataTaskWithRequest:req
+ completionHandler:^(NSData* data, NSURLResponse* res, NSError* error) {
+ if (error && [error code] == NSURLErrorCancelled) {
+ return;
+ }
+
+ Response response;
+ using Error = Response::Error;
+
+ if (error) {
+ if (data) {
+ response.data =
+ std::make_shared<std::string>((const char*)[data bytes], [data length]);
+ }
+
+ switch ([error code]) {
+ case NSURLErrorBadServerResponse: // 5xx errors
+ response.error = std::make_unique<Error>(
+ Error::Reason::Server, [[error localizedDescription] UTF8String]);
+ break;
+
+ case NSURLErrorNetworkConnectionLost:
+ case NSURLErrorCannotFindHost:
+ case NSURLErrorCannotConnectToHost:
+ case NSURLErrorDNSLookupFailed:
+ case NSURLErrorNotConnectedToInternet:
+ case NSURLErrorInternationalRoamingOff:
+ case NSURLErrorCallIsActive:
+ case NSURLErrorDataNotAllowed:
+ case NSURLErrorTimedOut:
+ response.error = std::make_unique<Error>(
+ Error::Reason::Connection, [[error localizedDescription] UTF8String]);
+ break;
+
+ default:
+ response.error = std::make_unique<Error>(
+ Error::Reason::Other, [[error localizedDescription] UTF8String]);
+ break;
+ }
+ } else if ([res isKindOfClass:[NSHTTPURLResponse class]]) {
+ const long responseCode = [(NSHTTPURLResponse *)res statusCode];
+
+ NSDictionary *headers = [(NSHTTPURLResponse *)res allHeaderFields];
+ NSString *cache_control = [headers objectForKey:@"Cache-Control"];
+ if (cache_control) {
+ response.expires = http::CacheControl::parse([cache_control UTF8String]).toTimePoint();
+ }
+
+ NSString *expires = [headers objectForKey:@"Expires"];
+ if (expires) {
+ response.expires = util::parseTimePoint([expires UTF8String]);
+ }
+
+ NSString *last_modified = [headers objectForKey:@"Last-Modified"];
+ if (last_modified) {
+ response.modified = util::parseTimePoint([last_modified UTF8String]);
+ }
+
+ NSString *etag = [headers objectForKey:@"ETag"];
+ if (etag) {
+ response.etag = std::string([etag UTF8String]);
+ }
+
+ if (responseCode == 200) {
+ response.data = std::make_shared<std::string>((const char *)[data bytes], [data length]);
+ } else if (responseCode == 204 || (responseCode == 404 && resource.kind == Resource::Kind::Tile)) {
+ response.noContent = true;
+ } else if (responseCode == 304) {
+ response.notModified = true;
+ } else if (responseCode == 404) {
+ response.error =
+ std::make_unique<Error>(Error::Reason::NotFound, "HTTP status code 404");
+ } else if (responseCode >= 500 && responseCode < 600) {
+ response.error =
+ std::make_unique<Error>(Error::Reason::Server, std::string{ "HTTP status code " } +
+ std::to_string(responseCode));
+ } else {
+ response.error =
+ std::make_unique<Error>(Error::Reason::Other, std::string{ "HTTP status code " } +
+ std::to_string(responseCode));
+ }
+ } else {
+ // This should never happen.
+ response.error = std::make_unique<Error>(Error::Reason::Other,
+ "Response class is not NSHTTPURLResponse");
+ }
+
+ shared->notify(response);
+ }];
+
+ [request->task resume];
+ }
+
+ return std::move(request);
+}
+
+}
diff --git a/platform/darwin/src/http_request_nsurl.mm b/platform/darwin/src/http_request_nsurl.mm
deleted file mode 100644
index edd8341709..0000000000
--- a/platform/darwin/src/http_request_nsurl.mm
+++ /dev/null
@@ -1,243 +0,0 @@
-#include <mbgl/storage/http_context_base.hpp>
-#include <mbgl/storage/http_request_base.hpp>
-#include <mbgl/storage/resource.hpp>
-#include <mbgl/storage/response.hpp>
-
-#include <mbgl/util/async_task.hpp>
-#include <mbgl/util/run_loop.hpp>
-
-#import <Foundation/Foundation.h>
-
-#include <map>
-#include <cassert>
-#include <mutex>
-
-namespace mbgl {
-
-class HTTPNSURLContext;
-
-class HTTPNSURLRequest : public HTTPRequestBase {
-public:
- HTTPNSURLRequest(HTTPNSURLContext*, Resource, Callback);
-
- void cancel() final;
-
-private:
- static std::unique_ptr<Response> handleResult(NSData *data, NSURLResponse *res, NSError *error, Resource);
- void handleResponse();
-
- HTTPNSURLContext *context = nullptr;
- std::shared_ptr<std::pair<bool, std::mutex>> cancelled;
- NSURLSessionDataTask *task = nullptr;
- std::unique_ptr<Response> response;
- util::AsyncTask async;
-};
-
-// -------------------------------------------------------------------------------------------------
-
-class HTTPNSURLContext : public HTTPContextBase {
-public:
- HTTPNSURLContext();
-
- HTTPRequestBase* createRequest(const Resource&, HTTPRequestBase::Callback) final;
-
- NSURLSession *session = nil;
- NSString *userAgent = nil;
- NSInteger accountType = 0;
-};
-
-HTTPNSURLContext::HTTPNSURLContext() {
- @autoreleasepool {
- NSURLSessionConfiguration* sessionConfig =
- [NSURLSessionConfiguration defaultSessionConfiguration];
- sessionConfig.timeoutIntervalForResource = 30;
- sessionConfig.HTTPMaximumConnectionsPerHost = 8;
- sessionConfig.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
- sessionConfig.URLCache = nil;
-
- session = [NSURLSession sessionWithConfiguration:sessionConfig];
-
- // Write user agent string
- userAgent = @"MapboxGL";
-
- accountType = [[NSUserDefaults standardUserDefaults] integerForKey:@"MGLMapboxAccountType"];
- }
-}
-
-HTTPRequestBase* HTTPNSURLContext::createRequest(const Resource& resource, HTTPRequestBase::Callback callback) {
- return new HTTPNSURLRequest(this, resource, callback);
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HTTPNSURLRequest::HTTPNSURLRequest(HTTPNSURLContext* context_,
- Resource resource_,
- Callback callback_)
- : HTTPRequestBase(resource_, callback_),
- context(context_),
- async([this] { handleResponse(); }) {
-
- // Ensure that a stack-allocated std::shared_ptr gets copied into the Objective-C
- // block used as the completion handler below. Objective-C will implicitly copy captured
- // stack variables. For member variable access it will implicitly copy the this pointer.
- // That wouldn't work here because we need the block to have its own shared_ptr.
- auto cancelled_ = cancelled = std::make_shared<std::pair<bool, std::mutex>>();
- cancelled->first = false;
-
- @autoreleasepool {
- NSURL* url = [NSURL URLWithString:@(resource.url.c_str())];
- if (context->accountType == 0 &&
- ([url.host isEqualToString:@"mapbox.com"] || [url.host hasSuffix:@".mapbox.com"])) {
- NSString* absoluteString = [url.absoluteString
- stringByAppendingFormat:(url.query ? @"&%@" : @"?%@"), @"events=true"];
- url = [NSURL URLWithString:absoluteString];
- }
-
- NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url];
- if (resource.priorEtag) {
- [req addValue:@(resource.priorEtag->c_str())
- forHTTPHeaderField:@"If-None-Match"];
- } else if (resource.priorModified) {
- [req addValue:@(util::rfc1123(*resource.priorModified).c_str())
- forHTTPHeaderField:@"If-Modified-Since"];
- }
-
- [req addValue:context->userAgent forHTTPHeaderField:@"User-Agent"];
-
- task = [context->session
- dataTaskWithRequest:req
- completionHandler:^(NSData* data, NSURLResponse* res, NSError* error) {
- std::unique_ptr<Response> response_ = HTTPNSURLRequest::handleResult(data, res, error, resource_);
- std::lock_guard<std::mutex> lock(cancelled_->second);
- if (!cancelled_->first) {
- response = std::move(response_);
- async.send();
- }
- }];
- [task resume];
- }
-}
-
-void HTTPNSURLRequest::handleResponse() {
- assert(response);
- notify(*response);
-
- delete this;
-}
-
-void HTTPNSURLRequest::cancel() {
- [task cancel];
- task = nil;
-
- {
- std::lock_guard<std::mutex> lock(cancelled->second);
- cancelled->first = true;
- }
-
- // The lock is in place to enforce that `async` is not accessed if the request has been
- // cancelled. Therefore it's not necessary to hold the lock beyond setting cancelled to
- // true, and in fact it's unsafe to so: if this is the last remaining shared reference,
- // `delete this` will destroy the mutex. If the lock was held, it would then be orphaned.
-
- delete this;
-}
-
-std::unique_ptr<Response> HTTPNSURLRequest::handleResult(NSData *data, NSURLResponse *res, NSError *error, Resource resource) {
- std::unique_ptr<Response> response = std::make_unique<Response>();
- using Error = Response::Error;
-
- if (error) {
- if ([error code] == NSURLErrorCancelled) {
- response.reset();
-
- } else {
- if (data) {
- response->data =
- std::make_shared<std::string>((const char*)[data bytes], [data length]);
- }
-
- switch ([error code]) {
- case NSURLErrorBadServerResponse: // 5xx errors
- response->error = std::make_unique<Error>(
- Error::Reason::Server, [[error localizedDescription] UTF8String]);
- break;
-
- case NSURLErrorNetworkConnectionLost:
- case NSURLErrorCannotFindHost:
- case NSURLErrorCannotConnectToHost:
- case NSURLErrorDNSLookupFailed:
- case NSURLErrorNotConnectedToInternet:
- case NSURLErrorInternationalRoamingOff:
- case NSURLErrorCallIsActive:
- case NSURLErrorDataNotAllowed:
- case NSURLErrorTimedOut:
- response->error = std::make_unique<Error>(
- Error::Reason::Connection, [[error localizedDescription] UTF8String]);
- break;
-
- default:
- response->error = std::make_unique<Error>(
- Error::Reason::Other, [[error localizedDescription] UTF8String]);
- break;
- }
- }
- } else if ([res isKindOfClass:[NSHTTPURLResponse class]]) {
- const long responseCode = [(NSHTTPURLResponse *)res statusCode];
-
- NSDictionary *headers = [(NSHTTPURLResponse *)res allHeaderFields];
- NSString *cache_control = [headers objectForKey:@"Cache-Control"];
- if (cache_control) {
- response->expires = parseCacheControl([cache_control UTF8String]);
- }
-
- NSString *expires = [headers objectForKey:@"Expires"];
- if (expires) {
- response->expires = util::parseTimePoint([expires UTF8String]);
- }
-
- NSString *last_modified = [headers objectForKey:@"Last-Modified"];
- if (last_modified) {
- response->modified = util::parseTimePoint([last_modified UTF8String]);
- }
-
- NSString *etag = [headers objectForKey:@"ETag"];
- if (etag) {
- response->etag = std::string([etag UTF8String]);
- }
-
- if (responseCode == 200) {
- response->data = std::make_shared<std::string>((const char *)[data bytes], [data length]);
- } else if (responseCode == 204 || (responseCode == 404 && resource.kind == Resource::Kind::Tile)) {
- response->noContent = true;
- } else if (responseCode == 304) {
- response->notModified = true;
- } else if (responseCode == 404) {
- response->error =
- std::make_unique<Error>(Error::Reason::NotFound, "HTTP status code 404");
- } else if (responseCode >= 500 && responseCode < 600) {
- response->error =
- std::make_unique<Error>(Error::Reason::Server, std::string{ "HTTP status code " } +
- std::to_string(responseCode));
- } else {
- response->error =
- std::make_unique<Error>(Error::Reason::Other, std::string{ "HTTP status code " } +
- std::to_string(responseCode));
- }
- } else {
- // This should never happen.
- response->error = std::make_unique<Error>(Error::Reason::Other,
- "Response class is not NSHTTPURLResponse");
- }
-
- return response;
-}
-
-std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext() {
- return std::make_unique<HTTPNSURLContext>();
-}
-
-uint32_t HTTPContextBase::maximumConcurrentRequests() {
- return 20;
-}
-
-}
diff --git a/platform/default/http_request_curl.cpp b/platform/default/http_file_source.cpp
index fc57e4c137..3250a77c80 100644
--- a/platform/default/http_request_curl.cpp
+++ b/platform/default/http_file_source.cpp
@@ -1,5 +1,4 @@
-#include <mbgl/storage/http_context_base.hpp>
-#include <mbgl/storage/http_request_base.hpp>
+#include <mbgl/storage/http_file_source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/platform/log.hpp>
@@ -9,28 +8,23 @@
#include <mbgl/util/string.hpp>
#include <mbgl/util/timer.hpp>
#include <mbgl/util/chrono.hpp>
+#include <mbgl/util/http_header.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>
#include <cstring>
#include <cstdio>
-void handleError(CURLMcode code) {
+static void handleError(CURLMcode code) {
if (code != CURLM_OK) {
throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(code));
}
}
-void handleError(CURLcode code) {
+static void handleError(CURLcode code) {
if (code != CURLE_OK) {
throw std::runtime_error(std::string("CURL easy error: ") + curl_easy_strerror(code));
}
@@ -38,20 +32,14 @@ void handleError(CURLcode code) {
namespace mbgl {
-class HTTPCURLRequest;
-
-class HTTPCURLContext : public HTTPContextBase {
- MBGL_STORE_THREAD(tid)
-
+class HTTPFileSource::Impl {
public:
- HTTPCURLContext();
- ~HTTPCURLContext();
-
- HTTPRequestBase* createRequest(const Resource&, HTTPRequestBase::Callback) final;
+ Impl();
+ ~Impl();
static int handleSocket(CURL *handle, curl_socket_t s, int action, void *userp, void *socketp);
static int startTimeout(CURLM *multi, long timeout_ms, void *userp);
- static void onTimeout(HTTPCURLContext *context);
+ static void onTimeout(HTTPFileSource::Impl *context);
void perform(curl_socket_t s, util::RunLoop::Event event);
CURL *getHandle();
@@ -73,14 +61,10 @@ public:
std::queue<CURL *> handles;
};
-class HTTPCURLRequest : public HTTPRequestBase {
- MBGL_STORE_THREAD(tid)
-
+class HTTPRequest : public AsyncRequest {
public:
- HTTPCURLRequest(HTTPCURLContext*, const Resource&, Callback);
- ~HTTPCURLRequest();
-
- void cancel() final;
+ HTTPRequest(HTTPFileSource::Impl*, const Resource&, FileSource::Callback);
+ ~HTTPRequest();
void handleResult(CURLcode code);
@@ -88,24 +72,21 @@ private:
static size_t headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp);
static size_t writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp);
- HTTPCURLContext *context = nullptr;
+ HTTPFileSource::Impl* context = nullptr;
+ Resource resource;
+ FileSource::Callback callback;
// Will store the current response.
std::shared_ptr<std::string> data;
std::unique_ptr<Response> response;
- // In case of revalidation requests, this will store the old response.
- const std::shared_ptr<const Response> existingResponse;
-
CURL *handle = nullptr;
curl_slist *headers = nullptr;
- char error[CURL_ERROR_SIZE];
+ char error[CURL_ERROR_SIZE] = { 0 };
};
-// -------------------------------------------------------------------------------------------------
-
-HTTPCURLContext::HTTPCURLContext() {
+HTTPFileSource::Impl::Impl() {
if (curl_global_init(CURL_GLOBAL_ALL)) {
throw std::runtime_error("Could not init cURL");
}
@@ -119,7 +100,7 @@ HTTPCURLContext::HTTPCURLContext() {
handleError(curl_multi_setopt(multi, CURLMOPT_TIMERDATA, this));
}
-HTTPCURLContext::~HTTPCURLContext() {
+HTTPFileSource::Impl::~Impl() {
while (!handles.empty()) {
curl_easy_cleanup(handles.front());
handles.pop();
@@ -134,11 +115,7 @@ HTTPCURLContext::~HTTPCURLContext() {
timeout.stop();
}
-HTTPRequestBase* HTTPCURLContext::createRequest(const Resource& resource, HTTPRequestBase::Callback callback) {
- return new HTTPCURLRequest(this, resource, callback);
-}
-
-CURL *HTTPCURLContext::getHandle() {
+CURL *HTTPFileSource::Impl::getHandle() {
if (!handles.empty()) {
auto handle = handles.front();
handles.pop();
@@ -148,20 +125,19 @@ CURL *HTTPCURLContext::getHandle() {
}
}
-void HTTPCURLContext::returnHandle(CURL *handle) {
+void HTTPFileSource::Impl::returnHandle(CURL *handle) {
curl_easy_reset(handle);
handles.push(handle);
}
-void HTTPCURLContext::checkMultiInfo() {
- MBGL_VERIFY_THREAD(tid);
+void HTTPFileSource::Impl::checkMultiInfo() {
CURLMsg *message = nullptr;
int pending = 0;
while ((message = curl_multi_info_read(multi, &pending))) {
switch (message->msg) {
case CURLMSG_DONE: {
- HTTPCURLRequest *baton = nullptr;
+ HTTPRequest *baton = nullptr;
curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, (char *)&baton);
assert(baton);
baton->handleResult(message->data.result);
@@ -174,9 +150,7 @@ void HTTPCURLContext::checkMultiInfo() {
}
}
-void HTTPCURLContext::perform(curl_socket_t s, util::RunLoop::Event events) {
- MBGL_VERIFY_THREAD(tid);
-
+void HTTPFileSource::Impl::perform(curl_socket_t s, util::RunLoop::Event events) {
int flags = 0;
if (events == util::RunLoop::Event::Read) {
@@ -192,23 +166,22 @@ void HTTPCURLContext::perform(curl_socket_t s, util::RunLoop::Event events) {
checkMultiInfo();
}
-int HTTPCURLContext::handleSocket(CURL * /* handle */, curl_socket_t s, int action, void *userp,
+int HTTPFileSource::Impl::handleSocket(CURL * /* handle */, curl_socket_t s, int action, void *userp,
void * /* socketp */) {
assert(userp);
- auto context = reinterpret_cast<HTTPCURLContext *>(userp);
- MBGL_VERIFY_THREAD(context->tid);
+ auto context = reinterpret_cast<Impl *>(userp);
switch (action) {
case CURL_POLL_IN: {
using namespace std::placeholders;
util::RunLoop::Get()->addWatch(s, util::RunLoop::Event::Read,
- std::bind(&HTTPCURLContext::perform, context, _1, _2));
+ std::bind(&Impl::perform, context, _1, _2));
break;
}
case CURL_POLL_OUT: {
using namespace std::placeholders;
util::RunLoop::Get()->addWatch(s, util::RunLoop::Event::Write,
- std::bind(&HTTPCURLContext::perform, context, _1, _2));
+ std::bind(&Impl::perform, context, _1, _2));
break;
}
case CURL_POLL_REMOVE:
@@ -221,8 +194,7 @@ int HTTPCURLContext::handleSocket(CURL * /* handle */, curl_socket_t s, int acti
return 0;
}
-void HTTPCURLContext::onTimeout(HTTPCURLContext *context) {
- MBGL_VERIFY_THREAD(context->tid);
+void HTTPFileSource::Impl::onTimeout(Impl *context) {
int running_handles;
CURLMcode error = curl_multi_socket_action(context->multi, CURL_SOCKET_TIMEOUT, 0, &running_handles);
if (error != CURLM_OK) {
@@ -231,127 +203,27 @@ void HTTPCURLContext::onTimeout(HTTPCURLContext *context) {
context->checkMultiInfo();
}
-int HTTPCURLContext::startTimeout(CURLM * /* multi */, long timeout_ms, void *userp) {
+int HTTPFileSource::Impl::startTimeout(CURLM * /* multi */, long timeout_ms, void *userp) {
assert(userp);
- auto context = reinterpret_cast<HTTPCURLContext *>(userp);
- MBGL_VERIFY_THREAD(context->tid);
+ auto context = reinterpret_cast<Impl *>(userp);
+
if (timeout_ms < 0) {
// A timeout of 0 ms means that the timer will invoked in the next loop iteration.
timeout_ms = 0;
}
+
context->timeout.stop();
context->timeout.start(mbgl::Milliseconds(timeout_ms), Duration::zero(),
- std::bind(&HTTPCURLContext::onTimeout, context));
+ std::bind(&Impl::onTimeout, context));
return 0;
}
-// -------------------------------------------------------------------------------------------------
-
-#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 = std::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
-
-HTTPCURLRequest::HTTPCURLRequest(HTTPCURLContext* context_, const Resource& resource_, Callback callback_)
- : HTTPRequestBase(resource_, callback_),
- context(context_),
+HTTPRequest::HTTPRequest(HTTPFileSource::Impl* context_, const Resource& resource_, FileSource::Callback callback_)
+ : context(context_),
+ resource(resource_),
+ callback(callback_),
handle(context->getHandle()) {
- // 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.
@@ -370,12 +242,7 @@ HTTPCURLRequest::HTTPCURLRequest(HTTPCURLContext* context_, const Resource& reso
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, resource.url.c_str()));
handleError(curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeCallback));
@@ -394,9 +261,7 @@ HTTPCURLRequest::HTTPCURLRequest(HTTPCURLContext* context_, const Resource& reso
handleError(curl_multi_add_handle(context->multi, handle));
}
-HTTPCURLRequest::~HTTPCURLRequest() {
- MBGL_VERIFY_THREAD(tid);
-
+HTTPRequest::~HTTPRequest() {
handleError(curl_multi_remove_handle(context->multi, handle));
context->returnHandle(handle);
handle = nullptr;
@@ -407,16 +272,11 @@ HTTPCURLRequest::~HTTPCURLRequest() {
}
}
-void HTTPCURLRequest::cancel() {
- delete this;
-}
-
// This function is called when we have new data for a request. We just append it to the string
// containing the previous data.
-size_t HTTPCURLRequest::writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp) {
+size_t HTTPRequest::writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp) {
assert(userp);
- auto impl = reinterpret_cast<HTTPCURLRequest *>(userp);
- MBGL_VERIFY_THREAD(impl->tid);
+ auto impl = reinterpret_cast<HTTPRequest *>(userp);
if (!impl->data) {
impl->data = std::make_shared<std::string>();
@@ -442,10 +302,9 @@ size_t headerMatches(const char *const header, const char *const buffer, const s
return i == headerLength ? i : std::string::npos;
}
-size_t HTTPCURLRequest::headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp) {
+size_t HTTPRequest::headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp) {
assert(userp);
- auto baton = reinterpret_cast<HTTPCURLRequest *>(userp);
- MBGL_VERIFY_THREAD(baton->tid);
+ auto baton = reinterpret_cast<HTTPRequest *>(userp);
if (!baton->response) {
baton->response = std::make_unique<Response>();
@@ -462,7 +321,7 @@ size_t HTTPCURLRequest::headerCallback(char *const buffer, const size_t size, co
baton->response->etag = std::string(buffer + begin, length - begin - 2); // remove \r\n
} else if ((begin = headerMatches("cache-control: ", buffer, length)) != std::string::npos) {
const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
- baton->response->expires = parseCacheControl(value.c_str());
+ baton->response->expires = http::CacheControl::parse(value.c_str()).toTimePoint();
} else if ((begin = headerMatches("expires: ", buffer, length)) != std::string::npos) {
const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
baton->response->expires = SystemClock::from_time_t(curl_getdate(value.c_str(), nullptr));
@@ -471,16 +330,7 @@ size_t HTTPCURLRequest::headerCallback(char *const buffer, const size_t size, co
return length;
}
-void HTTPCURLRequest::handleResult(CURLcode code) {
- MBGL_VERIFY_THREAD(tid);
-
- if (cancelled) {
- // In this case, it doesn't make sense to even process the response even further since
- // the request was canceled anyway.
- delete this;
- return;
- }
-
+void HTTPRequest::handleResult(CURLcode code) {
// Make sure a response object exists in case we haven't got any headers or content.
if (!response) {
response = std::make_unique<Response>();
@@ -533,16 +383,23 @@ void HTTPCURLRequest::handleResult(CURLcode code) {
}
}
- // Actually return the response.
- notify(*response);
- delete this;
+ // Calling `callback` may result in deleting `this`. Copy data to temporaries first.
+ auto callback_ = callback;
+ auto response_ = *response;
+ callback_(response_);
}
-std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext() {
- return std::make_unique<HTTPCURLContext>();
+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.get(), resource, callback);
}
-uint32_t HTTPContextBase::maximumConcurrentRequests() {
+uint32_t HTTPFileSource::maximumConcurrentRequests() {
return 20;
}
diff --git a/platform/default/online_file_source.cpp b/platform/default/online_file_source.cpp
index 533046850a..6753b34f25 100644
--- a/platform/default/online_file_source.cpp
+++ b/platform/default/online_file_source.cpp
@@ -1,5 +1,5 @@
#include <mbgl/storage/online_file_source.hpp>
-#include <mbgl/storage/http_context_base.hpp>
+#include <mbgl/storage/http_file_source.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/storage/response.hpp>
@@ -35,7 +35,7 @@ public:
OnlineFileSource::Impl& impl;
Resource resource;
- HTTPRequestBase* request = nullptr;
+ std::unique_ptr<AsyncRequest> request;
util::Timer timer;
Callback callback;
@@ -82,7 +82,7 @@ public:
assert(activeRequests.find(request) == activeRequests.end());
assert(!request->request);
- if (activeRequests.size() >= HTTPContextBase::maximumConcurrentRequests()) {
+ if (activeRequests.size() >= HTTPFileSource::maximumConcurrentRequests()) {
queueRequest(request);
} else {
activateRequest(request);
@@ -96,10 +96,10 @@ public:
void activateRequest(OnlineFileRequest* request) {
activeRequests.insert(request);
- request->request = httpContext->createRequest(request->resource, [=] (Response response) {
+ request->request = httpFileSource.request(request->resource, [=] (Response response) {
activeRequests.erase(request);
activatePendingRequest();
- request->request = nullptr;
+ request->request.reset();
request->completed(response);
});
}
@@ -140,7 +140,7 @@ private:
std::unordered_map<OnlineFileRequest*, std::list<OnlineFileRequest*>::iterator> pendingRequestsMap;
std::unordered_set<OnlineFileRequest*> activeRequests;
- const std::unique_ptr<HTTPContextBase> httpContext { HTTPContextBase::createContext() };
+ HTTPFileSource httpFileSource;
util::AsyncTask reachability { std::bind(&Impl::networkIsReachableAgain, this) };
};
@@ -198,9 +198,6 @@ OnlineFileRequest::OnlineFileRequest(const Resource& resource_, Callback callbac
OnlineFileRequest::~OnlineFileRequest() {
impl.remove(this);
- if (request) {
- request->cancel();
- }
}
static Duration errorRetryTimeout(Response::Error::Reason failedRequestReason, uint32_t failedRequests) {
diff --git a/platform/ios/platform.gyp b/platform/ios/platform.gyp
index b4190d6fef..a38ab3869c 100644
--- a/platform/ios/platform.gyp
+++ b/platform/ios/platform.gyp
@@ -98,7 +98,7 @@
'../default/mbgl/storage/offline_download.cpp',
'../default/sqlite3.hpp',
'../default/sqlite3.cpp',
- '../darwin/src/http_request_nsurl.mm',
+ '../darwin/src/http_file_source.mm',
'../darwin/src/log_nslog.mm',
'../darwin/src/string_nsstring.mm',
'../darwin/src/image.mm',
diff --git a/platform/linux/platform.gyp b/platform/linux/platform.gyp
index 557587d6e9..4bef3e69cd 100644
--- a/platform/linux/platform.gyp
+++ b/platform/linux/platform.gyp
@@ -57,7 +57,7 @@
'../default/png_reader.cpp',
'../default/jpeg_reader.cpp',
'../default/asset_file_source.cpp',
- '../default/http_request_curl.cpp',
+ '../default/http_file_source.cpp',
'../default/default_file_source.cpp',
'../default/online_file_source.cpp',
'../default/mbgl/storage/offline.hpp',
diff --git a/platform/osx/platform.gyp b/platform/osx/platform.gyp
index 657aa85286..229c721845 100644
--- a/platform/osx/platform.gyp
+++ b/platform/osx/platform.gyp
@@ -59,7 +59,7 @@
'../default/mbgl/storage/offline_download.cpp',
'../default/sqlite3.hpp',
'../default/sqlite3.cpp',
- '../darwin/src/http_request_nsurl.mm',
+ '../darwin/src/http_file_source.mm',
'../darwin/src/log_nslog.mm',
'../darwin/src/string_nsstring.mm',
'../darwin/src/image.mm',