diff options
author | John Firebaugh <john.firebaugh@gmail.com> | 2016-02-15 17:35:13 -0800 |
---|---|---|
committer | John Firebaugh <john.firebaugh@gmail.com> | 2016-04-14 14:18:19 -0700 |
commit | 3af3e72bb3cb3f05b33be304d59e66cc244ef4d9 (patch) | |
tree | ef1d237c3083694307081c219c9da94ae43fe540 /platform/darwin | |
parent | 6d88a23a60bc0a6dc9945bf09a659d15dd827192 (diff) | |
download | qtlocation-mapboxgl-3af3e72bb3cb3f05b33be304d59e66cc244ef4d9.tar.gz |
[all] Replace HTTPContextBase/HTTPRequestBase with FileSource
Diffstat (limited to 'platform/darwin')
-rw-r--r-- | platform/darwin/src/http_file_source.mm | 228 | ||||
-rw-r--r-- | platform/darwin/src/http_request_nsurl.mm | 243 |
2 files changed, 228 insertions, 243 deletions
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; -} - -} |