123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383 |
- #if !defined(__has_feature) || !__has_feature(objc_arc)
- #error "This file requires ARC support."
- #endif
- #import "GTMSessionFetcherService.h"
- NSString *const kGTMSessionFetcherServiceSessionBecameInvalidNotification =
- @"kGTMSessionFetcherServiceSessionBecameInvalidNotification";
- NSString *const kGTMSessionFetcherServiceSessionKey = @"kGTMSessionFetcherServiceSessionKey";
- #if !GTMSESSION_BUILD_COMBINED_SOURCES
- @interface GTMSessionFetcher (ServiceMethods)
- - (BOOL)beginFetchMayDelay:(BOOL)mayDelay
- mayAuthorize:(BOOL)mayAuthorize
- mayDecorate:(BOOL)mayDecorate;
- @end
- #endif
- @interface GTMSessionFetcherService ()
- @property(atomic, strong, readwrite) NSDictionary *delayedFetchersByHost;
- @property(atomic, strong, readwrite) NSDictionary *runningFetchersByHost;
- @property(atomic, strong, readonly) NSPointerArray *decoratorsPointerArray;
- @end
- @interface GTMSessionFetcherSessionDelegateDispatcher : NSObject <NSURLSessionDelegate>
- @property(atomic) NSURLSession *session;
- @property(atomic) NSTimeInterval discardInterval;
- @property(atomic, readonly) NSTimer *discardTimer;
- - (instancetype)initWithParentService:(GTMSessionFetcherService *)parentService
- sessionDiscardInterval:(NSTimeInterval)discardInterval;
- - (void)setFetcher:(GTMSessionFetcher *)fetcher forTask:(NSURLSessionTask *)task;
- - (void)removeFetcher:(GTMSessionFetcher *)fetcher;
- - (void)startSessionUsage;
- - (void)abandon;
- @end
- @implementation GTMSessionFetcherService {
- NSMutableDictionary *_delayedFetchersByHost;
- NSMutableDictionary *_runningFetchersByHost;
- NSUInteger _maxRunningFetchersPerHost;
-
- GTMSessionFetcherSessionDelegateDispatcher *_delegateDispatcher;
-
- dispatch_semaphore_t _sessionCreationSemaphore;
- dispatch_queue_t _callbackQueue;
- NSOperationQueue *_delegateQueue;
- NSHTTPCookieStorage *_cookieStorage;
- NSString *_userAgent;
- NSTimeInterval _timeout;
- NSURLCredential *_credential;
- NSURLCredential *_proxyCredential;
- NSInteger _cookieStorageMethod;
- id<GTMFetcherAuthorizationProtocol> _authorizer;
-
-
-
- NSMutableArray *_stoppedFetchersToWaitFor;
-
-
- NSDate *_stoppedAllFetchersDate;
- }
- @synthesize maxRunningFetchersPerHost = _maxRunningFetchersPerHost,
- configuration = _configuration,
- configurationBlock = _configurationBlock,
- cookieStorage = _cookieStorage,
- userAgent = _userAgent,
- challengeBlock = _challengeBlock,
- credential = _credential,
- proxyCredential = _proxyCredential,
- allowedInsecureSchemes = _allowedInsecureSchemes,
- allowLocalhostRequest = _allowLocalhostRequest,
- allowInvalidServerCertificates = _allowInvalidServerCertificates,
- retryEnabled = _retryEnabled,
- retryBlock = _retryBlock,
- maxRetryInterval = _maxRetryInterval,
- minRetryInterval = _minRetryInterval,
- metricsCollectionBlock = _metricsCollectionBlock,
- properties = _properties,
- unusedSessionTimeout = _unusedSessionTimeout,
- decoratorsPointerArray = _decoratorsPointerArray,
- testBlock = _testBlock;
- #if GTM_BACKGROUND_TASK_FETCHING
- @synthesize skipBackgroundTask = _skipBackgroundTask;
- #endif
- - (instancetype)init {
- self = [super init];
- if (self) {
- _delayedFetchersByHost = [[NSMutableDictionary alloc] init];
- _runningFetchersByHost = [[NSMutableDictionary alloc] init];
- _maxRunningFetchersPerHost = 10;
- _cookieStorageMethod = -1;
- _unusedSessionTimeout = 60.0;
- _delegateDispatcher = [[GTMSessionFetcherSessionDelegateDispatcher alloc]
- initWithParentService:self
- sessionDiscardInterval:_unusedSessionTimeout];
- _callbackQueue = dispatch_get_main_queue();
- _delegateQueue = [[NSOperationQueue alloc] init];
- _delegateQueue.maxConcurrentOperationCount = 1;
- _delegateQueue.name = @"com.google.GTMSessionFetcher.NSURLSessionDelegateQueue";
- _sessionCreationSemaphore = dispatch_semaphore_create(1);
-
-
-
- _userAgent = GTMFetcherStandardUserAgentString(nil);
- }
- return self;
- }
- - (void)dealloc {
- [self detachAuthorizer];
- [_delegateDispatcher abandon];
- }
- #pragma mark Generate a new fetcher
- - (id)fetcherWithRequest:(NSURLRequest *)request fetcherClass:(Class)fetcherClass {
- GTMSessionFetcher *fetcher = [[fetcherClass alloc] initWithRequest:request
- configuration:self.configuration];
- fetcher.callbackQueue = self.callbackQueue;
- fetcher.sessionDelegateQueue = self.sessionDelegateQueue;
- fetcher.challengeBlock = self.challengeBlock;
- fetcher.credential = self.credential;
- fetcher.proxyCredential = self.proxyCredential;
- fetcher.authorizer = self.authorizer;
- fetcher.cookieStorage = self.cookieStorage;
- fetcher.allowedInsecureSchemes = self.allowedInsecureSchemes;
- fetcher.allowLocalhostRequest = self.allowLocalhostRequest;
- fetcher.allowInvalidServerCertificates = self.allowInvalidServerCertificates;
- fetcher.configurationBlock = self.configurationBlock;
- fetcher.retryEnabled = self.retryEnabled;
- fetcher.retryBlock = self.retryBlock;
- fetcher.maxRetryInterval = self.maxRetryInterval;
- fetcher.minRetryInterval = self.minRetryInterval;
- if (@available(iOS 10.0, *)) {
- fetcher.metricsCollectionBlock = self.metricsCollectionBlock;
- }
- fetcher.properties = self.properties;
- fetcher.service = self;
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
- if (self.cookieStorageMethod >= 0) {
- [fetcher setCookieStorageMethod:self.cookieStorageMethod];
- }
- #pragma clang diagnostic pop
- #if GTM_BACKGROUND_TASK_FETCHING
- fetcher.skipBackgroundTask = self.skipBackgroundTask;
- #endif
- NSString *userAgent = self.userAgent;
- if (userAgent.length > 0 && [request valueForHTTPHeaderField:@"User-Agent"] == nil) {
- [fetcher setRequestValue:userAgent forHTTPHeaderField:@"User-Agent"];
- }
- fetcher.testBlock = self.testBlock;
- return fetcher;
- }
- - (GTMSessionFetcher *)fetcherWithRequest:(NSURLRequest *)request {
- return [self fetcherWithRequest:request fetcherClass:[GTMSessionFetcher class]];
- }
- - (GTMSessionFetcher *)fetcherWithURL:(NSURL *)requestURL {
- return [self fetcherWithRequest:[NSURLRequest requestWithURL:requestURL]];
- }
- - (GTMSessionFetcher *)fetcherWithURLString:(NSString *)requestURLString {
- NSURL *url = [NSURL URLWithString:requestURLString];
- return [self fetcherWithURL:url];
- }
- - (void)addDecorator:(id<GTMFetcherDecoratorProtocol>)decorator {
- @synchronized(self) {
- if (!_decoratorsPointerArray) {
- _decoratorsPointerArray = [NSPointerArray weakObjectsPointerArray];
- }
- [_decoratorsPointerArray addPointer:(__bridge void *)decorator];
- }
- }
- - (nullable NSArray<id<GTMFetcherDecoratorProtocol>> *)decorators {
- @synchronized(self) {
- return _decoratorsPointerArray.allObjects;
- }
- }
- - (void)removeDecorator:(id<GTMFetcherDecoratorProtocol>)decorator {
- @synchronized(self) {
- NSUInteger i = 0;
- for (id<GTMFetcherDecoratorProtocol> decoratorCandidate in _decoratorsPointerArray) {
- if (decoratorCandidate == decorator) {
- break;
- }
- ++i;
- }
- GTMSESSION_ASSERT_DEBUG(i < _decoratorsPointerArray.count,
- @"decorator %@ must be passed to -addDecorator: before removing",
- decorator);
- if (i < _decoratorsPointerArray.count) {
- [_decoratorsPointerArray removePointerAtIndex:i];
- }
- }
- }
- - (NSURLSession *)session {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- NSURLSession *session = _delegateDispatcher.session;
- return session;
- }
- }
- - (NSURLSession *)sessionForFetcherCreation {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- if (!_delegateDispatcher) {
-
- return nil;
- }
- }
-
-
- dispatch_semaphore_wait(_sessionCreationSemaphore, DISPATCH_TIME_FOREVER);
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
-
-
-
- [_delegateDispatcher startSessionUsage];
- NSURLSession *session = _delegateDispatcher.session;
- if (session) {
-
-
- dispatch_semaphore_signal(_sessionCreationSemaphore);
- } else {
-
-
-
- }
- return session;
- }
- }
- - (id<NSURLSessionDelegate>)sessionDelegate {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- return _delegateDispatcher;
- }
- }
- #pragma mark Queue Management
- - (void)addRunningFetcher:(GTMSessionFetcher *)fetcher forHost:(NSString *)host {
-
- NSMutableArray *runningForHost = [_runningFetchersByHost objectForKey:host];
- if (runningForHost == nil) {
- runningForHost = [NSMutableArray arrayWithObject:fetcher];
- [_runningFetchersByHost setObject:runningForHost forKey:host];
- } else {
- [runningForHost addObject:fetcher];
- }
- }
- - (void)addDelayedFetcher:(GTMSessionFetcher *)fetcher forHost:(NSString *)host {
-
- NSMutableArray *delayedForHost = [_delayedFetchersByHost objectForKey:host];
- if (delayedForHost == nil) {
- delayedForHost = [NSMutableArray arrayWithObject:fetcher];
- [_delayedFetchersByHost setObject:delayedForHost forKey:host];
- } else {
- [delayedForHost addObject:fetcher];
- }
- }
- - (BOOL)isDelayingFetcher:(GTMSessionFetcher *)fetcher {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- NSString *host = fetcher.request.URL.host;
- if (host == nil) {
- return NO;
- }
- NSArray *delayedForHost = [_delayedFetchersByHost objectForKey:host];
- NSUInteger idx = [delayedForHost indexOfObjectIdenticalTo:fetcher];
- BOOL isDelayed = (delayedForHost != nil) && (idx != NSNotFound);
- return isDelayed;
- }
- }
- - (BOOL)fetcherShouldBeginFetching:(GTMSessionFetcher *)fetcher {
-
- NSURL *requestURL = fetcher.request.URL;
- NSString *host = requestURL.host;
-
- if (host.length == 0 && [requestURL isFileURL]) {
- host = @"localhost";
- }
- if (host.length == 0) {
-
- GTMSESSION_ASSERT_DEBUG([[requestURL scheme] isEqual:@"data"], @"%@ lacks host", fetcher);
- return YES;
- }
- BOOL shouldBeginResult;
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- NSMutableArray *runningForHost = [_runningFetchersByHost objectForKey:host];
- if (runningForHost != nil && [runningForHost indexOfObjectIdenticalTo:fetcher] != NSNotFound) {
- GTMSESSION_ASSERT_DEBUG(NO, @"%@ was already running", fetcher);
- return YES;
- }
- BOOL shouldRunNow = (fetcher.usingBackgroundSession || _maxRunningFetchersPerHost == 0 ||
- _maxRunningFetchersPerHost >
- [[self class] numberOfNonBackgroundSessionFetchers:runningForHost]);
- if (shouldRunNow) {
- [self addRunningFetcher:fetcher forHost:host];
- shouldBeginResult = YES;
- } else {
- [self addDelayedFetcher:fetcher forHost:host];
- shouldBeginResult = NO;
- }
- }
-
-
-
- fetcher.serviceHost = host;
- return shouldBeginResult;
- }
- - (void)startFetcher:(GTMSessionFetcher *)fetcher {
- [fetcher beginFetchMayDelay:NO mayAuthorize:YES mayDecorate:YES];
- }
- - (GTMSessionFetcherSessionDelegateDispatcher *)delegateDispatcherForFetcher:
- (GTMSessionFetcher *)fetcher {
- GTMSessionCheckNotSynchronized(self);
- NSURLSession *fetcherSession = fetcher.session;
- if (fetcherSession) {
- id<NSURLSessionDelegate> fetcherDelegate = fetcherSession.delegate;
-
-
-
- BOOL hasDispatcher =
- (fetcherDelegate != nil && ![fetcherDelegate isKindOfClass:[GTMSessionFetcher class]]);
- if (hasDispatcher) {
- GTMSESSION_ASSERT_DEBUG(
- [fetcherDelegate isKindOfClass:[GTMSessionFetcherSessionDelegateDispatcher class]],
- @"Fetcher delegate class: %@", [fetcherDelegate class]);
- return (GTMSessionFetcherSessionDelegateDispatcher *)fetcherDelegate;
- }
- }
- return nil;
- }
- - (void)fetcherDidCreateSession:(GTMSessionFetcher *)fetcher {
- if (fetcher.canShareSession) {
- NSURLSession *fetcherSession = fetcher.session;
- GTMSESSION_ASSERT_DEBUG(fetcherSession != nil, @"Fetcher missing its session: %@", fetcher);
- GTMSessionFetcherSessionDelegateDispatcher *delegateDispatcher =
- [self delegateDispatcherForFetcher:fetcher];
- if (delegateDispatcher) {
- GTMSESSION_ASSERT_DEBUG(delegateDispatcher.session == nil,
- @"Fetcher made an extra session: %@", fetcher);
-
- delegateDispatcher.session = fetcherSession;
-
- dispatch_semaphore_signal(_sessionCreationSemaphore);
- }
- }
- }
- - (void)fetcherDidBeginFetching:(GTMSessionFetcher *)fetcher {
-
-
- GTMSessionFetcherSessionDelegateDispatcher *delegateDispatcher =
- [self delegateDispatcherForFetcher:fetcher];
- if (delegateDispatcher) {
- GTMSESSION_ASSERT_DEBUG(fetcher.canShareSession, @"Inappropriate shared session: %@", fetcher);
-
-
-
- NSURLSession *sharedSession = delegateDispatcher.session;
- NSURLSession *fetcherSession = fetcher.session;
- GTMSESSION_ASSERT_DEBUG(sharedSession != nil, @"Missing delegate session: %@", fetcher);
- GTMSESSION_ASSERT_DEBUG(fetcherSession == sharedSession,
- @"Inconsistent session: %@ %@ (shared: %@)", fetcher, fetcherSession,
- sharedSession);
- if (sharedSession != nil && fetcherSession == sharedSession) {
- NSURLSessionTask *task = fetcher.sessionTask;
- GTMSESSION_ASSERT_DEBUG(task != nil, @"Missing session task: %@", fetcher);
- if (task) {
- [delegateDispatcher setFetcher:fetcher forTask:task];
- }
- }
- }
- }
- - (void)stopFetcher:(GTMSessionFetcher *)fetcher {
- [fetcher stopFetching];
- }
- - (void)fetcherDidStop:(GTMSessionFetcher *)fetcher {
-
- NSString *host = fetcher.serviceHost;
- if (!host) {
-
- return;
- }
-
-
- GTMSessionFetcherSessionDelegateDispatcher *delegateDispatcher =
- [self delegateDispatcherForFetcher:fetcher];
- [delegateDispatcher removeFetcher:fetcher];
- NSMutableArray *fetchersToStart;
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
-
- [_stoppedFetchersToWaitFor addObject:fetcher];
- NSMutableArray *runningForHost = [_runningFetchersByHost objectForKey:host];
- [runningForHost removeObject:fetcher];
- NSMutableArray *delayedForHost = [_delayedFetchersByHost objectForKey:host];
- [delayedForHost removeObject:fetcher];
- while (delayedForHost.count > 0 &&
- [[self class] numberOfNonBackgroundSessionFetchers:runningForHost] <
- _maxRunningFetchersPerHost) {
-
-
- GTMSessionFetcher *nextFetcher = nil;
- for (GTMSessionFetcher *delayedFetcher in delayedForHost) {
- if (nextFetcher == nil || delayedFetcher.servicePriority < nextFetcher.servicePriority) {
- nextFetcher = delayedFetcher;
- }
- }
- if (nextFetcher) {
- [self addRunningFetcher:nextFetcher forHost:host];
- runningForHost = [_runningFetchersByHost objectForKey:host];
- [delayedForHost removeObjectIdenticalTo:nextFetcher];
- if (!fetchersToStart) {
- fetchersToStart = [NSMutableArray array];
- }
- [fetchersToStart addObject:nextFetcher];
- }
- }
- if (runningForHost.count == 0) {
-
- [_runningFetchersByHost removeObjectForKey:host];
- }
- if (delayedForHost.count == 0) {
- [_delayedFetchersByHost removeObjectForKey:host];
- }
- }
-
- for (GTMSessionFetcher *nextFetcher in fetchersToStart) {
- [self startFetcher:nextFetcher];
- }
-
-
- fetcher.serviceHost = nil;
- }
- - (NSUInteger)numberOfFetchers {
- NSUInteger running = [self numberOfRunningFetchers];
- NSUInteger delayed = [self numberOfDelayedFetchers];
- return running + delayed;
- }
- - (NSUInteger)numberOfRunningFetchers {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- NSUInteger sum = 0;
- for (NSString *host in _runningFetchersByHost) {
- NSArray *fetchers = [_runningFetchersByHost objectForKey:host];
- sum += fetchers.count;
- }
- return sum;
- }
- }
- - (NSUInteger)numberOfDelayedFetchers {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- NSUInteger sum = 0;
- for (NSString *host in _delayedFetchersByHost) {
- NSArray *fetchers = [_delayedFetchersByHost objectForKey:host];
- sum += fetchers.count;
- }
- return sum;
- }
- }
- - (NSArray *)issuedFetchers {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- NSMutableArray *allFetchers = [NSMutableArray array];
- void (^accumulateFetchers)(id, id, BOOL *) =
- ^(NSString *host, NSArray *fetchersForHost, BOOL *stop) {
- [allFetchers addObjectsFromArray:fetchersForHost];
- };
- [_runningFetchersByHost enumerateKeysAndObjectsUsingBlock:accumulateFetchers];
- [_delayedFetchersByHost enumerateKeysAndObjectsUsingBlock:accumulateFetchers];
- GTMSESSION_ASSERT_DEBUG(allFetchers.count == [NSSet setWithArray:allFetchers].count,
- @"Fetcher appears multiple times\n running: %@\n delayed: %@",
- _runningFetchersByHost, _delayedFetchersByHost);
- return allFetchers.count > 0 ? allFetchers : nil;
- }
- }
- - (NSArray *)issuedFetchersWithRequestURL:(NSURL *)requestURL {
- NSString *host = requestURL.host;
- if (host.length == 0) return nil;
- NSURL *targetURL = [requestURL absoluteURL];
- NSArray *allFetchers = [self issuedFetchers];
- NSIndexSet *indexes = [allFetchers
- indexesOfObjectsPassingTest:^BOOL(GTMSessionFetcher *fetcher, NSUInteger idx, BOOL *stop) {
- NSURL *fetcherURL = [fetcher.request.URL absoluteURL];
- return [fetcherURL isEqual:targetURL];
- }];
- NSArray *result = nil;
- if (indexes.count > 0) {
- result = [allFetchers objectsAtIndexes:indexes];
- }
- return result;
- }
- - (void)stopAllFetchers {
- NSArray *delayedFetchersByHost;
- NSArray *runningFetchersByHost;
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
-
-
-
-
- _stoppedAllFetchersDate = [[NSDate alloc] init];
-
-
- delayedFetchersByHost = _delayedFetchersByHost.allValues;
- [_delayedFetchersByHost removeAllObjects];
- runningFetchersByHost = _runningFetchersByHost.allValues;
- [_runningFetchersByHost removeAllObjects];
- }
- for (NSArray *delayedForHost in delayedFetchersByHost) {
- for (GTMSessionFetcher *fetcher in delayedForHost) {
- [self stopFetcher:fetcher];
- }
- }
- for (NSArray *runningForHost in runningFetchersByHost) {
- for (GTMSessionFetcher *fetcher in runningForHost) {
- [self stopFetcher:fetcher];
- }
- }
- }
- - (NSDate *)stoppedAllFetchersDate {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- return _stoppedAllFetchersDate;
- }
- }
- #pragma mark Accessors
- - (BOOL)reuseSession {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- return _delegateDispatcher != nil;
- }
- }
- - (void)setReuseSession:(BOOL)shouldReuse {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- BOOL wasReusing = (_delegateDispatcher != nil);
- if (shouldReuse != wasReusing) {
- [self abandonDispatcher];
- if (shouldReuse) {
- _delegateDispatcher = [[GTMSessionFetcherSessionDelegateDispatcher alloc]
- initWithParentService:self
- sessionDiscardInterval:_unusedSessionTimeout];
- } else {
- _delegateDispatcher = nil;
- }
- }
- }
- }
- - (void)resetSession {
- GTMSessionCheckNotSynchronized(self);
- dispatch_semaphore_wait(_sessionCreationSemaphore, DISPATCH_TIME_FOREVER);
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- [self resetSessionInternal];
- }
- dispatch_semaphore_signal(_sessionCreationSemaphore);
- }
- - (void)resetSessionInternal {
- GTMSessionCheckSynchronized(self);
-
- if (_delegateDispatcher) {
- [self abandonDispatcher];
- _delegateDispatcher = [[GTMSessionFetcherSessionDelegateDispatcher alloc]
- initWithParentService:self
- sessionDiscardInterval:_unusedSessionTimeout];
- }
- }
- - (void)resetSessionForDispatcherDiscardTimer:(NSTimer *)timer {
- GTMSessionCheckNotSynchronized(self);
- dispatch_semaphore_wait(_sessionCreationSemaphore, DISPATCH_TIME_FOREVER);
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- if (_delegateDispatcher.discardTimer == timer) {
-
-
-
- [self resetSessionInternal];
- } else {
-
-
-
-
-
- }
- }
- dispatch_semaphore_signal(_sessionCreationSemaphore);
- }
- - (NSTimeInterval)unusedSessionTimeout {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- return _unusedSessionTimeout;
- }
- }
- - (void)setUnusedSessionTimeout:(NSTimeInterval)timeout {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- _unusedSessionTimeout = timeout;
- _delegateDispatcher.discardInterval = timeout;
- }
- }
- - (void)abandonDispatcher {
- GTMSessionCheckSynchronized(self);
- [_delegateDispatcher abandon];
- }
- - (NSDictionary *)runningFetchersByHost {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- return [_runningFetchersByHost copy];
- }
- }
- - (void)setRunningFetchersByHost:(NSDictionary *)dict {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- _runningFetchersByHost = [dict mutableCopy];
- }
- }
- - (NSDictionary *)delayedFetchersByHost {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- return [_delayedFetchersByHost copy];
- }
- }
- - (void)setDelayedFetchersByHost:(NSDictionary *)dict {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- _delayedFetchersByHost = [dict mutableCopy];
- }
- }
- - (id<GTMFetcherAuthorizationProtocol>)authorizer {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- return _authorizer;
- }
- }
- - (void)setAuthorizer:(id<GTMFetcherAuthorizationProtocol>)obj {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- if (obj != _authorizer) {
- [self detachAuthorizer];
- }
- _authorizer = obj;
- }
-
-
- if ([obj respondsToSelector:@selector(setFetcherService:)]) {
- [obj setFetcherService:self];
- }
- }
- - (void)detachAuthorizer {
-
-
-
-
-
-
-
-
-
-
- if ([_authorizer respondsToSelector:@selector(fetcherService)]) {
- id authFetcherService = [_authorizer fetcherService];
- if (authFetcherService == self) {
- [_authorizer setFetcherService:nil];
- }
- }
- }
- - (nonnull dispatch_queue_t)callbackQueue {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- return _callbackQueue;
- }
- }
- - (void)setCallbackQueue:(dispatch_queue_t)queue {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- _callbackQueue = queue ?: dispatch_get_main_queue();
- }
- }
- - (NSOperationQueue *)sessionDelegateQueue {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- return _delegateQueue;
- }
- }
- - (void)setSessionDelegateQueue:(NSOperationQueue *)queue {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- _delegateQueue = queue ?: [NSOperationQueue mainQueue];
- }
- }
- - (NSOperationQueue *)delegateQueue {
-
-
- return nil;
- }
- + (NSUInteger)numberOfNonBackgroundSessionFetchers:(NSArray *)fetchers {
- NSUInteger sum = 0;
- for (GTMSessionFetcher *fetcher in fetchers) {
- if (!fetcher.usingBackgroundSession) {
- ++sum;
- }
- }
- return sum;
- }
- @end
- @implementation GTMSessionFetcherService (TestingSupport)
- + (instancetype)mockFetcherServiceWithFakedData:(NSData *)fakedDataOrNil
- fakedError:(NSError *)fakedErrorOrNil {
- #if !GTM_DISABLE_FETCHER_TEST_BLOCK
- NSURL *url = [NSURL URLWithString:@"http://example.invalid"];
- NSHTTPURLResponse *fakedResponse =
- [[NSHTTPURLResponse alloc] initWithURL:url
- statusCode:(fakedErrorOrNil ? 500 : 200)HTTPVersion:@"HTTP/1.1"
- headerFields:nil];
- return [self mockFetcherServiceWithFakedData:fakedDataOrNil
- fakedResponse:fakedResponse
- fakedError:fakedErrorOrNil];
- #else
- GTMSESSION_ASSERT_DEBUG(0, @"Test blocks disabled");
- return nil;
- #endif
- }
- + (instancetype)mockFetcherServiceWithFakedData:(NSData *)fakedDataOrNil
- fakedResponse:(NSHTTPURLResponse *)fakedResponse
- fakedError:(NSError *)fakedErrorOrNil {
- #if !GTM_DISABLE_FETCHER_TEST_BLOCK
- GTMSessionFetcherService *service = [[self alloc] init];
- service.allowedInsecureSchemes = @[ @"http" ];
- service.testBlock =
- ^(GTMSessionFetcher *fetcherToTest, GTMSessionFetcherTestResponse testResponse) {
- testResponse(fakedResponse, fakedDataOrNil, fakedErrorOrNil);
- };
- return service;
- #else
- GTMSESSION_ASSERT_DEBUG(0, @"Test blocks disabled");
- return nil;
- #endif
- }
- #pragma mark Synchronous Wait for Unit Testing
- - (BOOL)waitForCompletionOfAllFetchersWithTimeout:(NSTimeInterval)timeoutInSeconds {
- NSDate *giveUpDate = [NSDate dateWithTimeIntervalSinceNow:timeoutInSeconds];
- _stoppedFetchersToWaitFor = [NSMutableArray array];
- BOOL shouldSpinRunLoop = [NSThread isMainThread];
- const NSTimeInterval kSpinInterval = 0.001;
- BOOL didTimeOut = NO;
- while (([self numberOfFetchers] > 0 || _stoppedFetchersToWaitFor.count > 0)) {
- didTimeOut = [giveUpDate timeIntervalSinceNow] < 0;
- if (didTimeOut) break;
- GTMSessionFetcher *stoppedFetcher = _stoppedFetchersToWaitFor.firstObject;
- if (stoppedFetcher) {
- [_stoppedFetchersToWaitFor removeObject:stoppedFetcher];
- [stoppedFetcher waitForCompletionWithTimeout:10.0 * kSpinInterval];
- }
- if (shouldSpinRunLoop) {
- NSDate *stopDate = [NSDate dateWithTimeIntervalSinceNow:kSpinInterval];
- [[NSRunLoop currentRunLoop] runUntilDate:stopDate];
- } else {
- [NSThread sleepForTimeInterval:kSpinInterval];
- }
- }
- _stoppedFetchersToWaitFor = nil;
- return !didTimeOut;
- }
- @end
- @implementation GTMSessionFetcherService (BackwardsCompatibilityOnly)
- - (NSInteger)cookieStorageMethod {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- return _cookieStorageMethod;
- }
- }
- - (void)setCookieStorageMethod:(NSInteger)cookieStorageMethod {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- _cookieStorageMethod = cookieStorageMethod;
- }
- }
- @end
- @implementation GTMSessionFetcherSessionDelegateDispatcher {
- __weak GTMSessionFetcherService *_parentService;
- NSURLSession *_session;
-
- NSMutableDictionary *_taskToFetcherMap;
-
- NSTimer *_discardTimer;
- NSTimeInterval _discardInterval;
- }
- @synthesize discardInterval = _discardInterval, session = _session;
- - (instancetype)init {
- [self doesNotRecognizeSelector:_cmd];
- return nil;
- }
- - (instancetype)initWithParentService:(GTMSessionFetcherService *)parentService
- sessionDiscardInterval:(NSTimeInterval)discardInterval {
- self = [super init];
- if (self) {
- _discardInterval = discardInterval;
- _parentService = parentService;
- }
- return self;
- }
- - (NSString *)description {
- return
- [NSString stringWithFormat:@"%@ %p %@ %@", [self class], self, _session ?: @"<no session>",
- _taskToFetcherMap.count > 0 ? _taskToFetcherMap : @"<no tasks>"];
- }
- - (NSTimer *)discardTimer {
- GTMSessionCheckNotSynchronized(self);
- @synchronized(self) {
- return _discardTimer;
- }
- }
- - (void)startDiscardTimer {
- GTMSessionCheckSynchronized(self);
- [_discardTimer invalidate];
- _discardTimer = nil;
- if (_discardInterval > 0) {
- _discardTimer = [NSTimer timerWithTimeInterval:_discardInterval
- target:self
- selector:@selector(discardTimerFired:)
- userInfo:nil
- repeats:NO];
- [_discardTimer setTolerance:(_discardInterval / 10)];
- [[NSRunLoop mainRunLoop] addTimer:_discardTimer forMode:NSRunLoopCommonModes];
- }
- }
- - (void)destroyDiscardTimer {
- GTMSessionCheckSynchronized(self);
- [_discardTimer invalidate];
- _discardTimer = nil;
- }
- - (void)discardTimerFired:(NSTimer *)timer {
- GTMSessionFetcherService *service;
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- NSUInteger numberOfTasks = _taskToFetcherMap.count;
- if (numberOfTasks == 0) {
- service = _parentService;
- }
- }
-
-
-
-
-
-
-
- [service resetSessionForDispatcherDiscardTimer:timer];
- }
- - (void)abandon {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- [self destroySessionAndTimer];
- }
- }
- - (void)startSessionUsage {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- [self destroyDiscardTimer];
- }
- }
- - (void)destroySessionAndTimer {
- GTMSessionCheckSynchronized(self);
- [self destroyDiscardTimer];
-
- [_session finishTasksAndInvalidate];
-
-
-
- _session = nil;
- }
- - (void)setFetcher:(GTMSessionFetcher *)fetcher forTask:(NSURLSessionTask *)task {
- GTMSESSION_ASSERT_DEBUG(fetcher != nil, @"missing fetcher");
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- if (_taskToFetcherMap == nil) {
- _taskToFetcherMap = [[NSMutableDictionary alloc] init];
- }
- if (fetcher) {
- [_taskToFetcherMap setObject:fetcher forKey:task];
- [self destroyDiscardTimer];
- }
- }
- }
- - (void)removeFetcher:(GTMSessionFetcher *)fetcher {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
-
-
-
-
- NSArray *tasks = [_taskToFetcherMap allKeysForObject:fetcher];
- GTMSESSION_ASSERT_DEBUG(tasks.count <= 1, @"fetcher task not unmapped: %@", tasks);
- [_taskToFetcherMap removeObjectsForKeys:tasks];
- if (_taskToFetcherMap.count == 0) {
- [self startDiscardTimer];
- }
- }
- }
- - (id)fetcherForTask:(NSURLSessionTask *)task {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- return [_taskToFetcherMap objectForKey:task];
- }
- }
- - (void)removeTaskFromMap:(NSURLSessionTask *)task {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- [_taskToFetcherMap removeObjectForKey:task];
- }
- }
- - (void)setSession:(NSURLSession *)session {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- _session = session;
- }
- }
- - (NSURLSession *)session {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- return _session;
- }
- }
- - (NSTimeInterval)discardInterval {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- return _discardInterval;
- }
- }
- - (void)setDiscardInterval:(NSTimeInterval)interval {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- _discardInterval = interval;
- }
- }
- - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error {
- GTM_LOG_SESSION_DELEGATE(@"%@ %p URLSession:%@ didBecomeInvalidWithError:%@", [self class], self,
- session, error);
- NSDictionary *localTaskToFetcherMap;
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- _session = nil;
- localTaskToFetcherMap = [_taskToFetcherMap copy];
- }
-
-
- [localTaskToFetcherMap enumerateKeysAndObjectsUsingBlock:^(
- NSURLSessionTask *task, GTMSessionFetcher *fetcher, BOOL *stop) {
- if (fetcher.session == session) {
-
-
- NSError *canceledError = [NSError errorWithDomain:NSURLErrorDomain
- code:NSURLErrorCancelled
- userInfo:nil];
- [self URLSession:session task:task didCompleteWithError:canceledError];
- } else {
- GTMSESSION_ASSERT_DEBUG(0, @"Unexpected session in fetcher: %@ has %@ (expected %@)", fetcher,
- fetcher.session, session);
- }
- }];
-
- NSDictionary *userInfo = @{kGTMSessionFetcherServiceSessionKey : session};
- NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
- [nc postNotificationName:kGTMSessionFetcherServiceSessionBecameInvalidNotification
- object:_parentService
- userInfo:userInfo];
- }
- #pragma mark - NSURLSessionTaskDelegate
- - (void)URLSession:(NSURLSession *)session
- task:(NSURLSessionTask *)task
- willPerformHTTPRedirection:(NSHTTPURLResponse *)response
- newRequest:(NSURLRequest *)request
- completionHandler:(void (^)(NSURLRequest *))completionHandler {
- id<NSURLSessionTaskDelegate> fetcher = [self fetcherForTask:task];
- [fetcher URLSession:session
- task:task
- willPerformHTTPRedirection:response
- newRequest:request
- completionHandler:completionHandler];
- }
- - (void)URLSession:(NSURLSession *)session
- task:(NSURLSessionTask *)task
- didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
- completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))handler {
- id<NSURLSessionTaskDelegate> fetcher = [self fetcherForTask:task];
- [fetcher URLSession:session task:task didReceiveChallenge:challenge completionHandler:handler];
- }
- - (void)URLSession:(NSURLSession *)session
- task:(NSURLSessionTask *)task
- needNewBodyStream:(void (^)(NSInputStream *bodyStream))handler {
- id<NSURLSessionTaskDelegate> fetcher = [self fetcherForTask:task];
- [fetcher URLSession:session task:task needNewBodyStream:handler];
- }
- - (void)URLSession:(NSURLSession *)session
- task:(NSURLSessionTask *)task
- didSendBodyData:(int64_t)bytesSent
- totalBytesSent:(int64_t)totalBytesSent
- totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
- id<NSURLSessionTaskDelegate> fetcher = [self fetcherForTask:task];
- [fetcher URLSession:session
- task:task
- didSendBodyData:bytesSent
- totalBytesSent:totalBytesSent
- totalBytesExpectedToSend:totalBytesExpectedToSend];
- }
- - (void)URLSession:(NSURLSession *)session
- task:(NSURLSessionTask *)task
- didCompleteWithError:(NSError *)error {
- id<NSURLSessionTaskDelegate> fetcher = [self fetcherForTask:task];
-
- [self removeTaskFromMap:task];
- [fetcher URLSession:session task:task didCompleteWithError:error];
- }
- - (void)URLSession:(NSURLSession *)session
- task:(NSURLSessionTask *)task
- didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics
- API_AVAILABLE(ios(10.0), macosx(10.12), tvos(10.0), watchos(6.0)) {
- id<NSURLSessionTaskDelegate> fetcher = [self fetcherForTask:task];
- [fetcher URLSession:session task:task didFinishCollectingMetrics:metrics];
- }
- - (void)URLSession:(NSURLSession *)session
- dataTask:(NSURLSessionDataTask *)dataTask
- didReceiveResponse:(NSURLResponse *)response
- completionHandler:(void (^)(NSURLSessionResponseDisposition))handler {
- id<NSURLSessionDataDelegate> fetcher = [self fetcherForTask:dataTask];
- [fetcher URLSession:session
- dataTask:dataTask
- didReceiveResponse:response
- completionHandler:handler];
- }
- - (void)URLSession:(NSURLSession *)session
- dataTask:(NSURLSessionDataTask *)dataTask
- didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask {
- id<NSURLSessionDataDelegate> fetcher = [self fetcherForTask:dataTask];
- GTMSESSION_ASSERT_DEBUG(fetcher != nil, @"Missing fetcher for %@", dataTask);
- [self removeTaskFromMap:dataTask];
- if (fetcher) {
- GTMSESSION_ASSERT_DEBUG([fetcher isKindOfClass:[GTMSessionFetcher class]],
- @"Expecting GTMSessionFetcher");
- [self setFetcher:(GTMSessionFetcher *)fetcher forTask:downloadTask];
- }
- [fetcher URLSession:session dataTask:dataTask didBecomeDownloadTask:downloadTask];
- }
- - (void)URLSession:(NSURLSession *)session
- dataTask:(NSURLSessionDataTask *)dataTask
- didReceiveData:(NSData *)data {
- id<NSURLSessionDataDelegate> fetcher = [self fetcherForTask:dataTask];
- [fetcher URLSession:session dataTask:dataTask didReceiveData:data];
- }
- - (void)URLSession:(NSURLSession *)session
- dataTask:(NSURLSessionDataTask *)dataTask
- willCacheResponse:(NSCachedURLResponse *)proposedResponse
- completionHandler:(void (^)(NSCachedURLResponse *))handler {
- id<NSURLSessionDataDelegate> fetcher = [self fetcherForTask:dataTask];
- [fetcher URLSession:session
- dataTask:dataTask
- willCacheResponse:proposedResponse
- completionHandler:handler];
- }
- - (void)URLSession:(NSURLSession *)session
- downloadTask:(NSURLSessionDownloadTask *)downloadTask
- didFinishDownloadingToURL:(NSURL *)location {
- id<NSURLSessionDownloadDelegate> fetcher = [self fetcherForTask:downloadTask];
- [fetcher URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
- }
- - (void)URLSession:(NSURLSession *)session
- downloadTask:(NSURLSessionDownloadTask *)downloadTask
- didWriteData:(int64_t)bytesWritten
- totalBytesWritten:(int64_t)totalWritten
- totalBytesExpectedToWrite:(int64_t)totalExpected {
- id<NSURLSessionDownloadDelegate> fetcher = [self fetcherForTask:downloadTask];
- [fetcher URLSession:session
- downloadTask:downloadTask
- didWriteData:bytesWritten
- totalBytesWritten:totalWritten
- totalBytesExpectedToWrite:totalExpected];
- }
- - (void)URLSession:(NSURLSession *)session
- downloadTask:(NSURLSessionDownloadTask *)downloadTask
- didResumeAtOffset:(int64_t)fileOffset
- expectedTotalBytes:(int64_t)expectedTotalBytes {
- id<NSURLSessionDownloadDelegate> fetcher = [self fetcherForTask:downloadTask];
- [fetcher URLSession:session
- downloadTask:downloadTask
- didResumeAtOffset:fileOffset
- expectedTotalBytes:expectedTotalBytes];
- }
- @end
|