diff options
Diffstat (limited to 'platform/darwin/src/http_file_source.mm')
-rw-r--r-- | platform/darwin/src/http_file_source.mm | 139 |
1 files changed, 96 insertions, 43 deletions
diff --git a/platform/darwin/src/http_file_source.mm b/platform/darwin/src/http_file_source.mm index a72a97e299..0abae701d7 100644 --- a/platform/darwin/src/http_file_source.mm +++ b/platform/darwin/src/http_file_source.mm @@ -26,10 +26,10 @@ namespace mbgl { class HTTPRequestShared { public: HTTPRequestShared(Response& response_, util::AsyncTask& async_) - : response(response_), - async(async_) { + : response(response_), + async(async_) { } - + void notify(const Response& response_) { std::lock_guard<std::mutex> lock(mutex); if (!cancelled) { @@ -37,16 +37,16 @@ public: 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; }; @@ -54,24 +54,24 @@ private: class HTTPRequest : public AsyncRequest { public: HTTPRequest(FileSource::Callback callback_) - : shared(std::make_shared<HTTPRequestShared>(response, async)), - 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; @@ -86,15 +86,15 @@ public: @autoreleasepool { NSURLSessionConfiguration *sessionConfig = MGLNativeNetworkManager.sharedManager.sessionConfiguration; session = [NSURLSession sessionWithConfiguration:sessionConfig]; - + userAgent = getUserAgent(); } } - + NSURLSession* session = nil; NSString* userAgent = nil; NSInteger accountType = 0; - + private: NSString* getUserAgent() const; NSBundle* getSDKBundle() const; @@ -102,7 +102,7 @@ private: NSString *HTTPFileSource::Impl::getUserAgent() const { NSMutableArray *userAgentComponents = [NSMutableArray array]; - + NSBundle *appBundle = [NSBundle mainBundle]; if (appBundle) { NSString *appName = appBundle.infoDictionary[@"CFBundleName"]; @@ -112,7 +112,7 @@ NSString *HTTPFileSource::Impl::getUserAgent() const { } else { [userAgentComponents addObject:[NSProcessInfo processInfo].processName]; } - + NSBundle *sdkBundle = HTTPFileSource::Impl::getSDKBundle(); if (sdkBundle) { NSString *versionString = sdkBundle.infoDictionary[@"MGLSemanticVersionString"]; @@ -124,12 +124,12 @@ NSString *HTTPFileSource::Impl::getUserAgent() const { sdkBundle.infoDictionary[@"CFBundleName"], versionString]]; } } - + // Avoid %s here because it inserts hidden bidirectional markers on macOS when the system // language is set to a right-to-left language. [userAgentComponents addObject:[NSString stringWithFormat:@"MapboxGL/0.0.0 (%@)", @(mbgl::version::revision)]]; - + NSString *systemName = @"Darwin"; #if TARGET_OS_IPHONE systemName = @"iOS"; @@ -152,7 +152,7 @@ NSString *HTTPFileSource::Impl::getUserAgent() const { if (systemVersion) { [userAgentComponents addObject:[NSString stringWithFormat:@"%@/%@", systemName, systemVersion]]; } - + NSString *cpu = nil; #if TARGET_CPU_X86 cpu = @"x86"; @@ -166,7 +166,7 @@ NSString *HTTPFileSource::Impl::getUserAgent() const { if (cpu) { [userAgentComponents addObject:[NSString stringWithFormat:@"(%@)", cpu]]; } - + return [userAgentComponents componentsJoinedByString:@" "]; } @@ -182,7 +182,7 @@ NSBundle *HTTPFileSource::Impl::getSDKBundle() const { } HTTPFileSource::HTTPFileSource() - : impl(std::make_unique<Impl>()) { +: impl(std::make_unique<Impl>()) { } HTTPFileSource::~HTTPFileSource() = default; @@ -204,7 +204,7 @@ NSURL *resourceURLWithAccountType(const Resource& resource, NSInteger accountTyp if (accountType == 0 && isValidMapboxEndpoint(url)) { NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; NSMutableArray *queryItems = [NSMutableArray array]; - + if (resource.usage == Resource::Usage::Offline) { [queryItems addObject:[NSURLQueryItem queryItemWithName:@"offline" value:@"true"]]; } else { @@ -224,11 +224,11 @@ NSURL *resourceURLWithAccountType(const Resource& resource, NSInteger accountTyp #endif return url; } - + 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 = resourceURLWithAccountType(resource, impl->accountType); [MGLNativeNetworkManager.sharedManager debugLog:@"Requesting URI: %@", url.relativePath]; @@ -236,16 +236,16 @@ std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url]; if (resource.priorEtag) { [req addValue:@(resource.priorEtag->c_str()) - forHTTPHeaderField:@"If-None-Match"]; + forHTTPHeaderField:@"If-None-Match"]; } else if (resource.priorModified) { [req addValue:@(util::rfc1123(*resource.priorModified).c_str()) - forHTTPHeaderField:@"If-Modified-Since"]; + forHTTPHeaderField:@"If-Modified-Since"]; } - + [req addValue:impl->userAgent forHTTPHeaderField:@"User-Agent"]; - + const bool isTile = resource.kind == mbgl::Resource::Kind::Tile; - + if (isTile) { [MGLNativeNetworkManager.sharedManager startDownloadEvent:url.relativePath type:@"tile"]; } @@ -272,9 +272,9 @@ std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, switch ([error code]) { case NSURLErrorBadServerResponse: // 5xx errors response.error = std::make_unique<Error>( - Error::Reason::Server, [[error localizedDescription] UTF8String]); + Error::Reason::Server, [[error localizedDescription] UTF8String]); break; - + case NSURLErrorNetworkConnectionLost: case NSURLErrorCannotFindHost: case NSURLErrorCannotConnectToHost: @@ -285,13 +285,55 @@ std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, case NSURLErrorDataNotAllowed: case NSURLErrorTimedOut: response.error = std::make_unique<Error>( - Error::Reason::Connection, [[error localizedDescription] UTF8String]); + Error::Reason::Connection, [[error localizedDescription] UTF8String]); break; - + default: response.error = std::make_unique<Error>( - Error::Reason::Other, [[error localizedDescription] UTF8String]); + 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) { + const auto cc = http::CacheControl::parse([cache_control UTF8String]); + response.expires = cc.toTimePoint(); + response.mustRevalidate = cc.mustRevalidate; + } + + NSString *expires = [headers objectForKey:@"Expires"]; + if (expires) { + response.expires = util::parseTimestamp([expires UTF8String]); + } + + NSString *last_modified = [headers objectForKey:@"Last-Modified"]; + if (last_modified) { + response.modified = util::parseTimestamp([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 && isTile)) { + 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 == 429) { + // Get the standard header + optional<std::string> retryAfter; + NSString *retryAfterHeader = headers[@"Retry-After"]; + if (retryAfterHeader) { + retryAfter = std::string([retryAfterHeader UTF8String]); } } else if ([res isKindOfClass:[NSHTTPURLResponse class]]) { const long responseCode = [(NSHTTPURLResponse *)res statusCode]; @@ -354,18 +396,29 @@ std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, std::make_unique<Error>(Error::Reason::Other, std::string{ "HTTP status code " } + std::to_string(responseCode)); } + + response.error = std::make_unique<Error>(Error::Reason::RateLimit, "HTTP status code 429", http::parseRetryHeaders(retryAfter, xRateLimitReset)); + } 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 { - // This should never happen. - response.error = std::make_unique<Error>(Error::Reason::Other, - "Response class is not NSHTTPURLResponse"); + response.error = + std::make_unique<Error>(Error::Reason::Other, std::string{ "HTTP status code " } + + std::to_string(responseCode)); } - - shared->notify(response); - }]; - + } 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); } |