PageRenderTime 53ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/core/externals/update-engine/externals/gdata-objectivec-client/Source/HTTPFetcher/GTMHTTPFetcherService.m

http://macfuse.googlecode.com/
Objective C | 499 lines | 358 code | 79 blank | 62 comment | 72 complexity | 2034353ba6b541a464d1e02b5d0e0392 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause, GPL-2.0
  1. /* Copyright (c) 2010 Google Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. //
  16. // GTMHTTPFetcherService.m
  17. //
  18. #import "GTMHTTPFetcherService.h"
  19. @interface GTMHTTPFetcher (ServiceMethods)
  20. - (BOOL)beginFetchMayDelay:(BOOL)mayDelay
  21. mayAuthorize:(BOOL)mayAuthorize;
  22. @end
  23. @interface GTMHTTPFetcherService ()
  24. @property (retain, readwrite) NSDictionary *delayedHosts;
  25. @property (retain, readwrite) NSDictionary *runningHosts;
  26. - (void)detachAuthorizer;
  27. @end
  28. @implementation GTMHTTPFetcherService
  29. @synthesize maxRunningFetchersPerHost = maxRunningFetchersPerHost_,
  30. userAgent = userAgent_,
  31. timeout = timeout_,
  32. delegateQueue = delegateQueue_,
  33. runLoopModes = runLoopModes_,
  34. credential = credential_,
  35. proxyCredential = proxyCredential_,
  36. cookieStorageMethod = cookieStorageMethod_,
  37. shouldFetchInBackground = shouldFetchInBackground_,
  38. fetchHistory = fetchHistory_;
  39. - (id)init {
  40. self = [super init];
  41. if (self) {
  42. fetchHistory_ = [[GTMHTTPFetchHistory alloc] init];
  43. delayedHosts_ = [[NSMutableDictionary alloc] init];
  44. runningHosts_ = [[NSMutableDictionary alloc] init];
  45. cookieStorageMethod_ = kGTMHTTPFetcherCookieStorageMethodFetchHistory;
  46. maxRunningFetchersPerHost_ = 10;
  47. }
  48. return self;
  49. }
  50. - (void)dealloc {
  51. [self detachAuthorizer];
  52. [delayedHosts_ release];
  53. [runningHosts_ release];
  54. [fetchHistory_ release];
  55. [userAgent_ release];
  56. [delegateQueue_ release];
  57. [runLoopModes_ release];
  58. [credential_ release];
  59. [proxyCredential_ release];
  60. [authorizer_ release];
  61. [super dealloc];
  62. }
  63. #pragma mark Generate a new fetcher
  64. - (id)fetcherWithRequest:(NSURLRequest *)request
  65. fetcherClass:(Class)fetcherClass {
  66. GTMHTTPFetcher *fetcher = [fetcherClass fetcherWithRequest:request];
  67. fetcher.fetchHistory = self.fetchHistory;
  68. fetcher.delegateQueue = self.delegateQueue;
  69. fetcher.runLoopModes = self.runLoopModes;
  70. fetcher.cookieStorageMethod = self.cookieStorageMethod;
  71. fetcher.credential = self.credential;
  72. fetcher.proxyCredential = self.proxyCredential;
  73. fetcher.shouldFetchInBackground = self.shouldFetchInBackground;
  74. fetcher.authorizer = self.authorizer;
  75. fetcher.service = self;
  76. NSString *userAgent = self.userAgent;
  77. if ([userAgent length] > 0
  78. && [request valueForHTTPHeaderField:@"User-Agent"] == nil) {
  79. [fetcher.mutableRequest setValue:userAgent
  80. forHTTPHeaderField:@"User-Agent"];
  81. }
  82. NSTimeInterval timeout = self.timeout;
  83. if (timeout > 0.0) {
  84. [fetcher.mutableRequest setTimeoutInterval:timeout];
  85. }
  86. return fetcher;
  87. }
  88. - (GTMHTTPFetcher *)fetcherWithRequest:(NSURLRequest *)request {
  89. return [self fetcherWithRequest:request
  90. fetcherClass:[GTMHTTPFetcher class]];
  91. }
  92. - (GTMHTTPFetcher *)fetcherWithURL:(NSURL *)requestURL {
  93. return [self fetcherWithRequest:[NSURLRequest requestWithURL:requestURL]];
  94. }
  95. - (GTMHTTPFetcher *)fetcherWithURLString:(NSString *)requestURLString {
  96. return [self fetcherWithURL:[NSURL URLWithString:requestURLString]];
  97. }
  98. #pragma mark Queue Management
  99. - (void)addRunningFetcher:(GTMHTTPFetcher *)fetcher
  100. forHost:(NSString *)host {
  101. // Add to the array of running fetchers for this host, creating the array
  102. // if needed
  103. NSMutableArray *runningForHost = [runningHosts_ objectForKey:host];
  104. if (runningForHost == nil) {
  105. runningForHost = [NSMutableArray arrayWithObject:fetcher];
  106. [runningHosts_ setObject:runningForHost forKey:host];
  107. } else {
  108. [runningForHost addObject:fetcher];
  109. }
  110. }
  111. - (void)addDelayedFetcher:(GTMHTTPFetcher *)fetcher
  112. forHost:(NSString *)host {
  113. // Add to the array of delayed fetchers for this host, creating the array
  114. // if needed
  115. NSMutableArray *delayedForHost = [delayedHosts_ objectForKey:host];
  116. if (delayedForHost == nil) {
  117. delayedForHost = [NSMutableArray arrayWithObject:fetcher];
  118. [delayedHosts_ setObject:delayedForHost forKey:host];
  119. } else {
  120. [delayedForHost addObject:fetcher];
  121. }
  122. }
  123. - (BOOL)isDelayingFetcher:(GTMHTTPFetcher *)fetcher {
  124. @synchronized(self) {
  125. NSString *host = [[[fetcher mutableRequest] URL] host];
  126. NSArray *delayedForHost = [delayedHosts_ objectForKey:host];
  127. NSUInteger idx = [delayedForHost indexOfObjectIdenticalTo:fetcher];
  128. BOOL isDelayed = (delayedForHost != nil) && (idx != NSNotFound);
  129. return isDelayed;
  130. }
  131. }
  132. - (BOOL)fetcherShouldBeginFetching:(GTMHTTPFetcher *)fetcher {
  133. // Entry point from the fetcher
  134. @synchronized(self) {
  135. NSURL *requestURL = [[fetcher mutableRequest] URL];
  136. NSString *host = [requestURL host];
  137. // Addresses "file:///path" case where localhost is the implicit host.
  138. if ([host length] == 0 && [requestURL isFileURL]) {
  139. host = @"localhost";
  140. }
  141. if ([host length] == 0) {
  142. #if DEBUG
  143. // Data URIs legitimately have no host, reject other hostless URLs.
  144. NSAssert1([[requestURL scheme] isEqual:@"data"], @"%@ lacks host", fetcher);
  145. #endif
  146. return YES;
  147. }
  148. NSMutableArray *runningForHost = [runningHosts_ objectForKey:host];
  149. if (runningForHost != nil
  150. && [runningForHost indexOfObjectIdenticalTo:fetcher] != NSNotFound) {
  151. #if DEBUG
  152. NSAssert1(0, @"%@ was already running", fetcher);
  153. #endif
  154. return YES;
  155. }
  156. // We'll save the host that serves as the key for this fetcher's array
  157. // to avoid any chance of the underlying request changing, stranding
  158. // the fetcher in the wrong array
  159. fetcher.serviceHost = host;
  160. fetcher.thread = [NSThread currentThread];
  161. if (maxRunningFetchersPerHost_ == 0
  162. || maxRunningFetchersPerHost_ > [runningForHost count]) {
  163. [self addRunningFetcher:fetcher forHost:host];
  164. return YES;
  165. } else {
  166. [self addDelayedFetcher:fetcher forHost:host];
  167. return NO;
  168. }
  169. }
  170. return YES;
  171. }
  172. // Fetcher start and stop methods, invoked on the appropriate thread for
  173. // the fetcher
  174. - (void)performSelector:(SEL)sel onStartThreadForFetcher:(GTMHTTPFetcher *)fetcher {
  175. NSOperationQueue *delegateQueue = fetcher.delegateQueue;
  176. NSThread *thread = fetcher.thread;
  177. if (delegateQueue != nil || [thread isEqual:[NSThread currentThread]]) {
  178. // The fetcher should run on the thread we're on now, or there's a delegate
  179. // queue specified so it doesn't matter what thread the fetcher is started
  180. // on, since it will call back on the queue.
  181. [self performSelector:sel withObject:fetcher];
  182. } else {
  183. // Fetcher must run on a specified thread (and that thread must have a
  184. // run loop.)
  185. [self performSelector:sel
  186. onThread:thread
  187. withObject:fetcher
  188. waitUntilDone:NO];
  189. }
  190. }
  191. - (void)startFetcherOnCurrentThread:(GTMHTTPFetcher *)fetcher {
  192. [fetcher beginFetchMayDelay:NO
  193. mayAuthorize:YES];
  194. }
  195. - (void)startFetcher:(GTMHTTPFetcher *)fetcher {
  196. [self performSelector:@selector(startFetcherOnCurrentThread:)
  197. onStartThreadForFetcher:fetcher];
  198. }
  199. - (void)stopFetcherOnCurrentThread:(GTMHTTPFetcher *)fetcher {
  200. [fetcher stopFetching];
  201. }
  202. - (void)stopFetcher:(GTMHTTPFetcher *)fetcher {
  203. [self performSelector:@selector(stopFetcherOnCurrentThread:)
  204. onStartThreadForFetcher:fetcher];
  205. }
  206. - (void)fetcherDidStop:(GTMHTTPFetcher *)fetcher {
  207. // Entry point from the fetcher
  208. @synchronized(self) {
  209. NSString *host = fetcher.serviceHost;
  210. if (!host) {
  211. // fetcher has been stopped previously
  212. return;
  213. }
  214. NSMutableArray *runningForHost = [runningHosts_ objectForKey:host];
  215. [runningForHost removeObject:fetcher];
  216. NSMutableArray *delayedForHost = [delayedHosts_ objectForKey:host];
  217. [delayedForHost removeObject:fetcher];
  218. while ([delayedForHost count] > 0
  219. && [runningForHost count] < maxRunningFetchersPerHost_) {
  220. // Start another delayed fetcher running, scanning for the minimum
  221. // priority value, defaulting to FIFO for equal priorities
  222. GTMHTTPFetcher *nextFetcher = nil;
  223. for (GTMHTTPFetcher *delayedFetcher in delayedForHost) {
  224. if (nextFetcher == nil
  225. || delayedFetcher.servicePriority < nextFetcher.servicePriority) {
  226. nextFetcher = delayedFetcher;
  227. }
  228. }
  229. if (nextFetcher) {
  230. [self addRunningFetcher:nextFetcher forHost:host];
  231. runningForHost = [runningHosts_ objectForKey:host];
  232. [delayedForHost removeObjectIdenticalTo:nextFetcher];
  233. [self startFetcher:nextFetcher];
  234. }
  235. }
  236. if ([runningForHost count] == 0) {
  237. // None left; remove the empty array
  238. [runningHosts_ removeObjectForKey:host];
  239. }
  240. if ([delayedForHost count] == 0) {
  241. [delayedHosts_ removeObjectForKey:host];
  242. }
  243. // The fetcher is no longer in the running or the delayed array,
  244. // so remove its host and thread properties
  245. fetcher.serviceHost = nil;
  246. fetcher.thread = nil;
  247. }
  248. }
  249. - (NSUInteger)numberOfFetchers {
  250. @synchronized(self) {
  251. NSUInteger running = [self numberOfRunningFetchers];
  252. NSUInteger delayed = [self numberOfDelayedFetchers];
  253. return running + delayed;
  254. }
  255. }
  256. - (NSUInteger)numberOfRunningFetchers {
  257. @synchronized(self) {
  258. NSUInteger sum = 0;
  259. for (NSString *host in runningHosts_) {
  260. NSArray *fetchers = [runningHosts_ objectForKey:host];
  261. sum += [fetchers count];
  262. }
  263. return sum;
  264. }
  265. }
  266. - (NSUInteger)numberOfDelayedFetchers {
  267. @synchronized(self) {
  268. NSUInteger sum = 0;
  269. for (NSString *host in delayedHosts_) {
  270. NSArray *fetchers = [delayedHosts_ objectForKey:host];
  271. sum += [fetchers count];
  272. }
  273. return sum;
  274. }
  275. }
  276. - (NSArray *)issuedFetchersWithRequestURL:(NSURL *)requestURL {
  277. @synchronized(self) {
  278. NSMutableArray *array = nil;
  279. NSString *host = [requestURL host];
  280. if ([host length] == 0) return nil;
  281. NSURL *absRequestURL = [requestURL absoluteURL];
  282. NSArray *runningForHost = [runningHosts_ objectForKey:host];
  283. for (GTMHTTPFetcher *fetcher in runningForHost) {
  284. NSURL *fetcherURL = [[[fetcher mutableRequest] URL] absoluteURL];
  285. if ([fetcherURL isEqual:absRequestURL]) {
  286. if (array == nil) {
  287. array = [NSMutableArray array];
  288. }
  289. [array addObject:fetcher];
  290. }
  291. }
  292. NSArray *delayedForHost = [delayedHosts_ objectForKey:host];
  293. for (GTMHTTPFetcher *fetcher in delayedForHost) {
  294. NSURL *fetcherURL = [[[fetcher mutableRequest] URL] absoluteURL];
  295. if ([fetcherURL isEqual:absRequestURL]) {
  296. if (array == nil) {
  297. array = [NSMutableArray array];
  298. }
  299. [array addObject:fetcher];
  300. }
  301. }
  302. return array;
  303. }
  304. }
  305. - (void)stopAllFetchers {
  306. @synchronized(self) {
  307. // Remove fetchers from the delayed list to avoid fetcherDidStop: from
  308. // starting more fetchers running as a side effect of stopping one
  309. NSArray *delayedForHosts = [delayedHosts_ allValues];
  310. [delayedHosts_ removeAllObjects];
  311. for (NSArray *delayedForHost in delayedForHosts) {
  312. for (GTMHTTPFetcher *fetcher in delayedForHost) {
  313. [self stopFetcher:fetcher];
  314. }
  315. }
  316. NSArray *runningForHosts = [runningHosts_ allValues];
  317. [runningHosts_ removeAllObjects];
  318. for (NSArray *runningForHost in runningForHosts) {
  319. for (GTMHTTPFetcher *fetcher in runningForHost) {
  320. [self stopFetcher:fetcher];
  321. }
  322. }
  323. }
  324. }
  325. #pragma mark Fetch History Settings
  326. // Turn on data caching to receive a copy of previously-retrieved objects.
  327. // Otherwise, fetches may return status 304 (No Change) rather than actual data
  328. - (void)setShouldCacheETaggedData:(BOOL)flag {
  329. self.fetchHistory.shouldCacheETaggedData = flag;
  330. }
  331. - (BOOL)shouldCacheETaggedData {
  332. return self.fetchHistory.shouldCacheETaggedData;
  333. }
  334. - (void)setETaggedDataCacheCapacity:(NSUInteger)totalBytes {
  335. self.fetchHistory.memoryCapacity = totalBytes;
  336. }
  337. - (NSUInteger)ETaggedDataCacheCapacity {
  338. return self.fetchHistory.memoryCapacity;
  339. }
  340. - (void)setShouldRememberETags:(BOOL)flag {
  341. self.fetchHistory.shouldRememberETags = flag;
  342. }
  343. - (BOOL)shouldRememberETags {
  344. return self.fetchHistory.shouldRememberETags;
  345. }
  346. // reset the ETag cache to avoid getting a Not Modified status
  347. // based on prior queries
  348. - (void)clearETaggedDataCache {
  349. [self.fetchHistory clearETaggedDataCache];
  350. }
  351. - (void)clearHistory {
  352. [self clearETaggedDataCache];
  353. [self.fetchHistory removeAllCookies];
  354. }
  355. #pragma mark Synchronous Wait for Unit Testing
  356. - (void)waitForCompletionOfAllFetchersWithTimeout:(NSTimeInterval)timeoutInSeconds {
  357. NSDate* giveUpDate = [NSDate dateWithTimeIntervalSinceNow:timeoutInSeconds];
  358. BOOL isMainThread = [NSThread isMainThread];
  359. while ([self numberOfFetchers] > 0
  360. && [giveUpDate timeIntervalSinceNow] > 0) {
  361. // Run the current run loop 1/1000 of a second to give the networking
  362. // code a chance to work
  363. if (isMainThread || delegateQueue_ == nil) {
  364. NSDate *stopDate = [NSDate dateWithTimeIntervalSinceNow:0.001];
  365. [[NSRunLoop currentRunLoop] runUntilDate:stopDate];
  366. } else {
  367. // Sleep on the delegate queue's background thread.
  368. [NSThread sleepForTimeInterval:0.001];
  369. }
  370. }
  371. }
  372. #pragma mark Accessors
  373. - (NSDictionary *)runningHosts {
  374. return runningHosts_;
  375. }
  376. - (void)setRunningHosts:(NSDictionary *)dict {
  377. [runningHosts_ autorelease];
  378. runningHosts_ = [dict mutableCopy];
  379. }
  380. - (NSDictionary *)delayedHosts {
  381. return delayedHosts_;
  382. }
  383. - (void)setDelayedHosts:(NSDictionary *)dict {
  384. [delayedHosts_ autorelease];
  385. delayedHosts_ = [dict mutableCopy];
  386. }
  387. - (id <GTMFetcherAuthorizationProtocol>)authorizer {
  388. return authorizer_;
  389. }
  390. - (void)setAuthorizer:(id <GTMFetcherAuthorizationProtocol>)obj {
  391. if (obj != authorizer_) {
  392. [self detachAuthorizer];
  393. }
  394. [authorizer_ autorelease];
  395. authorizer_ = [obj retain];
  396. // Use the fetcher service for the authorization fetches if the auth
  397. // object supports fetcher services
  398. if ([authorizer_ respondsToSelector:@selector(setFetcherService:)]) {
  399. [authorizer_ setFetcherService:self];
  400. }
  401. }
  402. - (void)detachAuthorizer {
  403. // This method is called by the fetcher service's dealloc and setAuthorizer:
  404. // methods; do not override.
  405. //
  406. // The fetcher service retains the authorizer, and the authorizer has a
  407. // weak pointer to the fetcher service (a non-zeroing pointer for
  408. // compatibility with iOS 4 and Mac OS X 10.5/10.6.)
  409. //
  410. // When this fetcher service no longer uses the authorizer, we want to remove
  411. // the authorizer's dependence on the fetcher service. Authorizers can still
  412. // function without a fetcher service.
  413. if ([authorizer_ respondsToSelector:@selector(fetcherService)]) {
  414. GTMHTTPFetcherService *authFS = [authorizer_ fetcherService];
  415. if (authFS == self) {
  416. [authorizer_ setFetcherService:nil];
  417. }
  418. }
  419. }
  420. @end