>
- NSString *binStr = [NSString stringWithFormat:@"\r%@\r<<%lu bytes>>\r",
- header, (long)(partSize - header.length)];
- [origParts addObject:binStr];
- }
- offset += partSize + boundary.length;
- }
- // rejoin the original parts
- streamDataStr = [origParts componentsJoinedByString:boundary];
- }
- }
- if (!streamDataStr) {
- // give up; just make a string showing the uploaded bytes
- streamDataStr = [NSString stringWithFormat:@"<<%u bytes>>", (unsigned int)data.length];
- }
- return streamDataStr;
-}
-
-// logFetchWithError is called following a successful or failed fetch attempt
-//
-// This method does all the work for appending to and creating log files
-
-- (void)logFetchWithError:(NSError *)error {
- if (![[self class] isLoggingEnabled]) return;
- NSString *logDirectory = [[self class] logDirectoryForCurrentRun];
- if (!logDirectory) return;
- NSString *processName = [[self class] loggingProcessName];
-
- // TODO: add Javascript to display response data formatted in hex
-
- // each response's NSData goes into its own xml or txt file, though all responses for this run of
- // the app share a main html file. This counter tracks all fetch responses for this app run.
- //
- // we'll use a local variable since this routine may be reentered while waiting for XML formatting
- // to be completed by an external task
- static int gResponseCounter = 0;
- int responseCounter = ++gResponseCounter;
-
- NSURLResponse *response = [self response];
- NSDictionary *responseHeaders = [self responseHeaders];
- NSString *responseDataStr = nil;
- NSDictionary *responseJSON = nil;
-
- // if there's response data, decide what kind of file to put it in based on the first bytes of the
- // file or on the mime type supplied by the server
- NSString *responseMIMEType = [response MIMEType];
- BOOL isResponseImage = NO;
-
- // file name for an image data file
- NSString *responseDataFileName = nil;
-
- int64_t responseDataLength = self.downloadedLength;
- if (responseDataLength > 0) {
- NSData *downloadedData = self.downloadedData;
- if (downloadedData == nil
- && responseDataLength > 0
- && responseDataLength < 20000
- && self.destinationFileURL) {
- // There's a download file that's not too big, so get the data to display from the downloaded
- // file.
- NSURL *destinationURL = self.destinationFileURL;
- downloadedData = [NSData dataWithContentsOfURL:destinationURL];
- }
- NSString *responseType = [responseHeaders valueForKey:@"Content-Type"];
- responseDataStr = [self formattedStringFromData:downloadedData
- contentType:responseType
- JSON:&responseJSON];
- NSString *responseDataExtn = nil;
- NSData *dataToWrite = nil;
- if (responseDataStr) {
- // we were able to make a UTF-8 string from the response data
- if ([responseMIMEType isEqual:@"application/atom+xml"]
- || [responseMIMEType hasSuffix:@"/xml"]) {
- responseDataExtn = @"xml";
- dataToWrite = [responseDataStr dataUsingEncoding:NSUTF8StringEncoding];
- }
- } else if ([responseMIMEType isEqual:@"image/jpeg"]) {
- responseDataExtn = @"jpg";
- dataToWrite = downloadedData;
- isResponseImage = YES;
- } else if ([responseMIMEType isEqual:@"image/gif"]) {
- responseDataExtn = @"gif";
- dataToWrite = downloadedData;
- isResponseImage = YES;
- } else if ([responseMIMEType isEqual:@"image/png"]) {
- responseDataExtn = @"png";
- dataToWrite = downloadedData;
- isResponseImage = YES;
- } else {
- // add more non-text types here
- }
- // if we have an extension, save the raw data in a file with that extension
- if (responseDataExtn && dataToWrite) {
- // generate a response file base name like
- NSString *responseBaseName = [NSString stringWithFormat:@"fetch_%d_response", responseCounter];
- responseDataFileName = [responseBaseName stringByAppendingPathExtension:responseDataExtn];
- NSString *responseDataFilePath = [logDirectory stringByAppendingPathComponent:responseDataFileName];
-
- NSError *downloadedError = nil;
- if (gIsLoggingToFile && ![dataToWrite writeToFile:responseDataFilePath
- options:0
- error:&downloadedError]) {
- NSLog(@"%@ logging write error:%@ (%@)", [self class], downloadedError, responseDataFileName);
- }
- }
- }
- // we'll have one main html file per run of the app
- NSString *htmlName = [[self class] htmlFileName];
- NSString *htmlPath =[logDirectory stringByAppendingPathComponent:htmlName];
-
- // if the html file exists (from logging previous fetches) we don't need
- // to re-write the header or the scripts
- NSFileManager *fileMgr = [NSFileManager defaultManager];
- BOOL didFileExist = [fileMgr fileExistsAtPath:htmlPath];
-
- NSMutableString* outputHTML = [NSMutableString string];
-
- // we need a header to say we'll have UTF-8 text
- if (!didFileExist) {
- [outputHTML appendFormat:@"%@ HTTP fetch log %@",
- processName, [[self class] loggingDateStamp]];
- }
- // now write the visible html elements
- NSString *copyableFileName = [NSString stringWithFormat:@"fetch_%d.txt", responseCounter];
-
- NSDate *now = [NSDate date];
- // write the date & time, the comment, and the link to the plain-text (copyable) log
- [outputHTML appendFormat:@"%@ ", now];
-
- NSString *comment = [self comment];
- if (comment.length > 0) {
- [outputHTML appendFormat:@"%@ ", comment];
- }
- [outputHTML appendFormat:@"request/response log
", copyableFileName];
- NSTimeInterval elapsed = -self.initialBeginFetchDate.timeIntervalSinceNow;
- [outputHTML appendFormat:@"elapsed: %5.3fsec
", elapsed];
-
- // write the request URL
- NSURLRequest *request = self.request;
- NSString *requestMethod = request.HTTPMethod;
- NSURL *requestURL = request.URL;
-
- // Save the request URL for next time in case this redirects.
- NSString *redirectedFromURLString = [self.redirectedFromURL absoluteString];
- self.redirectedFromURL = [requestURL copy];
- if (redirectedFromURLString) {
- [outputHTML appendFormat:@"redirected from %@
",
- redirectedFromURLString];
- }
- [outputHTML appendFormat:@"request: %@ %@
\n", requestMethod, requestURL];
-
- // write the request headers
- NSDictionary *requestHeaders = request.allHTTPHeaderFields;
- NSUInteger numberOfRequestHeaders = requestHeaders.count;
- if (numberOfRequestHeaders > 0) {
- // Indicate if the request is authorized; warn if the request is authorized but non-SSL
- NSString *auth = [requestHeaders objectForKey:@"Authorization"];
- NSString *headerDetails = @"";
- if (auth) {
- BOOL isInsecure = [[requestURL scheme] isEqual:@"http"];
- if (isInsecure) {
- // 26A0 = ⚠
- headerDetails =
- @" authorized, non-SSL ⚠ ";
- } else {
- headerDetails = @" authorized";
- }
- }
- NSString *cookiesHdr = [requestHeaders objectForKey:@"Cookie"];
- if (cookiesHdr) {
- headerDetails = [headerDetails stringByAppendingString:@" cookies"];
- }
- NSString *matchHdr = [requestHeaders objectForKey:@"If-Match"];
- if (matchHdr) {
- headerDetails = [headerDetails stringByAppendingString:@" if-match"];
- }
- matchHdr = [requestHeaders objectForKey:@"If-None-Match"];
- if (matchHdr) {
- headerDetails = [headerDetails stringByAppendingString:@" if-none-match"];
- }
- [outputHTML appendFormat:@" headers: %d %@
",
- (int)numberOfRequestHeaders, headerDetails];
- } else {
- [outputHTML appendFormat:@" headers: none
"];
- }
- // write the request post data
- NSData *bodyData = nil;
- NSData *loggedStreamData = self.loggedStreamData;
- if (loggedStreamData) {
- bodyData = loggedStreamData;
- } else {
- bodyData = self.bodyData;
- if (bodyData == nil) {
- bodyData = self.request.HTTPBody;
- }
- }
- uint64_t bodyDataLength = bodyData.length;
-
- if (bodyData.length == 0) {
- // If the data is in a body upload file URL, read that in if it's not huge.
- NSURL *bodyFileURL = self.bodyFileURL;
- if (bodyFileURL) {
- NSNumber *fileSizeNum = nil;
- NSError *fileSizeError = nil;
- if ([bodyFileURL getResourceValue:&fileSizeNum
- forKey:NSURLFileSizeKey
- error:&fileSizeError]) {
- bodyDataLength = [fileSizeNum unsignedLongLongValue];
- if (bodyDataLength > 0 && bodyDataLength < 50000) {
- bodyData = [NSData dataWithContentsOfURL:bodyFileURL
- options:NSDataReadingUncached
- error:&fileSizeError];
- }
- }
- }
- }
- NSString *bodyDataStr = nil;
- NSString *postType = [requestHeaders valueForKey:@"Content-Type"];
-
- if (bodyDataLength > 0) {
- [outputHTML appendFormat:@" data: %llu bytes, %@
\n",
- bodyDataLength, postType ? postType : @"(no type)"];
- NSString *logRequestBody = self.logRequestBody;
- if (logRequestBody) {
- bodyDataStr = [logRequestBody copy];
- self.logRequestBody = nil;
- } else {
- bodyDataStr = [self stringFromStreamData:bodyData
- contentType:postType];
- if (bodyDataStr) {
- // remove OAuth 2 client secret and refresh token
- bodyDataStr = [[self class] snipSubstringOfString:bodyDataStr
- betweenStartString:@"client_secret="
- endString:@"&"];
- bodyDataStr = [[self class] snipSubstringOfString:bodyDataStr
- betweenStartString:@"refresh_token="
- endString:@"&"];
- // remove ClientLogin password
- bodyDataStr = [[self class] snipSubstringOfString:bodyDataStr
- betweenStartString:@"&Passwd="
- endString:@"&"];
- }
- }
- } else {
- // no post data
- }
- // write the response status, MIME type, URL
- NSInteger status = [self statusCode];
- if (response) {
- NSString *statusString = @"";
- if (status != 0) {
- if (status == 200 || status == 201) {
- statusString = [NSString stringWithFormat:@"%ld", (long)status];
-
- // report any JSON-RPC error
- if ([responseJSON isKindOfClass:[NSDictionary class]]) {
- NSDictionary *jsonError = [responseJSON objectForKey:@"error"];
- if ([jsonError isKindOfClass:[NSDictionary class]]) {
- NSString *jsonCode = [[jsonError valueForKey:@"code"] description];
- NSString *jsonMessage = [jsonError valueForKey:@"message"];
- if (jsonCode || jsonMessage) {
- // 2691 = ⚑
- NSString *const jsonErrFmt =
- @" JSON error: %@ %@ ⚑";
- statusString = [statusString stringByAppendingFormat:jsonErrFmt,
- jsonCode ? jsonCode : @"",
- jsonMessage ? jsonMessage : @""];
- }
- }
- }
- } else {
- // purple for anything other than 200 or 201
- NSString *flag = status >= 400 ? @" ⚑" : @""; // 2691 = ⚑
- NSString *explanation = [NSHTTPURLResponse localizedStringForStatusCode:status];
- NSString *const statusFormat = @"%ld %@ %@";
- statusString = [NSString stringWithFormat:statusFormat, (long)status, explanation, flag];
- }
- }
- // show the response URL only if it's different from the request URL
- NSString *responseURLStr = @"";
- NSURL *responseURL = response.URL;
-
- if (responseURL && ![responseURL isEqual:request.URL]) {
- NSString *const responseURLFormat =
- @"response URL: %@
\n";
- responseURLStr = [NSString stringWithFormat:responseURLFormat, [responseURL absoluteString]];
- }
- [outputHTML appendFormat:@"response: status %@
\n%@",
- statusString, responseURLStr];
- // Write the response headers
- NSUInteger numberOfResponseHeaders = responseHeaders.count;
- if (numberOfResponseHeaders > 0) {
- // Indicate if the server is setting cookies
- NSString *cookiesSet = [responseHeaders valueForKey:@"Set-Cookie"];
- NSString *cookiesStr =
- cookiesSet ? @" sets cookies" : @"";
- // Indicate if the server is redirecting
- NSString *location = [responseHeaders valueForKey:@"Location"];
- BOOL isRedirect = status >= 300 && status <= 399 && location != nil;
- NSString *redirectsStr =
- isRedirect ? @" redirects" : @"";
- [outputHTML appendFormat:@" headers: %d %@ %@
\n",
- (int)numberOfResponseHeaders, cookiesStr, redirectsStr];
- } else {
- [outputHTML appendString:@" headers: none
\n"];
- }
- }
- // error
- if (error) {
- [outputHTML appendFormat:@"Error: %@
\n", error.description];
- }
- // Write the response data
- if (responseDataFileName) {
- if (isResponseImage) {
- // Make a small inline image that links to the full image file
- [outputHTML appendFormat:@" data: %lld bytes, %@
",
- responseDataLength, responseMIMEType];
- NSString *const fmt =
- @"
\n";
- [outputHTML appendFormat:fmt, responseDataFileName, responseDataFileName];
- } else {
- // The response data was XML; link to the xml file
- NSString *const fmt =
- @" data: %lld bytes, %@ %@\n";
- [outputHTML appendFormat:fmt, responseDataLength, responseMIMEType,
- responseDataFileName, [responseDataFileName pathExtension]];
- }
- } else {
- // The response data was not an image; just show the length and MIME type
- [outputHTML appendFormat:@" data: %lld bytes, %@\n",
- responseDataLength, responseMIMEType ? responseMIMEType : @"(no response type)"];
- }
- // Make a single string of the request and response, suitable for copying
- // to the clipboard and pasting into a bug report
- NSMutableString *copyable = [NSMutableString string];
- if (comment) {
- [copyable appendFormat:@"%@\n\n", comment];
- }
- [copyable appendFormat:@"%@ elapsed: %5.3fsec\n", now, elapsed];
- if (redirectedFromURLString) {
- [copyable appendFormat:@"Redirected from %@\n", redirectedFromURLString];
- }
- [copyable appendFormat:@"Request: %@ %@\n", requestMethod, requestURL];
- if (requestHeaders.count > 0) {
- [copyable appendFormat:@"Request headers:\n%@\n",
- [[self class] headersStringForDictionary:requestHeaders]];
- }
- if (bodyDataLength > 0) {
- [copyable appendFormat:@"Request body: (%llu bytes)\n", bodyDataLength];
- if (bodyDataStr) {
- [copyable appendFormat:@"%@\n", bodyDataStr];
- }
- [copyable appendString:@"\n"];
- }
- if (response) {
- [copyable appendFormat:@"Response: status %d\n", (int) status];
- [copyable appendFormat:@"Response headers:\n%@\n",
- [[self class] headersStringForDictionary:responseHeaders]];
- [copyable appendFormat:@"Response body: (%lld bytes)\n", responseDataLength];
- if (responseDataLength > 0) {
- NSString *logResponseBody = self.logResponseBody;
- if (logResponseBody) {
- // The user has provided the response body text.
- responseDataStr = [logResponseBody copy];
- self.logResponseBody = nil;
- }
- if (responseDataStr != nil) {
- [copyable appendFormat:@"%@\n", responseDataStr];
- } else {
- // Even though it's redundant, we'll put in text to indicate that all the bytes are binary.
- if (self.destinationFileURL) {
- [copyable appendFormat:@"<<%lld bytes>> to file %@\n",
- responseDataLength, self.destinationFileURL.path];
- } else {
- [copyable appendFormat:@"<<%lld bytes>>\n", responseDataLength];
- }
- }
- }
- }
- if (error) {
- [copyable appendFormat:@"Error: %@\n", error];
- }
- // Save to log property before adding the separator
- self.log = copyable;
-
- [copyable appendString:@"-----------------------------------------------------------\n"];
-
- // Write the copyable version to another file (linked to at the top of the html file, above)
- //
- // Ideally, something to just copy this to the clipboard like
- // Copy here."
- // would work everywhere, but it only works in Safari as of 8/2010
- if (gIsLoggingToFile) {
- NSString *parentDir = [[self class] loggingDirectory];
- NSString *copyablePath = [logDirectory stringByAppendingPathComponent:copyableFileName];
- NSError *copyableError = nil;
- if (![copyable writeToFile:copyablePath
- atomically:NO
- encoding:NSUTF8StringEncoding
- error:©ableError]) {
- // Error writing to file
- NSLog(@"%@ logging write error:%@ (%@)", [self class], copyableError, copyablePath);
- }
- [outputHTML appendString:@"
"];
-
- // Append the HTML to the main output file
- const char* htmlBytes = outputHTML.UTF8String;
- NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:htmlPath
- append:YES];
- [stream open];
- [stream write:(const uint8_t *) htmlBytes maxLength:strlen(htmlBytes)];
- [stream close];
-
- // Make a symlink to the latest html
- NSString *const symlinkNameSuffix = [[self class] symlinkNameSuffix];
- NSString *symlinkName = [processName stringByAppendingString:symlinkNameSuffix];
- NSString *symlinkPath = [parentDir stringByAppendingPathComponent:symlinkName];
-
- [fileMgr removeItemAtPath:symlinkPath error:NULL];
- [fileMgr createSymbolicLinkAtPath:symlinkPath
- withDestinationPath:htmlPath
- error:NULL];
-#if TARGET_OS_IPHONE
- static BOOL gReportedLoggingPath = NO;
- if (!gReportedLoggingPath) {
- gReportedLoggingPath = YES;
- NSLog(@"GTMSessionFetcher logging to \"%@\"", parentDir);
- }
-#endif
- }
-}
-
-- (NSInputStream *)loggedInputStreamForInputStream:(NSInputStream *)inputStream {
- if (!inputStream) return nil;
- if (![GTMSessionFetcher isLoggingEnabled]) return inputStream;
-
- [self clearLoggedStreamData]; // Clear any previous data.
- Class monitorClass = NSClassFromString(@"GTMReadMonitorInputStream");
- if (!monitorClass) {
- NSString const *str = @"<>";
- NSData *stringData = [str dataUsingEncoding:NSUTF8StringEncoding];
- [self appendLoggedStreamData:stringData];
- return inputStream;
- }
- inputStream = [monitorClass inputStreamWithStream:inputStream];
-
- GTMReadMonitorInputStream *readMonitorInputStream = (GTMReadMonitorInputStream *)inputStream;
- [readMonitorInputStream setReadDelegate:self];
- SEL readSel = @selector(inputStream:readIntoBuffer:length:);
- [readMonitorInputStream setReadSelector:readSel];
-
- return inputStream;
-}
-
-- (GTMSessionFetcherBodyStreamProvider)loggedStreamProviderForStreamProvider:
- (GTMSessionFetcherBodyStreamProvider)streamProvider {
- if (!streamProvider) return nil;
- if (![GTMSessionFetcher isLoggingEnabled]) return streamProvider;
-
- [self clearLoggedStreamData]; // Clear any previous data.
- Class monitorClass = NSClassFromString(@"GTMReadMonitorInputStream");
- if (!monitorClass) {
- NSString const *str = @"<>";
- NSData *stringData = [str dataUsingEncoding:NSUTF8StringEncoding];
- [self appendLoggedStreamData:stringData];
- return streamProvider;
- }
- GTMSessionFetcherBodyStreamProvider loggedStreamProvider =
- ^(GTMSessionFetcherBodyStreamProviderResponse response) {
- streamProvider(^(NSInputStream *bodyStream) {
- bodyStream = [self loggedInputStreamForInputStream:bodyStream];
- response(bodyStream);
- });
- };
- return loggedStreamProvider;
-}
-
-@end
-
-@implementation GTMSessionFetcher (GTMSessionFetcherLoggingUtilities)
-
-- (void)inputStream:(GTMReadMonitorInputStream *)stream
- readIntoBuffer:(void *)buffer
- length:(int64_t)length {
- // append the captured data
- NSData *data = [NSData dataWithBytesNoCopy:buffer
- length:(NSUInteger)length
- freeWhenDone:NO];
- [self appendLoggedStreamData:data];
-}
-
-#pragma mark Fomatting Utilities
-
-+ (NSString *)snipSubstringOfString:(NSString *)originalStr
- betweenStartString:(NSString *)startStr
- endString:(NSString *)endStr {
-#if SKIP_GTM_FETCH_LOGGING_SNIPPING
- return originalStr;
-#else
- if (!originalStr) return nil;
-
- // Find the start string, and replace everything between it
- // and the end string (or the end of the original string) with "_snip_"
- NSRange startRange = [originalStr rangeOfString:startStr];
- if (startRange.location == NSNotFound) return originalStr;
-
- // We found the start string
- NSUInteger originalLength = originalStr.length;
- NSUInteger startOfTarget = NSMaxRange(startRange);
- NSRange targetAndRest = NSMakeRange(startOfTarget, originalLength - startOfTarget);
- NSRange endRange = [originalStr rangeOfString:endStr
- options:0
- range:targetAndRest];
- NSRange replaceRange;
- if (endRange.location == NSNotFound) {
- // Found no end marker so replace to end of string
- replaceRange = targetAndRest;
- } else {
- // Replace up to the endStr
- replaceRange = NSMakeRange(startOfTarget, endRange.location - startOfTarget);
- }
- NSString *result = [originalStr stringByReplacingCharactersInRange:replaceRange
- withString:@"_snip_"];
- return result;
-#endif // SKIP_GTM_FETCH_LOGGING_SNIPPING
-}
-
-+ (NSString *)headersStringForDictionary:(NSDictionary *)dict {
- // Format the dictionary in http header style, like
- // Accept: application/json
- // Cache-Control: no-cache
- // Content-Type: application/json; charset=utf-8
- //
- // Pad the key names, but not beyond 16 chars, since long custom header
- // keys just create too much whitespace
- NSArray *keys = [dict.allKeys sortedArrayUsingSelector:@selector(compare:)];
-
- NSMutableString *str = [NSMutableString string];
- for (NSString *key in keys) {
- NSString *value = [dict valueForKey:key];
- if ([key isEqual:@"Authorization"]) {
- // Remove OAuth 1 token
- value = [[self class] snipSubstringOfString:value
- betweenStartString:@"oauth_token=\""
- endString:@"\""];
-
- // Remove OAuth 2 bearer token (draft 16, and older form)
- value = [[self class] snipSubstringOfString:value
- betweenStartString:@"Bearer "
- endString:@"\n"];
- value = [[self class] snipSubstringOfString:value
- betweenStartString:@"OAuth "
- endString:@"\n"];
-
- // Remove Google ClientLogin
- value = [[self class] snipSubstringOfString:value
- betweenStartString:@"GoogleLogin auth="
- endString:@"\n"];
- }
- [str appendFormat:@" %@: %@\n", key, value];
- }
- return str;
-}
-
-@end
-
-#endif // !STRIP_GTM_FETCH_LOGGING
diff --git a/Old My Mind/Pods/GTMSessionFetcher/Source/GTMSessionFetcherService.h b/Old My Mind/Pods/GTMSessionFetcher/Source/GTMSessionFetcherService.h
deleted file mode 100755
index a696ac7..0000000
--- a/Old My Mind/Pods/GTMSessionFetcher/Source/GTMSessionFetcherService.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/* Copyright 2014 Google Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// For best performance and convenient usage, fetchers should be generated by a common
-// GTMSessionFetcherService instance, like
-//
-// _fetcherService = [[GTMSessionFetcherService alloc] init];
-// GTMSessionFetcher* myFirstFetcher = [_fetcherService fetcherWithRequest:request1];
-// GTMSessionFetcher* mySecondFetcher = [_fetcherService fetcherWithRequest:request2];
-
-#import "GTMSessionFetcher.h"
-
-GTM_ASSUME_NONNULL_BEGIN
-
-// Notifications.
-
-// This notification indicates a reusable session has become invalid. It is intended mainly for the
-// service's unit tests.
-//
-// The notification object is the fetcher service.
-// The invalid session is provided via the userInfo kGTMSessionFetcherServiceSessionKey key.
-extern NSString *const kGTMSessionFetcherServiceSessionBecameInvalidNotification;
-extern NSString *const kGTMSessionFetcherServiceSessionKey;
-
-@interface GTMSessionFetcherService : NSObject
-
-// Queues of delayed and running fetchers. Each dictionary contains arrays
-// of GTMSessionFetcher *fetchers, keyed by NSString *host
-@property(atomic, strong, readonly, GTM_NULLABLE) GTM_NSDictionaryOf(NSString *, NSArray *) *delayedFetchersByHost;
-@property(atomic, strong, readonly, GTM_NULLABLE) GTM_NSDictionaryOf(NSString *, NSArray *) *runningFetchersByHost;
-
-// A max value of 0 means no fetchers should be delayed.
-// The default limit is 10 simultaneous fetchers targeting each host.
-// This does not apply to fetchers whose useBackgroundSession property is YES. Since services are
-// not resurrected on an app relaunch, delayed fetchers would effectively be abandoned.
-@property(atomic, assign) NSUInteger maxRunningFetchersPerHost;
-
-// Properties to be applied to each fetcher; see GTMSessionFetcher.h for descriptions
-@property(atomic, strong, GTM_NULLABLE) NSURLSessionConfiguration *configuration;
-@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherConfigurationBlock configurationBlock;
-@property(atomic, strong, GTM_NULLABLE) NSHTTPCookieStorage *cookieStorage;
-@property(atomic, strong, GTM_NULL_RESETTABLE) dispatch_queue_t callbackQueue;
-@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherChallengeBlock challengeBlock;
-@property(atomic, strong, GTM_NULLABLE) NSURLCredential *credential;
-@property(atomic, strong) NSURLCredential *proxyCredential;
-@property(atomic, copy, GTM_NULLABLE) GTM_NSArrayOf(NSString *) *allowedInsecureSchemes;
-@property(atomic, assign) BOOL allowLocalhostRequest;
-@property(atomic, assign) BOOL allowInvalidServerCertificates;
-@property(atomic, assign, getter=isRetryEnabled) BOOL retryEnabled;
-@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherRetryBlock retryBlock;
-@property(atomic, assign) NSTimeInterval maxRetryInterval;
-@property(atomic, assign) NSTimeInterval minRetryInterval;
-@property(atomic, copy, GTM_NULLABLE) GTM_NSDictionaryOf(NSString *, id) *properties;
-
-#if GTM_BACKGROUND_TASK_FETCHING
-@property(atomic, assign) BOOL skipBackgroundTask;
-#endif
-
-// A default useragent of GTMFetcherStandardUserAgentString(nil) will be given to each fetcher
-// created by this service unless the request already has a user-agent header set.
-// This default will be added starting with builds with the SDKs for OS X 10.11 and iOS 9.
-//
-// To use the configuration's default user agent, set this property to nil.
-@property(atomic, copy, GTM_NULLABLE) NSString *userAgent;
-
-// The authorizer to attach to the created fetchers. If a specific fetcher should
-// not authorize its requests, the fetcher's authorizer property may be set to nil
-// before the fetch begins.
-@property(atomic, strong, GTM_NULLABLE) id authorizer;
-
-// Delegate queue used by the session when calling back to the fetcher. The default
-// is the main queue. Changing this does not affect the queue used to call back to the
-// application; that is specified by the callbackQueue property above.
-@property(atomic, strong, GTM_NULL_RESETTABLE) NSOperationQueue *sessionDelegateQueue;
-
-// When enabled, indicates the same session should be used by subsequent fetchers.
-//
-// This is enabled by default.
-@property(atomic, assign) BOOL reuseSession;
-
-// Sets the delay until an unused session is invalidated.
-// The default interval is 60 seconds.
-//
-// If the interval is set to 0, then any reused session is not invalidated except by
-// explicitly invoking -resetSession. Be aware that setting the interval to 0 thus
-// causes the session's delegate to be retained until the session is explicitly reset.
-@property(atomic, assign) NSTimeInterval unusedSessionTimeout;
-
-// If shouldReuseSession is enabled, this will force creation of a new session when future
-// fetchers begin.
-- (void)resetSession;
-
-// Create a fetcher
-//
-// These methods will return a fetcher. If successfully created, the connection
-// will hold a strong reference to it for the life of the connection as well.
-// So the caller doesn't have to hold onto the fetcher explicitly unless they
-// want to be able to monitor or cancel it.
-- (GTMSessionFetcher *)fetcherWithRequest:(NSURLRequest *)request;
-- (GTMSessionFetcher *)fetcherWithURL:(NSURL *)requestURL;
-- (GTMSessionFetcher *)fetcherWithURLString:(NSString *)requestURLString;
-
-// Common method for fetcher creation.
-//
-// -fetcherWithRequest:fetcherClass: may be overridden to customize creation of
-// fetchers. This is the ONLY method in the GTMSessionFetcher library intended to
-// be overridden.
-- (id)fetcherWithRequest:(NSURLRequest *)request
- fetcherClass:(Class)fetcherClass;
-
-- (BOOL)isDelayingFetcher:(GTMSessionFetcher *)fetcher;
-
-- (NSUInteger)numberOfFetchers; // running + delayed fetchers
-- (NSUInteger)numberOfRunningFetchers;
-- (NSUInteger)numberOfDelayedFetchers;
-
-// Return a list of all running or delayed fetchers. This includes fetchers created
-// by the service which have been started and have not yet stopped.
-//
-// Returns an array of fetcher objects, or nil if none.
-- (GTM_NULLABLE GTM_NSArrayOf(GTMSessionFetcher *) *)issuedFetchers;
-
-// Search for running or delayed fetchers with the specified URL.
-//
-// Returns an array of fetcher objects found, or nil if none found.
-- (GTM_NULLABLE GTM_NSArrayOf(GTMSessionFetcher *) *)issuedFetchersWithRequestURL:(NSURL *)requestURL;
-
-- (void)stopAllFetchers;
-
-// Methods for use by the fetcher class only.
-- (GTM_NULLABLE NSURLSession *)session;
-- (GTM_NULLABLE NSURLSession *)sessionForFetcherCreation;
-- (GTM_NULLABLE id)sessionDelegate;
-- (GTM_NULLABLE NSDate *)stoppedAllFetchersDate;
-
-// The testBlock can inspect its fetcher parameter's request property to
-// determine which fetcher is being faked.
-@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherTestBlock testBlock;
-
-@end
-
-@interface GTMSessionFetcherService (TestingSupport)
-
-// Convenience method to create a fetcher service for testing.
-//
-// Fetchers generated by this mock fetcher service will not perform any
-// network operation, but will invoke callbacks and provide the supplied data
-// or error to the completion handler.
-//
-// You can make more customized mocks by setting the test block property of the service
-// or fetcher; the test block can inspect the fetcher's request or other properties.
-//
-// See the description of the testBlock property below.
-+ (instancetype)mockFetcherServiceWithFakedData:(GTM_NULLABLE NSData *)fakedDataOrNil
- fakedError:(GTM_NULLABLE NSError *)fakedErrorOrNil;
-
-// Spin the run loop and discard events (or, if not on the main thread, just sleep the thread)
-// until all running and delayed fetchers have completed.
-//
-// This is only for use in testing or in tools without a user interface.
-//
-// Synchronous fetches should never be done by shipping apps; they are
-// sufficient reason for rejection from the app store.
-//
-// Returns NO if timed out.
-- (BOOL)waitForCompletionOfAllFetchersWithTimeout:(NSTimeInterval)timeoutInSeconds;
-
-@end
-
-@interface GTMSessionFetcherService (BackwardsCompatibilityOnly)
-
-// Clients using GTMSessionFetcher should set the cookie storage explicitly themselves.
-// This method is just for compatibility with the old fetcher.
-@property(atomic, assign) NSInteger cookieStorageMethod;
-
-@end
-
-GTM_ASSUME_NONNULL_END
diff --git a/Old My Mind/Pods/GTMSessionFetcher/Source/GTMSessionFetcherService.m b/Old My Mind/Pods/GTMSessionFetcher/Source/GTMSessionFetcherService.m
deleted file mode 100755
index fc6e6d9..0000000
--- a/Old My Mind/Pods/GTMSessionFetcher/Source/GTMSessionFetcherService.m
+++ /dev/null
@@ -1,1352 +0,0 @@
-/* Copyright 2014 Google Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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;
-@end
-#endif // !GTMSESSION_BUILD_COMBINED_SOURCES
-
-@interface GTMSessionFetcherService ()
-
-@property(atomic, strong, readwrite) NSDictionary *delayedFetchersByHost;
-@property(atomic, strong, readwrite) NSDictionary *runningFetchersByHost;
-
-@end
-
-// Since NSURLSession doesn't support a separate delegate per task (!), instances of this
-// class serve as a session delegate trampoline.
-//
-// This class maps a session's tasks to fetchers, and resends delegate messages to the task's
-// fetcher.
-@interface GTMSessionFetcherSessionDelegateDispatcher : NSObject
-
-// The session for the tasks in this dispatcher's task-to-fetcher map.
-@property(atomic) NSURLSession *session;
-
-// The timer interval for invalidating a session that has no active tasks.
-@property(atomic) NSTimeInterval discardInterval;
-
-// The current discard timer.
-@property(atomic, readonly) NSTimer *discardTimer;
-
-
-- (instancetype)initWithParentService:(GTMSessionFetcherService *)parentService
- sessionDiscardInterval:(NSTimeInterval)discardInterval;
-
-- (void)setFetcher:(GTMSessionFetcher *)fetcher
- forTask:(NSURLSessionTask *)task;
-- (void)removeFetcher:(GTMSessionFetcher *)fetcher;
-
-// Before using a session, tells the delegate dispatcher to stop the discard timer.
-- (void)startSessionUsage;
-
-// When abandoning a delegate dispatcher, we want to avoid the session retaining
-// the delegate after tasks complete.
-- (void)abandon;
-
-@end
-
-
-@implementation GTMSessionFetcherService {
- NSMutableDictionary *_delayedFetchersByHost;
- NSMutableDictionary *_runningFetchersByHost;
- NSUInteger _maxRunningFetchersPerHost;
-
- // When this ivar is nil, the service will not reuse sessions.
- GTMSessionFetcherSessionDelegateDispatcher *_delegateDispatcher;
-
- // Fetchers will wait on this if another fetcher is creating the shared NSURLSession.
- dispatch_semaphore_t _sessionCreationSemaphore;
-
- dispatch_queue_t _callbackQueue;
- NSOperationQueue *_delegateQueue;
- NSHTTPCookieStorage *_cookieStorage;
- NSString *_userAgent;
- NSTimeInterval _timeout;
-
- NSURLCredential *_credential; // Username & password.
- NSURLCredential *_proxyCredential; // Credential supplied to proxy servers.
-
- NSInteger _cookieStorageMethod;
-
- id _authorizer;
-
- // For waitForCompletionOfAllFetchersWithTimeout: we need to wait on stopped fetchers since
- // they've not yet finished invoking their queued callbacks. This array is nil except when
- // waiting on fetchers.
- NSMutableArray *_stoppedFetchersToWaitFor;
-
- // For fetchers that enqueued their callbacks before stopAllFetchers was called on the service,
- // set a barrier so the callbacks know to bail out.
- 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,
- properties = _properties,
- unusedSessionTimeout = _unusedSessionTimeout,
- 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);
-
- // Starting with the SDKs for OS X 10.11/iOS 9, the service has a default useragent.
- // Apps can remove this and get the default system "CFNetwork" useragent by setting the
- // fetcher service's userAgent property to nil.
-#if (!TARGET_OS_IPHONE && defined(MAC_OS_X_VERSION_10_11) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_11) \
- || (TARGET_OS_IPHONE && defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0)
- _userAgent = GTMFetcherStandardUserAgentString(nil);
-#endif
- }
- return self;
-}
-
-- (void)dealloc {
- [self detachAuthorizer];
- [_delegateDispatcher abandon];
-}
-
-#pragma mark Generate a new fetcher
-
-// Clients may override this method. Clients should not override any other library methods.
-- (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;
- fetcher.properties = self.properties;
- fetcher.service = self;
- if (self.cookieStorageMethod >= 0) {
- [fetcher setCookieStorageMethod:self.cookieStorageMethod];
- }
-
-#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];
-}
-
-// Returns a session for the fetcher's host, or nil.
-- (NSURLSession *)session {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- NSURLSession *session = _delegateDispatcher.session;
- return session;
- }
-}
-
-// Returns a session for the fetcher's host, or nil. For shared sessions, this
-// waits on a semaphore, blocking other fetchers while the caller creates the
-// session if needed.
-- (NSURLSession *)sessionForFetcherCreation {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
- if (!_delegateDispatcher) {
- // This fetcher is creating a non-shared session, so skip the semaphore usage.
- return nil;
- }
- }
-
- // Wait if another fetcher is currently creating a session; avoid waiting
- // inside the @synchronized block, as that can deadlock.
- dispatch_semaphore_wait(_sessionCreationSemaphore, DISPATCH_TIME_FOREVER);
-
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- // Before getting the NSURLSession for task creation, it is
- // important to invalidate and nil out the session discard timer; otherwise
- // the session can be invalidated between when it is returned to the
- // fetcher, and when the fetcher attempts to create its NSURLSessionTask.
- [_delegateDispatcher startSessionUsage];
-
- NSURLSession *session = _delegateDispatcher.session;
- if (session) {
- // The calling fetcher will receive a preexisting session, so
- // we can allow other fetchers to create a session.
- dispatch_semaphore_signal(_sessionCreationSemaphore);
- } else {
- // No existing session was obtained, so the calling fetcher will create the session;
- // it *must* invoke fetcherDidCreateSession: to signal the dispatcher's semaphore after
- // the session has been created (or fails to be created) to avoid a hang.
- }
- return session;
- }
-}
-
-- (id)sessionDelegate {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _delegateDispatcher;
- }
-}
-
-#pragma mark Queue Management
-
-- (void)addRunningFetcher:(GTMSessionFetcher *)fetcher
- forHost:(NSString *)host {
- // Add to the array of running fetchers for this host, creating the array if needed.
- 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 {
- // Add to the array of delayed fetchers for this host, creating the array if needed.
- 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 {
- // Entry point from the fetcher
- NSURL *requestURL = fetcher.request.URL;
- NSString *host = requestURL.host;
-
- // Addresses "file:///path" case where localhost is the implicit host.
- if (host.length == 0 && [requestURL isFileURL]) {
- host = @"localhost";
- }
-
- if (host.length == 0) {
- // Data URIs legitimately have no host, reject other hostless URLs.
- 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;
- }
- } // @synchronized(self)
-
- // We'll save the host that serves as the key for this fetcher's array
- // to avoid any chance of the underlying request changing, stranding
- // the fetcher in the wrong array
- fetcher.serviceHost = host;
-
- return shouldBeginResult;
-}
-
-- (void)startFetcher:(GTMSessionFetcher *)fetcher {
- [fetcher beginFetchMayDelay:NO
- mayAuthorize:YES];
-}
-
-// Internal utility. Returns a fetcher's delegate if it's a dispatcher, or nil if the fetcher
-// is its own delegate and has no dispatcher.
-- (GTMSessionFetcherSessionDelegateDispatcher *)delegateDispatcherForFetcher:(GTMSessionFetcher *)fetcher {
- GTMSessionCheckNotSynchronized(self);
-
- NSURLSession *fetcherSession = fetcher.session;
- if (fetcherSession) {
- id fetcherDelegate = fetcherSession.delegate;
- BOOL hasDispatcher = (fetcherDelegate != nil && fetcherDelegate != fetcher);
- 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);
-
- // Save this fetcher's session.
- delegateDispatcher.session = fetcherSession;
-
- // Allow other fetchers to request this session now.
- dispatch_semaphore_signal(_sessionCreationSemaphore);
- }
- }
-}
-
-- (void)fetcherDidBeginFetching:(GTMSessionFetcher *)fetcher {
- // If this fetcher has a separate delegate with a shared session, then
- // this fetcher should be added to the delegate's map of tasks to fetchers.
- GTMSessionFetcherSessionDelegateDispatcher *delegateDispatcher =
- [self delegateDispatcherForFetcher:fetcher];
- if (delegateDispatcher) {
- GTMSESSION_ASSERT_DEBUG(fetcher.canShareSession,
- @"Inappropriate shared session: %@", fetcher);
-
- // There should already be a session, from this or a previous fetcher.
- //
- // Sanity check that the fetcher's session is the delegate's shared session.
- 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 {
- // Entry point from the fetcher
- NSString *host = fetcher.serviceHost;
- if (!host) {
- // fetcher has been stopped previously
- return;
- }
-
- // This removeFetcher: invocation is a fallback; typically, fetchers are removed from the task
- // map when the task completes.
- GTMSessionFetcherSessionDelegateDispatcher *delegateDispatcher =
- [self delegateDispatcherForFetcher:fetcher];
- [delegateDispatcher removeFetcher:fetcher];
-
- NSMutableArray *fetchersToStart;
-
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- // If a test is waiting for all fetchers to stop, it needs to wait for this one
- // to invoke its callbacks on the callback queue.
- [_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) {
- // Start another delayed fetcher running, scanning for the minimum
- // priority value, defaulting to FIFO for equal priorities
- 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) {
- // None left; remove the empty array
- [_runningFetchersByHost removeObjectForKey:host];
- }
-
- if (delayedForHost.count == 0) {
- [_delayedFetchersByHost removeObjectForKey:host];
- }
- } // @synchronized(self)
-
- // Start fetchers outside of the synchronized block to avoid a deadlock.
- for (GTMSessionFetcher *nextFetcher in fetchersToStart) {
- [self startFetcher:nextFetcher];
- }
-
- // The fetcher is no longer in the running or the delayed array,
- // so remove its host and thread properties
- 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);
-
- // Set the time barrier so fetchers know not to call back even if
- // the stop calls below occur after the fetchers naturally
- // stopped and so were removed from _runningFetchersByHost,
- // but while the callbacks were already enqueued before stopAllFetchers
- // was invoked.
- _stoppedAllFetchersDate = [[NSDate alloc] init];
-
- // Remove fetchers from the delayed list to avoid fetcherDidStop: from
- // starting more fetchers running as a side effect of stopping one
- 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);
-
- // The old dispatchers may be retained as delegates of any ongoing sessions by those sessions.
- 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) {
- // If the delegate dispatcher's current discardTimer is the same object as the timer
- // that fired, no fetcher has recently attempted to start using the session by calling
- // startSessionUsage, which invalidates and nils out the timer.
- [self resetSessionInternal];
- } else {
- // A fetcher has invalidated the timer between its triggering and now, potentially
- // meaning a fetcher has requested access to the NSURLSession, and may be in the process
- // of starting a new task. The dispatcher should not be abandoned, as this can lead
- // to a race condition between calling -finishTasksAndInvalidate on the NSURLSession
- // and the fetcher attempting to create a new task.
- }
- }
-
- 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;
- }
-}
-
-// This method should be called inside of @synchronized(self)
-- (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)authorizer {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _authorizer;
- }
-}
-
-- (void)setAuthorizer:(id)obj {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- if (obj != _authorizer) {
- [self detachAuthorizer];
- }
-
- _authorizer = obj;
- }
-
- // Use the fetcher service for the authorization fetches if the auth
- // object supports fetcher services
- if ([obj respondsToSelector:@selector(setFetcherService:)]) {
-#if GTM_USE_SESSION_FETCHER
- [obj setFetcherService:self];
-#else
- [obj setFetcherService:(id)self];
-#endif
- }
-}
-
-// This should be called inside a @synchronized(self) block except during dealloc.
-- (void)detachAuthorizer {
- // This method is called by the fetcher service's dealloc and setAuthorizer:
- // methods; do not override.
- //
- // The fetcher service retains the authorizer, and the authorizer has a
- // weak pointer to the fetcher service (a non-zeroing pointer for
- // compatibility with iOS 4 and Mac OS X 10.5/10.6.)
- //
- // When this fetcher service no longer uses the authorizer, we want to remove
- // the authorizer's dependence on the fetcher service. Authorizers can still
- // function without a fetcher service.
- if ([_authorizer respondsToSelector:@selector(fetcherService)]) {
- id authFetcherService = [_authorizer fetcherService];
- if (authFetcherService == self) {
- [_authorizer setFetcherService:nil];
- }
- }
-}
-
-- (dispatch_queue_t GTM_NONNULL_TYPE)callbackQueue {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _callbackQueue;
- } // @synchronized(self)
-}
-
-- (void)setCallbackQueue:(dispatch_queue_t GTM_NULLABLE_TYPE)queue {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- _callbackQueue = queue ?: dispatch_get_main_queue();
- } // @synchronized(self)
-}
-
-- (NSOperationQueue * GTM_NONNULL_TYPE)sessionDelegateQueue {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _delegateQueue;
- } // @synchronized(self)
-}
-
-- (void)setSessionDelegateQueue:(NSOperationQueue * GTM_NULLABLE_TYPE)queue {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- _delegateQueue = queue ?: [NSOperationQueue mainQueue];
- } // @synchronized(self)
-}
-
-- (NSOperationQueue *)delegateQueue {
- // Provided for compatibility with the old fetcher service. The gtm-oauth2 code respects
- // any custom delegate queue for calling the app.
- 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];
- 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 // GTM_DISABLE_FETCHER_TEST_BLOCK
-}
-
-#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;
-
- // The task map maps NSURLSessionTasks to GTMSessionFetchers
- NSMutableDictionary *_taskToFetcherMap;
- // The discard timer will invalidate sessions after the session's last task completes.
- 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 ?: @"",
- _taskToFetcherMap.count > 0 ? _taskToFetcherMap : @""];
-}
-
-- (NSTimer *)discardTimer {
- GTMSessionCheckNotSynchronized(self);
- @synchronized(self) {
- return _discardTimer;
- }
-}
-
-// This method should be called inside of a @synchronized(self) block.
-- (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];
- }
-}
-
-// This method should be called inside of a @synchronized(self) block.
-- (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;
- }
- }
-
- // Inform the service that the discard timer has fired, and should check whether the
- // service can abandon us. -resetSession cannot be called directly, as there is a
- // race condition that must be guarded against with the NSURLSession being returned
- // from sessionForFetcherCreation outside other locks. The service can take steps
- // to prevent resetting the session if that has occurred.
- //
- // The service must be called from outside the @synchronized block.
- [service resetSessionForDispatcherDiscardTimer:timer];
-}
-
-- (void)abandon {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- [self destroySessionAndTimer];
- }
-}
-
-- (void)startSessionUsage {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- [self destroyDiscardTimer];
- }
-}
-
-// This method should be called inside of a @synchronized(self) block.
-- (void)destroySessionAndTimer {
- GTMSessionCheckSynchronized(self);
- [self destroyDiscardTimer];
-
- // Break any retain cycle from the session holding the delegate.
- [_session finishTasksAndInvalidate];
-
- // Immediately clear the session so no new task may be issued with it.
- //
- // The _taskToFetcherMap needs to stay valid until the outstanding tasks finish.
- _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);
-
- // Typically, a fetcher should be removed when its task invokes
- // URLSession:task:didCompleteWithError:.
- //
- // When fetching with a testBlock, though, the task completed delegate
- // method may not be invoked, requiring cleanup here.
- 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];
- }
- }
-}
-
-// This helper method provides synchronized access to the task map for the delegate
-// methods below.
-- (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;
- }
-}
-
-// NSURLSessionDelegate protocol methods.
-
-// - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session;
-//
-// TODO(seh): How do we route this to an appropriate fetcher?
-
-
-- (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];
- }
-
- // Any "suspended" tasks may not have received callbacks from NSURLSession when the session
- // completes; we'll call them now.
- [localTaskToFetcherMap enumerateKeysAndObjectsUsingBlock:^(NSURLSessionTask *task,
- GTMSessionFetcher *fetcher,
- BOOL *stop) {
- if (fetcher.session == session) {
- // Our delegate method URLSession:task:didCompleteWithError: will rely on
- // _taskToFetcherMap so that should still contain this fetcher.
- 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);
- }
- }];
-
- // Our tests rely on this notification to know the session discard timer fired.
- NSDictionary *userInfo = @{ kGTMSessionFetcherServiceSessionKey : session };
- NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
- [nc postNotificationName:kGTMSessionFetcherServiceSessionBecameInvalidNotification
- object:_parentService
- userInfo:userInfo];
-}
-
-
-#pragma mark - NSURLSessionTaskDelegate
-
-// NSURLSessionTaskDelegate protocol methods.
-//
-// We won't test here if the fetcher responds to these since we only want this
-// class to implement the same delegate methods the fetcher does (so NSURLSession's
-// tests for respondsToSelector: will have the same result whether the session
-// delegate is the fetcher or this dispatcher.)
-
-- (void)URLSession:(NSURLSession *)session
- task:(NSURLSessionTask *)task
-willPerformHTTPRedirection:(NSHTTPURLResponse *)response
- newRequest:(NSURLRequest *)request
- completionHandler:(void (^)(NSURLRequest *))completionHandler {
- id 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 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 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 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 fetcher = [self fetcherForTask:task];
-
- // This is the usual way tasks are removed from the task map.
- [self removeTaskFromMap:task];
-
- [fetcher URLSession:session
- task:task
- didCompleteWithError:error];
-}
-
-// NSURLSessionDataDelegate protocol methods.
-
-- (void)URLSession:(NSURLSession *)session
- dataTask:(NSURLSessionDataTask *)dataTask
-didReceiveResponse:(NSURLResponse *)response
- completionHandler:(void (^)(NSURLSessionResponseDisposition))handler {
- id fetcher = [self fetcherForTask:dataTask];
- [fetcher URLSession:session
- dataTask:dataTask
- didReceiveResponse:response
- completionHandler:handler];
-}
-
-- (void)URLSession:(NSURLSession *)session
- dataTask:(NSURLSessionDataTask *)dataTask
-didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask {
- id 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 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 fetcher = [self fetcherForTask:dataTask];
- [fetcher URLSession:session
- dataTask:dataTask
- willCacheResponse:proposedResponse
- completionHandler:handler];
-}
-
-// NSURLSessionDownloadDelegate protocol methods.
-
-- (void)URLSession:(NSURLSession *)session
- downloadTask:(NSURLSessionDownloadTask *)downloadTask
-didFinishDownloadingToURL:(NSURL *)location {
- id 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 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 fetcher = [self fetcherForTask:downloadTask];
- [fetcher URLSession:session
- downloadTask:downloadTask
- didResumeAtOffset:fileOffset
- expectedTotalBytes:expectedTotalBytes];
-}
-
-@end
diff --git a/Old My Mind/Pods/GTMSessionFetcher/Source/GTMSessionUploadFetcher.h b/Old My Mind/Pods/GTMSessionFetcher/Source/GTMSessionUploadFetcher.h
deleted file mode 100755
index 51372f2..0000000
--- a/Old My Mind/Pods/GTMSessionFetcher/Source/GTMSessionUploadFetcher.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/* Copyright 2014 Google Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// GTMSessionUploadFetcher implements Google's resumable upload protocol.
-
-//
-// This subclass of GTMSessionFetcher simulates the series of fetches
-// needed for chunked upload as a single fetch operation.
-//
-// Protocol document: TBD
-//
-// To the client, the only fetcher that exists is this class; the subsidiary
-// fetchers needed for uploading chunks are not visible (though the most recent
-// chunk fetcher may be accessed via the -activeFetcher or -chunkFetcher methods, and
-// -responseHeaders and -statusCode reflect results from the most recent chunk
-// fetcher.)
-//
-// Chunk fetchers are discarded as soon as they have completed.
-//
-
-// Note: Unlike the fetcher superclass, the methods of GTMSessionUploadFetcher should
-// only be used from the main thread until further work is done to make this subclass
-// thread-safe.
-
-#import "GTMSessionFetcher.h"
-#import "GTMSessionFetcherService.h"
-
-GTM_ASSUME_NONNULL_BEGIN
-
-// Unless an application knows it needs a smaller chunk size, it should use the standard
-// chunk size, which sends the entire file as a single chunk to minimize upload overhead.
-extern int64_t const kGTMSessionUploadFetcherStandardChunkSize;
-
-// When uploading requires data buffer allocations (such as uploading from an NSData or
-// an NSFileHandle) this is the maximum buffer size that will be created by the fetcher.
-extern int64_t const kGTMSessionUploadFetcherMaximumDemandBufferSize;
-
-// Notification that the upload location URL was provided by the server.
-extern NSString *const kGTMSessionFetcherUploadLocationObtainedNotification;
-
-// Block to provide data during uploads.
-//
-// Response data may be allocated with dataWithBytesNoCopy:length:freeWhenDone: for efficiency,
-// and released after the response block returns.
-//
-// Pass nil as the data (and optionally an NSError) for a failure.
-typedef void (^GTMSessionUploadFetcherDataProviderResponse)(NSData * GTM_NULLABLE_TYPE data,
- NSError * GTM_NULLABLE_TYPE error);
-typedef void (^GTMSessionUploadFetcherDataProvider)(int64_t offset, int64_t length,
- GTMSessionUploadFetcherDataProviderResponse response);
-
-@interface GTMSessionUploadFetcher : GTMSessionFetcher
-
-// Create an upload fetcher specifying either the request or the resume location URL,
-// then set an upload data source using one of these:
-//
-// setUploadFileURL:
-// setUploadDataLength:provider:
-// setUploadFileHandle:
-// setUploadData:
-
-+ (instancetype)uploadFetcherWithRequest:(NSURLRequest *)request
- uploadMIMEType:(NSString *)uploadMIMEType
- chunkSize:(int64_t)chunkSize
- fetcherService:(GTM_NULLABLE GTMSessionFetcherService *)fetcherServiceOrNil;
-
-+ (instancetype)uploadFetcherWithLocation:(NSURL * GTM_NULLABLE_TYPE)uploadLocationURL
- uploadMIMEType:(NSString *)uploadMIMEType
- chunkSize:(int64_t)chunkSize
- fetcherService:(GTM_NULLABLE GTMSessionFetcherService *)fetcherServiceOrNil;
-
-- (void)setUploadDataLength:(int64_t)fullLength
- provider:(GTM_NULLABLE GTMSessionUploadFetcherDataProvider)block;
-
-+ (NSArray *)uploadFetchersForBackgroundSessions;
-+ (GTM_NULLABLE instancetype)uploadFetcherForSessionIdentifier:(NSString *)sessionIdentifier;
-
-- (void)pauseFetching;
-- (void)resumeFetching;
-- (BOOL)isPaused;
-
-@property(atomic, strong, GTM_NULLABLE) NSURL *uploadLocationURL;
-@property(atomic, strong, GTM_NULLABLE) NSData *uploadData;
-@property(atomic, strong, GTM_NULLABLE) NSURL *uploadFileURL;
-@property(atomic, strong, GTM_NULLABLE) NSFileHandle *uploadFileHandle;
-@property(atomic, copy, readonly, GTM_NULLABLE) GTMSessionUploadFetcherDataProvider uploadDataProvider;
-@property(atomic, copy) NSString *uploadMIMEType;
-@property(atomic, assign) int64_t chunkSize;
-@property(atomic, readonly, assign) int64_t currentOffset;
-
-// The fetcher for the current data chunk, if any
-@property(atomic, strong, GTM_NULLABLE) GTMSessionFetcher *chunkFetcher;
-
-// The active fetcher is the current chunk fetcher, or the upload fetcher itself
-// if no chunk fetcher has yet been created.
-@property(atomic, readonly) GTMSessionFetcher *activeFetcher;
-
-// The last request made by an active fetcher. Useful for testing.
-@property(atomic, readonly, GTM_NULLABLE) NSURLRequest *lastChunkRequest;
-
-// The status code from the most recently-completed fetch.
-@property(atomic, assign) NSInteger statusCode;
-
-// Exposed for testing only.
-@property(atomic, readonly, GTM_NULLABLE) dispatch_queue_t delegateCallbackQueue;
-@property(atomic, readonly, GTM_NULLABLE) GTMSessionFetcherCompletionHandler delegateCompletionHandler;
-
-@end
-
-@interface GTMSessionFetcher (GTMSessionUploadFetcherMethods)
-
-@property(readonly, GTM_NULLABLE) GTMSessionUploadFetcher *parentUploadFetcher;
-
-@end
-
-GTM_ASSUME_NONNULL_END
diff --git a/Old My Mind/Pods/GTMSessionFetcher/Source/GTMSessionUploadFetcher.m b/Old My Mind/Pods/GTMSessionFetcher/Source/GTMSessionUploadFetcher.m
deleted file mode 100755
index a5ae0dc..0000000
--- a/Old My Mind/Pods/GTMSessionFetcher/Source/GTMSessionUploadFetcher.m
+++ /dev/null
@@ -1,1804 +0,0 @@
-/* Copyright 2014 Google Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-#import "GTMSessionUploadFetcher.h"
-
-static NSString *const kGTMSessionIdentifierIsUploadChunkFetcherMetadataKey = @"_upChunk";
-static NSString *const kGTMSessionIdentifierUploadFileURLMetadataKey = @"_upFileURL";
-static NSString *const kGTMSessionIdentifierUploadFileLengthMetadataKey = @"_upFileLen";
-static NSString *const kGTMSessionIdentifierUploadLocationURLMetadataKey = @"_upLocURL";
-static NSString *const kGTMSessionIdentifierUploadMIMETypeMetadataKey = @"_uploadMIME";
-static NSString *const kGTMSessionIdentifierUploadChunkSizeMetadataKey = @"_upChSize";
-static NSString *const kGTMSessionIdentifierUploadCurrentOffsetMetadataKey = @"_upOffset";
-
-static NSString *const kGTMSessionHeaderXGoogUploadChunkGranularity = @"X-Goog-Upload-Chunk-Granularity";
-static NSString *const kGTMSessionHeaderXGoogUploadCommand = @"X-Goog-Upload-Command";
-static NSString *const kGTMSessionHeaderXGoogUploadContentLength = @"X-Goog-Upload-Content-Length";
-static NSString *const kGTMSessionHeaderXGoogUploadContentType = @"X-Goog-Upload-Content-Type";
-static NSString *const kGTMSessionHeaderXGoogUploadOffset = @"X-Goog-Upload-Offset";
-static NSString *const kGTMSessionHeaderXGoogUploadProtocol = @"X-Goog-Upload-Protocol";
-static NSString *const kGTMSessionHeaderXGoogUploadSizeReceived = @"X-Goog-Upload-Size-Received";
-static NSString *const kGTMSessionHeaderXGoogUploadStatus = @"X-Goog-Upload-Status";
-static NSString *const kGTMSessionHeaderXGoogUploadURL = @"X-Goog-Upload-URL";
-
-// Property of chunk fetchers identifying the parent upload fetcher. Non-retained NSValue.
-static NSString *const kGTMSessionUploadFetcherChunkParentKey = @"_uploadFetcherChunkParent";
-
-int64_t const kGTMSessionUploadFetcherStandardChunkSize = (int64_t)LLONG_MAX;
-
-#if TARGET_OS_IPHONE
-int64_t const kGTMSessionUploadFetcherMaximumDemandBufferSize = 10 * 1024 * 1024; // 10 MB for iOS, watchOS, tvOS
-#else
-int64_t const kGTMSessionUploadFetcherMaximumDemandBufferSize = 100 * 1024 * 1024; // 100 MB for macOS
-#endif
-
-typedef NS_ENUM(NSUInteger, GTMSessionUploadFetcherStatus) {
- kStatusUnknown,
- kStatusActive,
- kStatusFinal,
- kStatusCancelled,
-};
-
-NSString *const kGTMSessionFetcherUploadLocationObtainedNotification =
- @"kGTMSessionFetcherUploadLocationObtainedNotification";
-
-#if !GTMSESSION_BUILD_COMBINED_SOURCES
-@interface GTMSessionFetcher (ProtectedMethods)
-
-// Access to non-public method on the parent fetcher class.
-- (void)stopFetchReleasingCallbacks:(BOOL)shouldReleaseCallbacks;
-- (void)createSessionIdentifierWithMetadata:(NSDictionary *)metadata;
-- (GTMSessionFetcherCompletionHandler)completionHandlerWithTarget:(id)target
- didFinishSelector:(SEL)finishedSelector;
-- (void)invokeOnCallbackQueue:(dispatch_queue_t)callbackQueue
- afterUserStopped:(BOOL)afterStopped
- block:(void (^)(void))block;
-- (NSTimer *)retryTimer;
-
-@property(readwrite, strong) NSData *downloadedData;
-- (void)releaseCallbacks;
-
-- (NSInteger)statusCodeUnsynchronized;
-
-@end
-#endif // !GTMSESSION_BUILD_COMBINED_SOURCES
-
-@interface GTMSessionUploadFetcher ()
-
-// Changing readonly to readwrite.
-@property(atomic, strong, readwrite) NSURLRequest *lastChunkRequest;
-@property(atomic, readwrite, assign) int64_t currentOffset;
-
-// Internal properties.
-@property(strong, atomic, GTM_NULLABLE) GTMSessionFetcher *fetcherInFlight; // Synchronized on self.
-
-@property(assign, atomic, getter=isSubdataGenerating) BOOL subdataGenerating;
-@property(assign, atomic) BOOL shouldInitiateOffsetQuery;
-@property(assign, atomic) int64_t uploadGranularity;
-
-@end
-
-@implementation GTMSessionUploadFetcher {
- GTMSessionFetcher *_chunkFetcher;
-
- // We'll call through to the delegate's completion handler.
- GTMSessionFetcherCompletionHandler _delegateCompletionHandler;
- dispatch_queue_t _delegateCallbackQueue;
-
- // The initial fetch's body length and bytes actually sent are
- // needed for calculating progress during subsequent chunk uploads
- int64_t _initialBodyLength;
- int64_t _initialBodySent;
-
- // The upload server address for the chunks of this upload session.
- NSURL *_uploadLocationURL;
-
- // _uploadData, _uploadDataProvider, or _uploadFileHandle may be set, but only one.
- NSData *_uploadData;
- NSFileHandle *_uploadFileHandle;
- GTMSessionUploadFetcherDataProvider _uploadDataProvider;
- NSURL *_uploadFileURL;
- int64_t _uploadFileLength;
- NSString *_uploadMIMEType;
- int64_t _chunkSize;
- int64_t _uploadGranularity;
- BOOL _isPaused;
- BOOL _isRestartedUpload;
- BOOL _shouldInitiateOffsetQuery;
-
- // Tied to useBackgroundSession property, since this property is applicable to chunk fetchers.
- BOOL _useBackgroundSessionOnChunkFetchers;
-
- // We keep the latest offset into the upload data just for progress reporting.
- int64_t _currentOffset;
-
- NSDictionary *_recentChunkReponseHeaders;
- NSInteger _recentChunkStatusCode;
-
- // For waiting, we need to know the fetcher in flight, if any, and if subdata generation
- // is in progress.
- GTMSessionFetcher *_fetcherInFlight;
- BOOL _isSubdataGenerating;
-}
-
-+ (void)load {
- [self uploadFetchersForBackgroundSessions];
-}
-
-+ (instancetype)uploadFetcherWithRequest:(NSURLRequest *)request
- uploadMIMEType:(NSString *)uploadMIMEType
- chunkSize:(int64_t)chunkSize
- fetcherService:(GTMSessionFetcherService *)fetcherService {
- GTMSessionUploadFetcher *fetcher = [self uploadFetcherWithRequest:request
- fetcherService:fetcherService];
- [fetcher setLocationURL:nil
- uploadMIMEType:uploadMIMEType
- chunkSize:chunkSize];
- return fetcher;
-}
-
-+ (instancetype)uploadFetcherWithLocation:(NSURL * GTM_NULLABLE_TYPE)uploadLocationURL
- uploadMIMEType:(NSString *)uploadMIMEType
- chunkSize:(int64_t)chunkSize
- fetcherService:(GTMSessionFetcherService *)fetcherService {
- GTMSessionUploadFetcher *fetcher = [self uploadFetcherWithRequest:nil
- fetcherService:fetcherService];
- [fetcher setLocationURL:uploadLocationURL
- uploadMIMEType:uploadMIMEType
- chunkSize:chunkSize];
- return fetcher;
-}
-
-+ (instancetype)uploadFetcherForSessionIdentifierMetadata:(NSDictionary *)metadata {
- GTMSESSION_ASSERT_DEBUG(
- [metadata[kGTMSessionIdentifierIsUploadChunkFetcherMetadataKey] boolValue],
- @"Session identifier metadata is not for an upload fetcher: %@", metadata);
-
- NSNumber *uploadFileLengthNum = metadata[kGTMSessionIdentifierUploadFileLengthMetadataKey];
- GTMSESSION_ASSERT_DEBUG(uploadFileLengthNum != nil,
- @"Session metadata missing an UploadFileSize");
- if (uploadFileLengthNum == nil) return nil;
-
- int64_t uploadFileLength = [uploadFileLengthNum longLongValue];
- GTMSESSION_ASSERT_DEBUG(uploadFileLength >= 0, @"Session metadata UploadFileSize is unknown");
-
- NSString *uploadFileURLString = metadata[kGTMSessionIdentifierUploadFileURLMetadataKey];
- GTMSESSION_ASSERT_DEBUG(uploadFileURLString, @"Session metadata missing an UploadFileURL");
- if (uploadFileURLString == nil) return nil;
-
- NSURL *uploadFileURL = [NSURL URLWithString:uploadFileURLString];
- // There used to be a call here to NSURL checkResourceIsReachableAndReturnError: to check for the
- // existence of the file (also tried NSFileManager fileExistsAtPath:). We've determined
- // empirically that the check can fail at startup even when the upload file does in fact exist.
- // For now, we'll go ahead and restore the background upload fetcher. If the file doesn't exist,
- // it will fail later.
-
- NSString *uploadLocationURLString = metadata[kGTMSessionIdentifierUploadLocationURLMetadataKey];
- NSURL *uploadLocationURL =
- uploadLocationURLString ? [NSURL URLWithString:uploadLocationURLString] : nil;
-
- NSString *uploadMIMEType =
- metadata[kGTMSessionIdentifierUploadMIMETypeMetadataKey];
- int64_t uploadChunkSize =
- [metadata[kGTMSessionIdentifierUploadChunkSizeMetadataKey] longLongValue];
- if (uploadChunkSize <= 0) {
- uploadChunkSize = kGTMSessionUploadFetcherStandardChunkSize;
- }
- int64_t currentOffset =
- [metadata[kGTMSessionIdentifierUploadCurrentOffsetMetadataKey] longLongValue];
- GTMSESSION_ASSERT_DEBUG(currentOffset <= uploadFileLength,
- @"CurrentOffset (%lld) exceeds UploadFileSize (%lld)",
- currentOffset, uploadFileLength);
- if (currentOffset > uploadFileLength) return nil;
-
- GTMSessionUploadFetcher *uploadFetcher = [self uploadFetcherWithLocation:uploadLocationURL
- uploadMIMEType:uploadMIMEType
- chunkSize:uploadChunkSize
- fetcherService:nil];
- // Set the upload file length before setting the upload file URL tries to determine the length.
- [uploadFetcher setUploadFileLength:uploadFileLength];
-
- uploadFetcher.uploadFileURL = uploadFileURL;
- uploadFetcher.sessionUserInfo = metadata;
- uploadFetcher.useBackgroundSession = YES;
- uploadFetcher.currentOffset = currentOffset;
- uploadFetcher.allowedInsecureSchemes = @[ @"http" ]; // Allowed on restored upload fetcher.
- return uploadFetcher;
-}
-
-+ (instancetype)uploadFetcherWithRequest:(NSURLRequest *)request
- fetcherService:(GTMSessionFetcherService *)fetcherService {
- // Internal utility method for instantiating fetchers
- GTMSessionUploadFetcher *fetcher;
- if ([fetcherService isKindOfClass:[GTMSessionFetcherService class]]) {
- fetcher = [fetcherService fetcherWithRequest:request
- fetcherClass:self];
- } else {
- fetcher = [self fetcherWithRequest:request];
- }
- fetcher.useBackgroundSession = YES;
- return fetcher;
-}
-
-+ (NSPointerArray *)uploadFetcherPointerArrayForBackgroundSessions {
- static NSPointerArray *gUploadFetcherPointerArrayForBackgroundSessions = nil;
-
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- gUploadFetcherPointerArrayForBackgroundSessions = [NSPointerArray weakObjectsPointerArray];
- });
- return gUploadFetcherPointerArrayForBackgroundSessions;
-}
-
-+ (instancetype)uploadFetcherForSessionIdentifier:(NSString *)sessionIdentifier {
- GTMSESSION_ASSERT_DEBUG(sessionIdentifier != nil, @"Invalid session identifier");
- NSArray *uploadFetchersForBackgroundSessions = [self uploadFetchersForBackgroundSessions];
- for (GTMSessionUploadFetcher *uploadFetcher in uploadFetchersForBackgroundSessions) {
- if ([uploadFetcher.chunkFetcher.sessionIdentifier isEqual:sessionIdentifier]) {
- return uploadFetcher;
- }
- }
- return nil;
-}
-
-+ (NSArray *)uploadFetchersForBackgroundSessions {
- // Collect the background session upload fetchers that are still in memory.
- NSPointerArray *uploadFetcherPointerArray = [self uploadFetcherPointerArrayForBackgroundSessions];
- [uploadFetcherPointerArray compact];
- NSMutableSet *restoredSessionIdentifiers = [[NSMutableSet alloc] init];
- NSMutableArray *uploadFetchers = [[NSMutableArray alloc] init];
- for (GTMSessionUploadFetcher *uploadFetcher in uploadFetcherPointerArray) {
- NSString *sessionIdentifier = uploadFetcher.chunkFetcher.sessionIdentifier;
- if (sessionIdentifier) {
- [restoredSessionIdentifiers addObject:sessionIdentifier];
- [uploadFetchers addObject:uploadFetcher];
- }
- }
-
- // The system may have other ongoing background upload sessions. Restore upload fetchers for those
- // too.
- NSArray *fetchers = [GTMSessionFetcher fetchersForBackgroundSessions];
- for (GTMSessionFetcher *fetcher in fetchers) {
- NSString *sessionIdentifier = fetcher.sessionIdentifier;
- if (!sessionIdentifier || [restoredSessionIdentifiers containsObject:sessionIdentifier]) {
- continue;
- }
- NSDictionary *sessionIdentifierMetadata = [fetcher sessionIdentifierMetadata];
- if (sessionIdentifierMetadata == nil) {
- continue;
- }
- if (![sessionIdentifierMetadata[kGTMSessionIdentifierIsUploadChunkFetcherMetadataKey] boolValue]) {
- continue;
- }
- GTMSessionUploadFetcher *uploadFetcher =
- [self uploadFetcherForSessionIdentifierMetadata:sessionIdentifierMetadata];
- if (uploadFetcher == nil) {
- // Something went wrong with this upload fetcher, so kill the restored chunk fetcher.
- [fetcher stopFetching];
- continue;
- }
- [uploadFetchers addObject:uploadFetcher];
- uploadFetcher->_chunkFetcher = fetcher;
- uploadFetcher->_fetcherInFlight = fetcher;
- [uploadFetcher attachSendProgressBlockToChunkFetcher:fetcher];
- fetcher.completionHandler =
- [fetcher completionHandlerWithTarget:uploadFetcher
- didFinishSelector:@selector(chunkFetcher:finishedWithData:error:)];
-
- GTMSESSION_LOG_DEBUG(@"%@ restoring upload fetcher %@ for chunk fetcher %@",
- [self class], uploadFetcher, fetcher);
- }
- return uploadFetchers;
-}
-
-- (void)setUploadData:(NSData *)data {
- BOOL changed = NO;
-
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- if (_uploadData != data) {
- _uploadData = data;
- changed = YES;
- }
- }
- if (changed) {
- [self setupRequestHeaders];
- }
-}
-
-- (NSData *)uploadData {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _uploadData;
- }
-}
-
-- (void)setUploadFileHandle:(NSFileHandle *)fh {
- BOOL changed = NO;
-
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- if (_uploadFileHandle != fh) {
- _uploadFileHandle = fh;
- changed = YES;
- }
- }
- if (changed) {
- [self setupRequestHeaders];
- }
-}
-
-- (NSFileHandle *)uploadFileHandle {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _uploadFileHandle;
- }
-}
-
-- (void)setUploadFileURL:(NSURL *)uploadURL {
- BOOL changed = NO;
-
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- if (_uploadFileURL != uploadURL) {
- _uploadFileURL = uploadURL;
- changed = YES;
- }
- }
- if (changed) {
- [self setupRequestHeaders];
- }
-}
-
-- (NSURL *)uploadFileURL {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _uploadFileURL;
- }
-}
-
-- (void)setUploadFileLength:(int64_t)fullLength {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- _uploadFileLength = fullLength;
- }
-}
-
-- (void)setUploadDataLength:(int64_t)fullLength
- provider:(GTMSessionUploadFetcherDataProvider)block {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- _uploadDataProvider = [block copy];
- _uploadFileLength = fullLength;
- }
- [self setupRequestHeaders];
-}
-
-- (GTMSessionUploadFetcherDataProvider)uploadDataProvider {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _uploadDataProvider;
- }
-}
-
-
-- (void)setUploadMIMEType:(NSString *)uploadMIMEType {
- GTMSESSION_ASSERT_DEBUG(0, @"TODO: disallow setUploadMIMEType by making declaration readonly");
- // (and uploadMIMEType, chunksize, currentOffset)
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- _uploadMIMEType = uploadMIMEType;
- }
-}
-
-- (NSString *)uploadMIMEType {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _uploadMIMEType;
- }
-}
-
-- (void)setChunkSize:(int64_t)chunkSize {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- _chunkSize = chunkSize;
- }
-}
-
-- (int64_t)chunkSize {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _chunkSize;
- }
-}
-
-- (void)setupRequestHeaders {
- GTMSessionCheckNotSynchronized(self);
-
-#if DEBUG
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- int hasData = (_uploadData != nil) ? 1 : 0;
- int hasFileHandle = (_uploadFileHandle != nil) ? 1 : 0;
- int hasFileURL = (_uploadFileURL != nil) ? 1 : 0;
- int hasUploadDataProvider = (_uploadDataProvider != nil) ? 1 : 0;
- int numberOfSources = hasData + hasFileHandle + hasFileURL + hasUploadDataProvider;
- #pragma unused(numberOfSources)
- GTMSESSION_ASSERT_DEBUG(numberOfSources == 1,
- @"Need just one upload source (%d)", numberOfSources);
- } // @synchronized(self)
-#endif
-
- // Add our custom headers to the initial request indicating the data
- // type and total size to be delivered later in the chunk requests.
- NSMutableURLRequest *mutableRequest = [self.request mutableCopy];
-
- GTMSESSION_ASSERT_DEBUG((mutableRequest == nil) != (_uploadLocationURL == nil),
- @"Request and location are mutually exclusive");
- if (!mutableRequest) return;
-
- NSNumber *lengthNum = @([self fullUploadLength]);
- [mutableRequest setValue:@"resumable"
- forHTTPHeaderField:kGTMSessionHeaderXGoogUploadProtocol];
- [mutableRequest setValue:@"start"
- forHTTPHeaderField:kGTMSessionHeaderXGoogUploadCommand];
- [mutableRequest setValue:_uploadMIMEType
- forHTTPHeaderField:kGTMSessionHeaderXGoogUploadContentType];
- [mutableRequest setValue:lengthNum.stringValue
- forHTTPHeaderField:kGTMSessionHeaderXGoogUploadContentLength];
-
- NSString *method = mutableRequest.HTTPMethod;
- if (method == nil || [method caseInsensitiveCompare:@"GET"] == NSOrderedSame) {
- [mutableRequest setHTTPMethod:@"POST"];
- }
-
- // Ensure the user agent header identifies this to the upload server as a
- // GTMSessionUploadFetcher client. The /1 can be incremented in the unlikely circumstance
- // we need to make a bug fix in the client that the server can recognize.
- NSString *const kUserAgentStub = @"(GTMSUF/1)";
- NSString *userAgent = [mutableRequest valueForHTTPHeaderField:@"User-Agent"];
- if (userAgent == nil
- || [userAgent rangeOfString:kUserAgentStub].location == NSNotFound) {
- if (userAgent.length == 0) {
- userAgent = GTMFetcherStandardUserAgentString(nil);
- }
- userAgent = [userAgent stringByAppendingFormat:@" %@", kUserAgentStub];
- [mutableRequest setValue:userAgent forHTTPHeaderField:@"User-Agent"];
- }
- [self setRequest:mutableRequest];
-}
-
-- (void)setLocationURL:(NSURL * GTM_NULLABLE_TYPE)location
- uploadMIMEType:(NSString *)uploadMIMEType
- chunkSize:(int64_t)chunkSize {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- GTMSESSION_ASSERT_DEBUG(chunkSize > 0, @"chunk size is zero");
-
- // When resuming an upload, set the known upload target URL.
- _uploadLocationURL = location;
-
- _uploadMIMEType = uploadMIMEType;
- _chunkSize = chunkSize;
-
- // Indicate that we've not yet determined the file handle's length
- _uploadFileLength = -1;
-
- // Indicate that we've not yet determined the upload fetcher status
- _recentChunkStatusCode = -1;
-
- // If this is restarting an upload begun by another fetcher,
- // the location is specified but the request is nil
- _isRestartedUpload = (location != nil);
- } // @synchronized(self)
-}
-
-- (int64_t)fullUploadLength {
- int64_t result;
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- if (_uploadData) {
- result = (int64_t)_uploadData.length;
- } else {
- if (_uploadFileLength == -1) {
- if (_uploadFileHandle) {
- // First time through, seek to end to determine file length
- _uploadFileLength = (int64_t)[_uploadFileHandle seekToEndOfFile];
- } else if (_uploadDataProvider) {
- // _uploadFileLength is set when the _uploadDataProvider is set.
- GTMSESSION_ASSERT_DEBUG(_uploadFileLength >= 0, @"No uploadDataProvider length set");
- } else {
- NSNumber *filesizeNum;
- NSError *valueError;
- if ([_uploadFileURL getResourceValue:&filesizeNum
- forKey:NSURLFileSizeKey
- error:&valueError]) {
- _uploadFileLength = filesizeNum.longLongValue;
- } else {
- GTMSESSION_ASSERT_DEBUG(NO, @"Cannot get file size: %@\n %@",
- valueError, _uploadFileURL.path);
- _uploadFileLength = 0;
- }
- }
- }
- result = _uploadFileLength;
- }
- } // @synchronized(self)
- return result;
-}
-
-// Make a subdata of the upload data.
-- (void)generateChunkSubdataWithOffset:(int64_t)offset
- length:(int64_t)length
- response:(GTMSessionUploadFetcherDataProviderResponse)response {
- GTMSessionUploadFetcherDataProvider uploadDataProvider = self.uploadDataProvider;
- if (uploadDataProvider) {
- uploadDataProvider(offset, length, response);
- return;
- }
-
- NSData *uploadData = self.uploadData;
- if (uploadData) {
- // NSData provided.
- NSData *resultData;
- if (offset == 0 && length == (int64_t)uploadData.length) {
- resultData = uploadData;
- } else {
- int64_t dataLength = (int64_t)uploadData.length;
- // Ensure our range is valid. b/18007814
- if (offset + length > dataLength) {
- NSString *errorMessage = [NSString stringWithFormat:
- @"Range invalid for upload data. offset: %lld\tlength: %lld\tdataLength: %lld",
- offset, length, dataLength];
- GTMSESSION_ASSERT_DEBUG(NO, @"%@", errorMessage);
- response(nil, [self uploadChunkUnavailableErrorWithDescription:errorMessage]);
- return;
- }
- NSRange range = NSMakeRange((NSUInteger)offset, (NSUInteger)length);
- resultData = [uploadData subdataWithRange:range];
- }
- response(resultData, nil);
- return;
- }
- NSURL *uploadFileURL = self.uploadFileURL;
- if (uploadFileURL) {
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- [self generateChunkSubdataFromFileURL:uploadFileURL
- offset:offset
- length:length
- response:response];
- });
- return;
- }
- GTMSESSION_ASSERT_DEBUG(_uploadFileHandle, @"Unexpectedly missing upload data package");
- NSFileHandle *uploadFileHandle = self.uploadFileHandle;
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- [self generateChunkSubdataFromFileHandle:uploadFileHandle
- offset:offset
- length:length
- response:response];
- });
-}
-
-- (void)generateChunkSubdataFromFileHandle:(NSFileHandle *)fileHandle
- offset:(int64_t)offset
- length:(int64_t)length
- response:(GTMSessionUploadFetcherDataProviderResponse)response {
- NSData *resultData;
- NSError *error;
- @try {
- [fileHandle seekToFileOffset:(unsigned long long)offset];
- resultData = [fileHandle readDataOfLength:(NSUInteger)length];
- }
- @catch (NSException *exception) {
- GTMSESSION_ASSERT_DEBUG(NO, @"uploadFileHandle failed to read, %@", exception);
- error = [self uploadChunkUnavailableErrorWithDescription:exception.description];
- }
- // The response always re-dispatches to the main thread, so we skip doing that here.
- response(resultData, error);
-}
-
-- (void)generateChunkSubdataFromFileURL:(NSURL *)fileURL
- offset:(int64_t)offset
- length:(int64_t)length
- response:(GTMSessionUploadFetcherDataProviderResponse)response {
- GTMSessionCheckNotSynchronized(self);
-
- NSData *resultData;
- NSError *error;
- int64_t fullUploadLength = [self fullUploadLength];
- NSData *mappedData =
- [NSData dataWithContentsOfURL:fileURL
- options:NSDataReadingMappedAlways + NSDataReadingUncached
- error:&error];
- if (!mappedData) {
- // We could not create an NSData by memory-mapping the file.
-#if TARGET_IPHONE_SIMULATOR
- // NSTemporaryDirectory() can differ in the simulator between app restarts,
- // yet the contents for the new path remains unchanged, so try the latest temp path.
- if ([error.domain isEqual:NSCocoaErrorDomain] && (error.code == NSFileReadNoSuchFileError)) {
- NSString *filename = [fileURL lastPathComponent];
- NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
- NSURL *newFileURL = [NSURL fileURLWithPath:filePath];
- if (![newFileURL isEqual:fileURL]) {
- [self generateChunkSubdataFromFileURL:newFileURL
- offset:offset
- length:length
- response:response];
- return;
- }
- }
-#endif
-
- // If the file is just too large to create an NSData for, or if for some other reason we can't
- // map it, create an NSFileHandle instead to read a subset into an NSData.
-#if DEBUG
- NSNumber *fileSizeNum;
- BOOL hasFileSize = [fileURL getResourceValue:&fileSizeNum forKey:NSURLFileSizeKey error:NULL];
- GTMSESSION_LOG_DEBUG(@"Note: uploadFileURL is falling back to creating upload chunks by reading"
- @" an NSFileHandle since uploadFileURL failed to map the upload file,"
- @" file size %@, %@",
- hasFileSize ? fileSizeNum : @"unknown", error);
-#endif
-
- NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingFromURL:fileURL
- error:&error];
- if (fileHandle != nil) {
- [self generateChunkSubdataFromFileHandle:fileHandle
- offset:offset
- length:length
- response:response];
- return;
- }
- GTMSESSION_ASSERT_DEBUG(NO, @"uploadFileURL failed to read, %@", error);
- // Fall through with the error.
- } else {
- // Successfully created an NSData by memory-mapping the file.
- if (offset > 0 || length < fullUploadLength) {
- NSRange range = NSMakeRange((NSUInteger)offset, (NSUInteger)length);
- resultData = [mappedData subdataWithRange:range];
- } else {
- resultData = mappedData;
- }
- }
- // The response always re-dispatches to the main thread, so we skip re-dispatching here.
- response(resultData, error);
-}
-
-- (NSError *)uploadChunkUnavailableErrorWithDescription:(NSString *)description {
- // The description in the userInfo is intended as a clue to programmers, not
- // for client code to examine or rely on.
- NSDictionary *userInfo = @{ @"description" : description };
- return [NSError errorWithDomain:kGTMSessionFetcherErrorDomain
- code:GTMSessionFetcherErrorUploadChunkUnavailable
- userInfo:userInfo];
-}
-
-- (NSError *)prematureFailureErrorWithUserInfo:(NSDictionary *)userInfo {
- // An error for if we get an unexpected status from the upload server or
- // otherwise cannot continue. This is an issue beyond the upload protocol;
- // there's no way the client can do anything useful except give up.
- NSError *error = [NSError errorWithDomain:kGTMSessionFetcherStatusDomain
- code:501 // Not implemented
- userInfo:userInfo];
- return error;
-}
-
-+ (GTMSessionUploadFetcherStatus)uploadStatusFromResponseHeaders:(NSDictionary *)responseHeaders {
- NSString *statusString = [responseHeaders objectForKey:kGTMSessionHeaderXGoogUploadStatus];
- if ([statusString isEqual:@"active"]) {
- return kStatusActive;
- }
- if ([statusString isEqual:@"final"]) {
- return kStatusFinal;
- }
- if ([statusString isEqual:@"cancelled"]) {
- return kStatusCancelled;
- }
- return kStatusUnknown;
-}
-
-#pragma mark Method overrides affecting the initial fetch only
-
-- (void)setCompletionHandler:(GTMSessionFetcherCompletionHandler)handler {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- _delegateCompletionHandler = handler;
- }
-}
-
-- (void)setDelegateCallbackQueue:(dispatch_queue_t GTM_NULLABLE_TYPE)queue {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- _delegateCallbackQueue = queue;
- }
-}
-
-- (dispatch_queue_t GTM_NULLABLE_TYPE)delegateCallbackQueue {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _delegateCallbackQueue;
- }
-}
-
-- (BOOL)isRestartedUpload {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _isRestartedUpload;
- }
-}
-
-- (GTMSessionFetcher * GTM_NULLABLE_TYPE)chunkFetcher {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _chunkFetcher;
- }
-}
-
-- (void)setChunkFetcher:(GTMSessionFetcher * GTM_NULLABLE_TYPE)fetcher {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- _chunkFetcher = fetcher;
- }
-}
-
-- (void)setFetcherInFlight:(GTMSessionFetcher * GTM_NULLABLE_TYPE)fetcher {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- _fetcherInFlight = fetcher;
- }
-}
-
-- (GTMSessionFetcher * GTM_NULLABLE_TYPE)fetcherInFlight {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _fetcherInFlight;
- }
-}
-
-- (void)beginFetchWithCompletionHandler:(GTMSessionFetcherCompletionHandler)handler {
- GTMSessionCheckNotSynchronized(self);
-
- [self setInitialBodyLength:[self bodyLength]];
-
- // We'll hold onto the superclass's callback queue so we can invoke the handler
- // even after the superclass has released the queue and its callback handler, as
- // happens during auth failure.
- [self setDelegateCallbackQueue:self.callbackQueue];
- self.completionHandler = handler;
-
- if ([self isRestartedUpload]) {
- // When restarting an upload, we know the destination location for chunk fetches,
- // but we need to query to find the initial offset.
- if (![self isPaused]) {
- [self sendQueryForUploadOffsetWithFetcherProperties:self.properties];
- }
- return;
- }
- // We don't want to call into the client's completion block immediately
- // after the finish of the initial connection (the delegate is called only
- // when uploading finishes), so we substitute our own completion block to be
- // called when the initial connection finishes
- GTMSESSION_ASSERT_DEBUG(self.fetcherInFlight == nil, @"unexpected fetcher in flight: %@",
- self.fetcherInFlight);
-
- self.fetcherInFlight = self;
- [super beginFetchWithCompletionHandler:^(NSData *data, NSError *error) {
- self.fetcherInFlight = nil;
- // callback
-
- BOOL hasTestBlock = (self.testBlock != nil);
- if (![self isRestartedUpload] && !hasTestBlock) {
- if (error == nil) {
- [self beginChunkFetches];
- } else {
- if ([self retryTimer] == nil) {
- [self invokeFinalCallbackWithData:nil
- error:error
- shouldInvalidateLocation:YES];
- }
- }
- } else {
- // If there was no initial request, then this fetch is resuming some
- // other uploadFetcher's initial request, and the superclass's connection
- // is never used, so at this point we call the user's actual completion
- // block.
- if (!hasTestBlock) {
- [self invokeFinalCallbackWithData:data
- error:error
- shouldInvalidateLocation:YES];
- } else {
- // There was a test block, so we won't do chunk fetches, but we simulate obtaining
- // the data to be uploaded from the upload data provider block or the file handle,
- // and then call back.
- [self generateChunkSubdataWithOffset:0
- length:[self fullUploadLength]
- response:^(NSData *generateData, NSError *generateError) {
- [self invokeFinalCallbackWithData:data
- error:error
- shouldInvalidateLocation:YES];
- }];
- }
- }
- }];
-}
-
-- (void)beginChunkFetches {
- GTMSessionCheckNotSynchronized(self);
-
-#if DEBUG
- // The initial response of the resumable upload protocol should have an
- // empty body
- //
- // This assert typically happens because the upload create/edit link URL was
- // not supplied with the request, and the server is thus expecting a non-
- // resumable request/response.
- if (self.downloadedData.length > 0) {
- NSData *downloadedData = self.downloadedData;
- NSString *str = [[NSString alloc] initWithData:downloadedData
- encoding:NSUTF8StringEncoding];
- #pragma unused(str)
- GTMSESSION_ASSERT_DEBUG(NO, @"unexpected response data (uploading to the wrong URL?)\n%@", str);
- }
-#endif
-
- // We need to get the upload URL from the location header to continue.
- NSDictionary *responseHeaders = [self responseHeaders];
-
- [self retrieveUploadChunkGranularityFromResponseHeaders:responseHeaders];
-
- GTMSessionUploadFetcherStatus uploadStatus =
- [[self class] uploadStatusFromResponseHeaders:responseHeaders];
- GTMSESSION_ASSERT_DEBUG(uploadStatus != kStatusUnknown,
- @"beginChunkFetches has unexpected upload status for headers %@", responseHeaders);
-
- BOOL isPrematureStop = (uploadStatus == kStatusFinal) || (uploadStatus == kStatusCancelled);
-
- NSString *uploadLocationURLStr = [responseHeaders objectForKey:kGTMSessionHeaderXGoogUploadURL];
- BOOL hasUploadLocation = (uploadLocationURLStr.length > 0);
-
- if (isPrematureStop || !hasUploadLocation) {
- GTMSESSION_ASSERT_DEBUG(NO, @"Premature failure: upload-status:\"%@\" location:%@",
- [responseHeaders objectForKey:kGTMSessionHeaderXGoogUploadStatus], uploadLocationURLStr);
- // We cannot continue since we do not know the location to use
- // as our upload destination.
- NSDictionary *userInfo = nil;
- NSData *downloadedData = self.downloadedData;
- if (downloadedData.length > 0) {
- userInfo = @{ kGTMSessionFetcherStatusDataKey : downloadedData };
- }
- NSError *failureError = [self prematureFailureErrorWithUserInfo:userInfo];
- [self invokeFinalCallbackWithData:nil
- error:failureError
- shouldInvalidateLocation:YES];
- return;
- }
-
- self.uploadLocationURL = [NSURL URLWithString:uploadLocationURLStr];
-
- NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
- [nc postNotificationName:kGTMSessionFetcherUploadLocationObtainedNotification
- object:self];
-
- // we've now sent all of the initial post body data, so we need to include
- // its size in future progress indicator callbacks
- [self setInitialBodySent:[self initialBodyLength]];
-
- // just in case the user paused us during the initial fetch...
- if (![self isPaused]) {
- [self uploadNextChunkWithOffset:0];
- }
-}
-
-- (void)URLSession:(NSURLSession *)session
- task:(NSURLSessionTask *)task
- didSendBodyData:(int64_t)bytesSent
- totalBytesSent:(int64_t)totalBytesSent
- totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
- // Overrides the superclass.
- [self invokeDelegateWithDidSendBytes:bytesSent
- totalBytesSent:totalBytesSent
- totalBytesExpectedToSend:totalBytesExpectedToSend + [self fullUploadLength]];
-}
-
-- (BOOL)shouldReleaseCallbacksUponCompletion {
- // Overrides the superclass.
-
- // We don't want the superclass to release the delegate and callback
- // blocks once the initial fetch has finished
- //
- // This is invoked for only successful completion of the connection;
- // an error always will invoke and release the callbacks
- return NO;
-}
-
-- (void)invokeFinalCallbackWithData:(NSData *)data
- error:(NSError *)error
- shouldInvalidateLocation:(BOOL)shouldInvalidateLocation {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- if (shouldInvalidateLocation) {
- _uploadLocationURL = nil;
- }
-
- dispatch_queue_t queue = _delegateCallbackQueue;
- GTMSessionFetcherCompletionHandler handler = _delegateCompletionHandler;
- if (queue && handler) {
- [self invokeOnCallbackQueue:queue
- afterUserStopped:NO
- block:^{
- handler(data, error);
- }];
- }
- } // @synchronized(self)
-
- [self releaseUploadAndBaseCallbacks];
-}
-
-- (void)releaseUploadAndBaseCallbacks {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- _delegateCallbackQueue = nil;
- _delegateCompletionHandler = nil;
- _uploadDataProvider = nil;
- }
-
- // Release the base class's callbacks, too, if needed.
- [self releaseCallbacks];
-}
-
-- (void)stopFetchReleasingCallbacks:(BOOL)shouldReleaseCallbacks {
- GTMSessionCheckNotSynchronized(self);
-
- // Clear _fetcherInFlight when stopped. Moved from stopFetching, since that's a public method,
- // where this method does the work. Fixes issue clearing value when retryBlock included.
- GTMSessionFetcher *fetcherInFlight = self.fetcherInFlight;
- if (fetcherInFlight == self) {
- self.fetcherInFlight = nil;
- }
-
- [super stopFetchReleasingCallbacks:shouldReleaseCallbacks];
-
- if (shouldReleaseCallbacks) {
- [self releaseUploadAndBaseCallbacks];
- }
-}
-
-#pragma mark Chunk fetching methods
-
-- (void)uploadNextChunkWithOffset:(int64_t)offset {
- // use the properties in each chunk fetcher
- NSDictionary *props = [self properties];
-
- [self uploadNextChunkWithOffset:offset
- fetcherProperties:props];
-}
-
-- (void)sendQueryForUploadOffsetWithFetcherProperties:(NSDictionary *)props {
- GTMSessionFetcher *queryFetcher = [self uploadFetcherWithProperties:props
- isQueryFetch:YES];
- queryFetcher.bodyData = [NSData data];
-
- NSString *originalComment = self.comment;
- [queryFetcher setCommentWithFormat:@"%@ (query offset)",
- originalComment ? originalComment : @"upload"];
-
- [queryFetcher setRequestValue:@"query" forHTTPHeaderField:kGTMSessionHeaderXGoogUploadCommand];
-
- self.fetcherInFlight = queryFetcher;
- [queryFetcher beginFetchWithDelegate:self
- didFinishSelector:@selector(queryFetcher:finishedWithData:error:)];
-}
-
-- (void)queryFetcher:(GTMSessionFetcher *)queryFetcher
- finishedWithData:(NSData *)data
- error:(NSError *)error {
- self.fetcherInFlight = nil;
-
- NSDictionary *responseHeaders = [queryFetcher responseHeaders];
- NSString *sizeReceivedHeader;
-
- GTMSessionUploadFetcherStatus uploadStatus =
- [[self class] uploadStatusFromResponseHeaders:responseHeaders];
- GTMSESSION_ASSERT_DEBUG(uploadStatus != kStatusUnknown || error != nil,
- @"query fetcher completion has unexpected upload status for headers %@", responseHeaders);
-
- if (error == nil) {
- sizeReceivedHeader = [responseHeaders objectForKey:kGTMSessionHeaderXGoogUploadSizeReceived];
-
- if (uploadStatus == kStatusCancelled ||
- (uploadStatus == kStatusActive && sizeReceivedHeader == nil)) {
- NSDictionary *userInfo = nil;
- if (data.length > 0) {
- userInfo = @{ kGTMSessionFetcherStatusDataKey : data };
- }
- error = [self prematureFailureErrorWithUserInfo:userInfo];
- }
- }
-
- if (error == nil) {
- int64_t offset = [sizeReceivedHeader longLongValue];
- int64_t fullUploadLength = [self fullUploadLength];
- if (offset >= fullUploadLength || uploadStatus == kStatusFinal) {
- // Handle we're done
- [self chunkFetcher:queryFetcher finishedWithData:data error:nil];
- } else {
- [self retrieveUploadChunkGranularityFromResponseHeaders:responseHeaders];
- [self uploadNextChunkWithOffset:offset];
- }
- } else {
- // Handle query error
- [self chunkFetcher:queryFetcher finishedWithData:data error:error];
- }
-}
-
-- (void)sendCancelUploadWithFetcherProperties:(NSDictionary *)props {
- GTMSessionFetcher *cancelFetcher = [self uploadFetcherWithProperties:props
- isQueryFetch:YES];
- cancelFetcher.bodyData = [NSData data];
-
- NSString *originalComment = self.comment;
- [cancelFetcher setCommentWithFormat:@"%@ (cancel)",
- originalComment ? originalComment : @"upload"];
-
- [cancelFetcher setRequestValue:@"cancel" forHTTPHeaderField:kGTMSessionHeaderXGoogUploadCommand];
-
- self.fetcherInFlight = cancelFetcher;
- [cancelFetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error) {
- self.fetcherInFlight = nil;
- if (error) {
- GTMSESSION_LOG_DEBUG(@"cancelFetcher %@", error);
- }
- }];
-}
-
-- (void)uploadNextChunkWithOffset:(int64_t)offset
- fetcherProperties:(NSDictionary *)props {
- GTMSessionCheckNotSynchronized(self);
-
- // Example chunk headers:
- // X-Goog-Upload-Command: upload, finalize
- // X-Goog-Upload-Offset: 0
- // Content-Length: 2000000
- // Content-Type: image/jpeg
- //
- // {bytes 0-1999999}
-
- // The chunk upload URL requires no authentication header.
- GTMSessionFetcher *chunkFetcher = [self uploadFetcherWithProperties:props
- isQueryFetch:NO];
- [self attachSendProgressBlockToChunkFetcher:chunkFetcher];
-
- BOOL isUploadingFileURL = (self.uploadFileURL != nil);
-
- // Upload another chunk, meeting server-required granularity.
- int64_t chunkSize = self.chunkSize;
-
- int64_t fullUploadLength = [self fullUploadLength];
-
- BOOL isUploadingFullFile = (offset == 0 && chunkSize >= fullUploadLength);
- if (!isUploadingFileURL || !isUploadingFullFile) {
- // We're not uploading the entire file and given the file URL. Since we'll be
- // allocating a subdata block for a chunk, we need to bound it to something that
- // won't blow the process's memory.
- if (chunkSize > kGTMSessionUploadFetcherMaximumDemandBufferSize) {
- chunkSize = kGTMSessionUploadFetcherMaximumDemandBufferSize;
- }
- }
-
- int64_t granularity = self.uploadGranularity;
- if (granularity > 0) {
- if (chunkSize < granularity) {
- chunkSize = granularity;
- } else {
- chunkSize = chunkSize - (chunkSize % granularity);
- }
- }
-
- GTMSESSION_ASSERT_DEBUG(offset < fullUploadLength || fullUploadLength == 0,
- @"offset %lld exceeds data length %lld", offset, fullUploadLength);
-
- if (granularity > 0) {
- offset = offset - (offset % granularity);
- }
-
- // If the chunk size is bigger than the remaining data, or else
- // it's close enough in size to the remaining data that we'd rather
- // avoid having a whole extra http fetch for the leftover bit, then make
- // this chunk size exactly match the remaining data size
- NSString *command;
- int64_t thisChunkSize = chunkSize;
-
- BOOL isChunkTooBig = (thisChunkSize >= (fullUploadLength - offset));
- BOOL isChunkAlmostBigEnough = (fullUploadLength - offset - 2500 < thisChunkSize);
- BOOL isFinalChunk = isChunkTooBig || isChunkAlmostBigEnough;
- if (isFinalChunk) {
- thisChunkSize = fullUploadLength - offset;
- if (thisChunkSize > 0) {
- command = @"upload, finalize";
- } else {
- command = @"finalize";
- }
- } else {
- command = @"upload";
- }
- NSString *lengthStr = @(thisChunkSize).stringValue;
- NSString *offsetStr = @(offset).stringValue;
-
- [chunkFetcher setRequestValue:command forHTTPHeaderField:kGTMSessionHeaderXGoogUploadCommand];
- [chunkFetcher setRequestValue:lengthStr forHTTPHeaderField:@"Content-Length"];
- [chunkFetcher setRequestValue:offsetStr forHTTPHeaderField:kGTMSessionHeaderXGoogUploadOffset];
-
- // Append the range of bytes in this chunk to the fetcher comment.
- NSString *baseComment = self.comment;
- [chunkFetcher setCommentWithFormat:@"%@ (%lld-%lld)",
- baseComment ? baseComment : @"upload", offset, MAX(0, offset + thisChunkSize - 1)];
-
- // The chunk size may have changed, so determine again if we're uploading the full file.
- isUploadingFullFile = (offset == 0 && thisChunkSize >= fullUploadLength);
- if (isUploadingFullFile && isUploadingFileURL) {
- // The data is the full upload file URL.
- chunkFetcher.bodyFileURL = self.uploadFileURL;
- [self beginChunkFetcher:chunkFetcher
- offset:offset];
- } else {
- // Make an NSData for the subset for this upload chunk.
- self.subdataGenerating = YES;
- [self generateChunkSubdataWithOffset:offset
- length:thisChunkSize
- response:^(NSData *chunkData, NSError *chunkError) {
- // The subdata methods may leave us on a background thread.
- dispatch_async(dispatch_get_main_queue(), ^{
- self.subdataGenerating = NO;
- if (chunkData == nil) {
- NSError *responseError = chunkError;
- if (!responseError) {
- responseError = [self uploadChunkUnavailableErrorWithDescription:@"chunkData is nil"];
- }
- [self invokeFinalCallbackWithData:nil
- error:responseError
- shouldInvalidateLocation:YES];
- return;
- }
-
- BOOL didWriteFile = NO;
- if (isUploadingFileURL) {
- // Make a temporary file with the data subset.
- NSString *tempName =
- [NSString stringWithFormat:@"GTMUpload_temp_%@", [[NSUUID UUID] UUIDString]];
- NSString *tempPath = [NSTemporaryDirectory() stringByAppendingPathComponent:tempName];
- NSError *writeError;
- didWriteFile = [chunkData writeToFile:tempPath
- options:NSDataWritingAtomic
- error:&writeError];
- if (didWriteFile) {
- chunkFetcher.bodyFileURL = [NSURL fileURLWithPath:tempPath];
- } else {
- GTMSESSION_LOG_DEBUG(@"writeToFile failed: %@\n%@", writeError, tempPath);
- }
- }
- if (!didWriteFile) {
- chunkFetcher.bodyData = [chunkData copy];
- }
- [self beginChunkFetcher:chunkFetcher
- offset:offset];
- });
- }];
- }
-}
-
-- (void)beginChunkFetcher:(GTMSessionFetcher *)chunkFetcher
- offset:(int64_t)offset {
-
- // Track the current offset for progress reporting
- self.currentOffset = offset;
-
- // Hang on to the fetcher in case we need to cancel it. We set these before beginning the
- // chunk fetch so the observers notified of chunk fetches can inspect the upload fetcher to
- // match to the chunk.
- self.chunkFetcher = chunkFetcher;
- self.fetcherInFlight = chunkFetcher;
-
- // Update the last chunk request, including any request headers.
- self.lastChunkRequest = chunkFetcher.request;
-
- [chunkFetcher beginFetchWithDelegate:self
- didFinishSelector:@selector(chunkFetcher:finishedWithData:error:)];
-}
-
-- (void)attachSendProgressBlockToChunkFetcher:(GTMSessionFetcher *)chunkFetcher {
- chunkFetcher.sendProgressBlock = ^(int64_t bytesSent, int64_t totalBytesSent,
- int64_t totalBytesExpectedToSend) {
- // The total bytes expected include the initial body and the full chunked
- // data, independent of how big this fetcher's chunk is.
- int64_t initialBodySent = [self bodyLength]; // TODO(grobbins) use [self initialBodySent]
- int64_t totalSent = initialBodySent + self.currentOffset + totalBytesSent;
- int64_t totalExpected = initialBodySent + [self fullUploadLength];
-
- [self invokeDelegateWithDidSendBytes:bytesSent
- totalBytesSent:totalSent
- totalBytesExpectedToSend:totalExpected];
- };
-}
-
-- (NSDictionary *)uploadSessionIdentifierMetadata {
- NSMutableDictionary *metadata = [NSMutableDictionary dictionary];
- metadata[kGTMSessionIdentifierIsUploadChunkFetcherMetadataKey] = @YES;
- GTMSESSION_ASSERT_DEBUG(self.uploadFileURL,
- @"Invalid upload fetcher to create session identifier for metadata");
- metadata[kGTMSessionIdentifierUploadFileURLMetadataKey] = [self.uploadFileURL absoluteString];
- metadata[kGTMSessionIdentifierUploadFileLengthMetadataKey] = @([self fullUploadLength]);
-
- if (self.uploadLocationURL) {
- metadata[kGTMSessionIdentifierUploadLocationURLMetadataKey] =
- [self.uploadLocationURL absoluteString];
- }
- if (self.uploadMIMEType) {
- metadata[kGTMSessionIdentifierUploadMIMETypeMetadataKey] = self.uploadMIMEType;
- }
- metadata[kGTMSessionIdentifierUploadChunkSizeMetadataKey] = @(self.chunkSize);
- metadata[kGTMSessionIdentifierUploadCurrentOffsetMetadataKey] = @(self.currentOffset);
- return metadata;
-}
-
-- (GTMSessionFetcher *)uploadFetcherWithProperties:(NSDictionary *)properties
- isQueryFetch:(BOOL)isQueryFetch {
- GTMSessionCheckNotSynchronized(self);
-
- // Common code to make a request for a query command or for a chunk upload.
- NSURL *uploadLocationURL = self.uploadLocationURL;
- NSMutableURLRequest *chunkRequest = [NSMutableURLRequest requestWithURL:uploadLocationURL];
- [chunkRequest setHTTPMethod:@"PUT"];
-
- // copy the user-agent from the original connection
- NSURLRequest *origRequest = self.request;
- NSString *userAgent = [origRequest valueForHTTPHeaderField:@"User-Agent"];
- if (userAgent.length > 0) {
- [chunkRequest setValue:userAgent forHTTPHeaderField:@"User-Agent"];
- }
- // To avoid timeouts when debugging, copy the timeout of the initial fetcher.
- NSTimeInterval origTimeout = [origRequest timeoutInterval];
- [chunkRequest setTimeoutInterval:origTimeout];
-
- //
- // Make a new chunk fetcher.
- //
- GTMSessionFetcher *chunkFetcher = [GTMSessionFetcher fetcherWithRequest:chunkRequest];
- chunkFetcher.callbackQueue = self.callbackQueue;
- chunkFetcher.sessionUserInfo = self.sessionUserInfo;
- chunkFetcher.configurationBlock = self.configurationBlock;
- chunkFetcher.allowedInsecureSchemes = self.allowedInsecureSchemes;
- chunkFetcher.allowLocalhostRequest = self.allowLocalhostRequest;
- chunkFetcher.allowInvalidServerCertificates = self.allowInvalidServerCertificates;
- chunkFetcher.useUploadTask = !isQueryFetch;
-
- if (self.uploadFileURL && !isQueryFetch && self.useBackgroundSession) {
- [chunkFetcher createSessionIdentifierWithMetadata:[self uploadSessionIdentifierMetadata]];
- }
-
- // Give the chunk fetcher the same properties as the previous chunk fetcher
- chunkFetcher.properties = [properties mutableCopy];
- [chunkFetcher setProperty:[NSValue valueWithNonretainedObject:self]
- forKey:kGTMSessionUploadFetcherChunkParentKey];
-
- // copy other fetcher settings to the new fetcher
- chunkFetcher.retryEnabled = self.retryEnabled;
- chunkFetcher.maxRetryInterval = self.maxRetryInterval;
-
- if ([self isRetryEnabled]) {
- // We interpose our own retry method both so we can change the request to ask the server to
- // tell us where to resume the chunk.
- chunkFetcher.retryBlock = ^(BOOL suggestedWillRetry, NSError *chunkError,
- GTMSessionFetcherRetryResponse response){
- void (^finish)(BOOL) = ^(BOOL shouldRetry){
- // We'll retry by sending an offset query.
- if (shouldRetry) {
- self.shouldInitiateOffsetQuery = !isQueryFetch;
-
- // We don't know what our actual offset is anymore, but the server will tell us.
- self.currentOffset = 0;
- }
- // We don't actually want to retry this specific fetcher.
- response(NO);
- };
-
- GTMSessionFetcherRetryBlock retryBlock = self.retryBlock;
- if (retryBlock) {
- // Ask the client, then call the finish block above.
- retryBlock(suggestedWillRetry, chunkError, finish);
- } else {
- finish(suggestedWillRetry);
- }
- };
- }
-
- return chunkFetcher;
-}
-
-- (void)chunkFetcher:(GTMSessionFetcher *)chunkFetcher
- finishedWithData:(NSData *)data
- error:(NSError *)error {
- BOOL hasDestroyedOldChunkFetcher = NO;
- self.fetcherInFlight = nil;
-
- NSDictionary *responseHeaders = [chunkFetcher responseHeaders];
- GTMSessionUploadFetcherStatus uploadStatus =
- [[self class] uploadStatusFromResponseHeaders:responseHeaders];
- GTMSESSION_ASSERT_DEBUG(uploadStatus != kStatusUnknown
- || error != nil
- || self.wasCreatedFromBackgroundSession,
- @"chunk fetcher completion has kStatusUnknown upload status for headers %@ fetcher %@",
- responseHeaders, self);
- BOOL isUploadStatusStopped = (uploadStatus == kStatusFinal || uploadStatus == kStatusCancelled);
-
- // Check if the fetcher was actually querying. If it failed, do not retry,
- // as it would enter an infinite retry loop.
- NSString *uploadCommand =
- chunkFetcher.request.allHTTPHeaderFields[kGTMSessionHeaderXGoogUploadCommand];
- BOOL isQueryFetch = [uploadCommand isEqual:@"query"];
-
- int64_t previousContentLength =
- [[chunkFetcher.request valueForHTTPHeaderField:@"Content-Length"] longLongValue];
- // The Content-Length header may not be present if the chunk fetcher was recreated from
- // a background session.
- BOOL hasKnownChunkSize = (previousContentLength > 0);
- BOOL needsQuery = (!hasKnownChunkSize && !isUploadStatusStopped);
-
- if (error || (needsQuery && !isQueryFetch)) {
- NSInteger status = error.code;
-
- // Status 4xx indicates a bad offset in the Google upload protocol. However, do not retry status
- // 404 per spec, nor if the upload size appears to have been zero (since the server will just
- // keep asking us to retry.)
- if (self.shouldInitiateOffsetQuery ||
- (needsQuery && !isQueryFetch) ||
- ([error.domain isEqual:kGTMSessionFetcherStatusDomain] &&
- status >= 400 && status <= 499 &&
- status != 404 &&
- uploadStatus == kStatusActive &&
- previousContentLength > 0)) {
- self.shouldInitiateOffsetQuery = NO;
- [self destroyChunkFetcher];
- hasDestroyedOldChunkFetcher = YES;
- [self sendQueryForUploadOffsetWithFetcherProperties:chunkFetcher.properties];
- } else {
- // Some unexpected status has occurred; handle it as we would a regular
- // object fetcher failure.
- [self invokeFinalCallbackWithData:data
- error:error
- shouldInvalidateLocation:NO];
- }
- } else {
- // The chunk has uploaded successfully.
- int64_t newOffset = self.currentOffset + previousContentLength;
-#if DEBUG
- // Verify that if we think all of the uploading data has been sent, the server responded with
- // the "final" upload status.
- BOOL hasUploadAllData = (newOffset == [self fullUploadLength]);
- BOOL isFinalStatus = (uploadStatus == kStatusFinal);
- #pragma unused(hasUploadAllData,isFinalStatus)
- GTMSESSION_ASSERT_DEBUG(hasUploadAllData == isFinalStatus || !hasKnownChunkSize,
- @"uploadStatus:%@ newOffset:%zd (%lld + %zd) fullUploadLength:%lld"
- @" chunkFetcher:%@ requestHeaders:%@ responseHeaders:%@",
- [responseHeaders objectForKey:kGTMSessionHeaderXGoogUploadStatus],
- newOffset, self.currentOffset, previousContentLength,
- [self fullUploadLength],
- chunkFetcher, chunkFetcher.request.allHTTPHeaderFields,
- responseHeaders);
-#endif
- if (isUploadStatusStopped) {
- // This was the last chunk.
- if (error == nil && uploadStatus == kStatusCancelled) {
- // Report cancelled status as an error.
- NSDictionary *userInfo = nil;
- if (data.length > 0) {
- userInfo = @{ kGTMSessionFetcherStatusDataKey : data };
- }
- data = nil;
- error = [self prematureFailureErrorWithUserInfo:userInfo];
- } else {
- // The upload is in final status.
- //
- // Take the chunk fetcher's data as the superclass data.
- self.downloadedData = data;
- self.statusCode = chunkFetcher.statusCode;
- }
-
- // we're done
- [self invokeFinalCallbackWithData:data
- error:error
- shouldInvalidateLocation:YES];
- } else {
- // Start the next chunk.
- self.currentOffset = newOffset;
-
- // We want to destroy this chunk fetcher before creating the next one, but
- // we want to pass on its properties
- NSDictionary *props = [chunkFetcher properties];
-
- // We no longer need to be able to cancel this chunkFetcher. Destroy it
- // before we create a new chunk fetcher.
- [self destroyChunkFetcher];
- hasDestroyedOldChunkFetcher = YES;
-
- [self uploadNextChunkWithOffset:newOffset
- fetcherProperties:props];
- }
- }
- if (!hasDestroyedOldChunkFetcher) {
- [self destroyChunkFetcher];
- }
-}
-
-- (void)destroyChunkFetcher {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- if (_fetcherInFlight == _chunkFetcher) {
- _fetcherInFlight = nil;
- }
-
- [_chunkFetcher stopFetching];
-
- NSURL *chunkFileURL = _chunkFetcher.bodyFileURL;
- BOOL wasTemporaryUploadFile = ![chunkFileURL isEqual:_uploadFileURL];
- if (wasTemporaryUploadFile) {
- NSError *error;
- [[NSFileManager defaultManager] removeItemAtURL:chunkFileURL
- error:&error];
- if (error) {
- GTMSESSION_LOG_DEBUG(@"removingItemAtURL failed: %@\n%@", error, chunkFileURL);
- }
- }
-
- _recentChunkReponseHeaders = _chunkFetcher.responseHeaders;
-
- // To avoid retain cycles, remove all properties except the parent identifier.
- _chunkFetcher.properties =
- @{ kGTMSessionUploadFetcherChunkParentKey : [NSValue valueWithNonretainedObject:self] };
-
- _chunkFetcher.retryBlock = nil;
- _chunkFetcher.sendProgressBlock = nil;
- _chunkFetcher = nil;
- } // @synchronized(self)
-}
-
-// This method calculates the proper values to pass to the client's send progress block.
-//
-// The actual total bytes sent include the initial body sent, plus the
-// offset into the batched data prior to the current chunk fetcher
-
-- (void)invokeDelegateWithDidSendBytes:(int64_t)bytesSent
- totalBytesSent:(int64_t)totalBytesSent
- totalBytesExpectedToSend:(int64_t)totalBytesExpected {
- GTMSessionCheckNotSynchronized(self);
-
- // Ensure the chunk fetcher survives the callback in case the user pauses the upload process.
- __block GTMSessionFetcher *holdFetcher = self.chunkFetcher;
-
- [self invokeOnCallbackQueue:self.delegateCallbackQueue
- afterUserStopped:NO
- block:^{
- GTMSessionFetcherSendProgressBlock sendProgressBlock = self.sendProgressBlock;
- if (sendProgressBlock) {
- sendProgressBlock(bytesSent, totalBytesSent, totalBytesExpected);
- }
- holdFetcher = nil;
- }];
-}
-
-- (void)retrieveUploadChunkGranularityFromResponseHeaders:(NSDictionary *)responseHeaders {
- GTMSessionCheckNotSynchronized(self);
-
- // Standard granularity for Google uploads is 256K.
- NSString *chunkGranularityHeader =
- [responseHeaders objectForKey:@"X-Goog-Upload-Chunk-Granularity"];
- self.uploadGranularity = chunkGranularityHeader.longLongValue;
-}
-
-#pragma mark -
-
-- (BOOL)isPaused {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _isPaused;
- } // @synchronized(self)
-}
-
-- (void)pauseFetching {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- _isPaused = YES;
- } // @synchronized(self)
-
- // Pausing just means stopping the current chunk from uploading;
- // when we resume, we will send a query request to the server to
- // figure out what bytes to resume sending.
- //
- // We won't try to cancel the initial data upload, but rather will check
- // for being paused in beginChunkFetches.
- [self destroyChunkFetcher];
-}
-
-- (void)resumeFetching {
- BOOL wasPaused;
-
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- wasPaused = _isPaused;
- _isPaused = NO;
- } // @synchronized(self)
-
- if (wasPaused) {
- [self sendQueryForUploadOffsetWithFetcherProperties:self.properties];
- }
-}
-
-- (void)stopFetching {
- // Overrides the superclass
- [self destroyChunkFetcher];
-
- // If we think the server is waiting for more data, then tell it there won't be more.
- if (self.uploadLocationURL) {
- [self sendCancelUploadWithFetcherProperties:[self properties]];
- self.uploadLocationURL = nil;
- }
-
- [super stopFetching];
-}
-
-#pragma mark -
-
-// Public properties.
-@synthesize currentOffset = _currentOffset,
- delegateCompletionHandler = _delegateCompletionHandler,
- chunkFetcher = _chunkFetcher,
- lastChunkRequest = _lastChunkRequest,
- subdataGenerating = _subdataGenerating,
- shouldInitiateOffsetQuery = _shouldInitiateOffsetQuery,
- uploadGranularity = _uploadGranularity;
-
-// Internal properties.
-@dynamic fetcherInFlight;
-@dynamic activeFetcher;
-@dynamic statusCode;
-@dynamic delegateCallbackQueue;
-
-+ (void)removePointer:(void *)pointer fromPointerArray:(NSPointerArray *)pointerArray {
- for (NSUInteger index = 0, count = pointerArray.count; index < count; ++index) {
- void *pointerAtIndex = [pointerArray pointerAtIndex:index];
- if (pointerAtIndex == pointer) {
- [pointerArray removePointerAtIndex:index];
- return;
- }
- }
-}
-
-- (BOOL)useBackgroundSession {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _useBackgroundSessionOnChunkFetchers;
- } // @synchronized(self
-}
-
-- (void)setUseBackgroundSession:(BOOL)useBackgroundSession {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- if (_useBackgroundSessionOnChunkFetchers != useBackgroundSession) {
- _useBackgroundSessionOnChunkFetchers = useBackgroundSession;
- NSPointerArray *uploadFetcherPointerArrayForBackgroundSessions =
- [[self class] uploadFetcherPointerArrayForBackgroundSessions];
- if (_useBackgroundSessionOnChunkFetchers) {
- [uploadFetcherPointerArrayForBackgroundSessions addPointer:(__bridge void *)self];
- } else {
- [[self class] removePointer:(__bridge void *)self
- fromPointerArray:uploadFetcherPointerArrayForBackgroundSessions];
- }
- }
- } // @synchronized(self
-}
-
-- (BOOL)canFetchWithBackgroundSession {
- // The initial upload fetcher is always a foreground session; the
- // useBackgroundSession property will apply only to chunk fetchers,
- // not to queries.
- return NO;
-}
-
-- (NSDictionary *)responseHeaders {
- GTMSessionCheckNotSynchronized(self);
- // Overrides the superclass
-
- // If asked for the fetcher's response, use the most recent chunk fetcher's response,
- // since the original request's response lacks useful information like the actual
- // Content-Type.
- NSDictionary *dict = self.chunkFetcher.responseHeaders;
- if (dict) {
- return dict;
- }
-
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- if (_recentChunkReponseHeaders) {
- return _recentChunkReponseHeaders;
- }
- } // @synchronized(self
-
- // No chunk fetcher yet completed, so return whatever we have from the initial fetch.
- return [super responseHeaders];
-}
-
-- (NSInteger)statusCodeUnsynchronized {
- GTMSessionCheckSynchronized(self);
-
- if (_recentChunkStatusCode != -1) {
- // Overrides the superclass to indicate status appropriate to the initial
- // or latest chunk fetch
- return _recentChunkStatusCode;
- } else {
- return [super statusCodeUnsynchronized];
- }
-}
-
-
-- (void)setStatusCode:(NSInteger)val {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- _recentChunkStatusCode = val;
- }
-}
-
-- (int64_t)initialBodyLength {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _initialBodyLength;
- }
-}
-
-- (void)setInitialBodyLength:(int64_t)length {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- _initialBodyLength = length;
- }
-}
-
-- (int64_t)initialBodySent {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _initialBodySent;
- }
-}
-
-- (void)setInitialBodySent:(int64_t)length {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- _initialBodySent = length;
- }
-}
-
-- (NSURL *)uploadLocationURL {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- return _uploadLocationURL;
- }
-}
-
-- (void)setUploadLocationURL:(NSURL *)locationURL {
- @synchronized(self) {
- GTMSessionMonitorSynchronized(self);
-
- _uploadLocationURL = locationURL;
- }
-}
-
-- (GTMSessionFetcher *)activeFetcher {
- GTMSessionFetcher *result = self.fetcherInFlight;
- if (result) return result;
-
- return self;
-}
-
-- (BOOL)isFetching {
- // If there is an active chunk fetcher, then the upload fetcher is considered
- // to still be fetching.
- if (self.fetcherInFlight != nil) return YES;
-
- return [super isFetching];
-}
-
-- (BOOL)waitForCompletionWithTimeout:(NSTimeInterval)timeoutInSeconds {
- NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeoutInSeconds];
-
- while (self.fetcherInFlight || self.subdataGenerating) {
- if ([timeoutDate timeIntervalSinceNow] < 0) return NO;
-
- if (self.subdataGenerating) {
- // Allow time for subdata generation.
- NSDate *stopDate = [NSDate dateWithTimeIntervalSinceNow:0.001];
- [[NSRunLoop currentRunLoop] runUntilDate:stopDate];
- } else {
- // Wait for any chunk or query fetchers that still have pending callbacks or
- // notifications.
- BOOL timedOut;
-
- if (self.fetcherInFlight == self) {
- timedOut = ![super waitForCompletionWithTimeout:timeoutInSeconds];
- } else {
- timedOut = ![self.fetcherInFlight waitForCompletionWithTimeout:timeoutInSeconds];
- }
- if (timedOut) return NO;
- }
- }
- return YES;
-}
-
-@end
-
-@implementation GTMSessionFetcher (GTMSessionUploadFetcherMethods)
-
-- (GTMSessionUploadFetcher *)parentUploadFetcher {
- NSValue *property = [self propertyForKey:kGTMSessionUploadFetcherChunkParentKey];
- if (!property) return nil;
-
- GTMSessionUploadFetcher *uploadFetcher = property.nonretainedObjectValue;
-
- GTMSESSION_ASSERT_DEBUG([uploadFetcher isKindOfClass:[GTMSessionUploadFetcher class]],
- @"Unexpected parent upload fetcher class: %@", [uploadFetcher class]);
- return uploadFetcher;
-}
-
-@end
diff --git a/Old My Mind/Pods/GoogleToolboxForMac/DebugUtils/GTMDebugSelectorValidation.h b/Old My Mind/Pods/GoogleToolboxForMac/DebugUtils/GTMDebugSelectorValidation.h
deleted file mode 100755
index 2396524..0000000
--- a/Old My Mind/Pods/GoogleToolboxForMac/DebugUtils/GTMDebugSelectorValidation.h
+++ /dev/null
@@ -1,100 +0,0 @@
-//
-// GTMDebugSelectorValidation.h
-//
-// This file should only be included within an implimation file. In any
-// function that takes an object and selector to invoke, you should call:
-//
-// GTMAssertSelectorNilOrImplementedWithArguments(obj, sel, @encode(arg1type), ..., NULL)
-// or
-// GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(obj, sel, @encode(returnType), @encode(arg1type), ..., NULL)
-//
-// This will then validate that the selector is defined and using the right
-// type(s), this can help catch errors much earlier then waiting for the
-// selector to actually fire (and in the case of error selectors, might never
-// really be tested until in the field).
-//
-// Copyright 2007-2008 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy
-// of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-//
-
-#if DEBUG
-
-#import
-#import "GTMDefines.h"
-
-static void GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(id obj, SEL sel, const char *retType, ...) {
-
- // verify that the object's selector is implemented with the proper
- // number and type of arguments
- va_list argList;
- va_start(argList, retType);
-
- if (obj && sel) {
- // check that the selector is implemented
- _GTMDevAssert([obj respondsToSelector:sel],
- @"\"%@\" selector \"%@\" is unimplemented or misnamed",
- NSStringFromClass([obj class]),
- NSStringFromSelector(sel));
-
- const char *expectedArgType;
- NSUInteger argCount = 2; // skip self and _cmd
- NSMethodSignature *sig = [obj methodSignatureForSelector:sel];
-
- // check that each expected argument is present and of the correct type
- while ((expectedArgType = va_arg(argList, const char*)) != 0) {
-
- if ([sig numberOfArguments] > argCount) {
- const char *foundArgType = [sig getArgumentTypeAtIndex:argCount];
-
- _GTMDevAssert(0 == strncmp(foundArgType, expectedArgType, strlen(expectedArgType)),
- @"\"%@\" selector \"%@\" argument %u should be type %s",
- NSStringFromClass([obj class]),
- NSStringFromSelector(sel),
- (uint32_t)(argCount - 2),
- expectedArgType);
- }
- argCount++;
- }
-
- // check that the proper number of arguments are present in the selector
- _GTMDevAssert(argCount == [sig numberOfArguments],
- @"\"%@\" selector \"%@\" should have %u arguments",
- NSStringFromClass([obj class]),
- NSStringFromSelector(sel),
- (uint32_t)(argCount - 2));
-
- // if asked, validate the return type
- if (retType && (strcmp("gtm_skip_return_test", retType) != 0)) {
- const char *foundRetType = [sig methodReturnType];
- _GTMDevAssert(0 == strncmp(foundRetType, retType, strlen(retType)),
- @"\"%@\" selector \"%@\" return type should be type %s",
- NSStringFromClass([obj class]),
- NSStringFromSelector(sel),
- retType);
- }
- }
-
- va_end(argList);
-}
-
-#define GTMAssertSelectorNilOrImplementedWithArguments(obj, sel, ...) \
- GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments((obj), (sel), "gtm_skip_return_test", __VA_ARGS__)
-
-#else // DEBUG
-
-// make it go away if not debug
-#define GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(obj, sel, retType, ...) do { } while (0)
-#define GTMAssertSelectorNilOrImplementedWithArguments(obj, sel, ...) do { } while (0)
-
-#endif // DEBUG
diff --git a/Old My Mind/Pods/GoogleToolboxForMac/DebugUtils/GTMDebugThreadValidation.h b/Old My Mind/Pods/GoogleToolboxForMac/DebugUtils/GTMDebugThreadValidation.h
deleted file mode 100755
index 3f50f17..0000000
--- a/Old My Mind/Pods/GoogleToolboxForMac/DebugUtils/GTMDebugThreadValidation.h
+++ /dev/null
@@ -1,44 +0,0 @@
-//
-// GTMDebugThreadValidation.h
-//
-// Copyright 2016 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy
-// of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-//
-
-#import "GTMDefines.h"
-#import
-
-// GTMCheckCurrentQueue, GTMIsCurrentQueue
-//
-// GTMCheckCurrentQueue takes a target queue and uses _GTMDevAssert to
-// report if that is not the currently executing queue.
-//
-// GTMIsCurrentQueue takes a target queue and returns true if the target queue
-// is the currently executing dispatch queue. This can be passed to another
-// assertion call in debug builds; it should never be used in release code.
-//
-// The dispatch queue must have a label.
-#define GTMCheckCurrentQueue(targetQueue) \
- _GTMDevAssert(GTMIsCurrentQueue(targetQueue), \
- @"Current queue is %s (expected %s)", \
- _GTMQueueName(DISPATCH_CURRENT_QUEUE_LABEL), \
- _GTMQueueName(targetQueue))
-
-#define GTMIsCurrentQueue(targetQueue) \
- (strcmp(_GTMQueueName(DISPATCH_CURRENT_QUEUE_LABEL), \
- _GTMQueueName(targetQueue)) == 0)
-
-#define _GTMQueueName(queue) \
- (strlen(dispatch_queue_get_label(queue)) > 0 ? \
- dispatch_queue_get_label(queue) : "unnamed")
diff --git a/Old My Mind/Pods/GoogleToolboxForMac/DebugUtils/GTMMethodCheck.h b/Old My Mind/Pods/GoogleToolboxForMac/DebugUtils/GTMMethodCheck.h
deleted file mode 100755
index 9fad81d..0000000
--- a/Old My Mind/Pods/GoogleToolboxForMac/DebugUtils/GTMMethodCheck.h
+++ /dev/null
@@ -1,69 +0,0 @@
-//
-// GTMMethodCheck.h
-//
-// Copyright 2006-2016 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy
-// of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-//
-
-#import
-#import
-#import
-
-/// A macro for enforcing debug time checks to make sure all required methods are linked in
-//
-// When using categories, it can be very easy to forget to include the
-// implementation of a category.
-// Let's say you had a class foo that depended on method bar of class baz, and
-// method bar was implemented as a member of a category.
-// You could add the following code:
-//
-// GTM_METHOD_CHECK(baz, bar)
-//
-// and the code would check to make sure baz was implemented just before main
-// was called. This works for both dynamic libraries, and executables.
-//
-//
-// This is not compiled into release builds.
-
-#ifdef DEBUG
-
-// This is the "magic".
-// A) we need a multi layer define here so that the preprocessor expands
-// __LINE__ the way we want it. We need __LINE__ so that each of our
-// GTM_METHOD_CHECKs generates a unique function name.
-#define GTM_METHOD_CHECK(class, method) GTM_METHOD_CHECK_INNER(class, method, __LINE__)
-#define GTM_METHOD_CHECK_INNER(class, method, line) \
- GTM_METHOD_CHECK_INNER_INNER(class, method, line)
-
-// B) define a function that is called at startup to check that |class| has an
-// implementation for |method| (either a class method or an instance method).
-#define GTM_METHOD_CHECK_INNER_INNER(class, method, line) \
-__attribute__ ((constructor, visibility("hidden"))) \
- static void xxGTMMethodCheckMethod ## class ## line () { \
- @autoreleasepool { \
- if (![class instancesRespondToSelector:@selector(method)] \
- && ![class respondsToSelector:@selector(method)]) { \
- fprintf(stderr, "%s:%d: error: We need method '%s' to be linked in for class '%s'\n", \
- __FILE__, line, #method, #class); \
- exit(EX_SOFTWARE); \
- } \
- } \
-}
-
-#else // DEBUG
-
-// Do nothing in release.
-#define GTM_METHOD_CHECK(class, method)
-
-#endif // DEBUG
diff --git a/Old My Mind/Pods/GoogleToolboxForMac/Foundation/GTMLogger.h b/Old My Mind/Pods/GoogleToolboxForMac/Foundation/GTMLogger.h
deleted file mode 100755
index 16f0eaf..0000000
--- a/Old My Mind/Pods/GoogleToolboxForMac/Foundation/GTMLogger.h
+++ /dev/null
@@ -1,508 +0,0 @@
-//
-// GTMLogger.h
-//
-// Copyright 2007-2008 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy
-// of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-//
-
-// Key Abstractions
-// ----------------
-//
-// This file declares multiple classes and protocols that are used by the
-// GTMLogger logging system. The 4 main abstractions used in this file are the
-// following:
-//
-// * logger (GTMLogger) - The main logging class that users interact with. It
-// has methods for logging at different levels and uses a log writer, a log
-// formatter, and a log filter to get the job done.
-//
-// * log writer (GTMLogWriter) - Writes a given string to some log file, where
-// a "log file" can be a physical file on disk, a POST over HTTP to some URL,
-// or even some in-memory structure (e.g., a ring buffer).
-//
-// * log formatter (GTMLogFormatter) - Given a format string and arguments as
-// a va_list, returns a single formatted NSString. A "formatted string" could
-// be a string with the date prepended, a string with values in a CSV format,
-// or even a string of XML.
-//
-// * log filter (GTMLogFilter) - Given a formatted log message as an NSString
-// and the level at which the message is to be logged, this class will decide
-// whether the given message should be logged or not. This is a flexible way
-// to filter out messages logged at a certain level, messages that contain
-// certain text, or filter nothing out at all. This gives the caller the
-// flexibility to dynamically enable debug logging in Release builds.
-//
-// This file also declares some classes to handle the common log writer, log
-// formatter, and log filter cases. Callers can also create their own writers,
-// formatters, and filters and they can even build them on top of the ones
-// declared here. Keep in mind that your custom writer/formatter/filter may be
-// called from multiple threads, so it must be thread-safe.
-
-#import
-#import "GTMDefines.h"
-
-// Predeclaration of used protocols that are declared later in this file.
-@protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter;
-
-// GTMLogger
-//
-// GTMLogger is the primary user-facing class for an object-oriented logging
-// system. It is built on the concept of log formatters (GTMLogFormatter), log
-// writers (GTMLogWriter), and log filters (GTMLogFilter). When a message is
-// sent to a GTMLogger to log a message, the message is formatted using the log
-// formatter, then the log filter is consulted to see if the message should be
-// logged, and if so, the message is sent to the log writer to be written out.
-//
-// GTMLogger is intended to be a flexible and thread-safe logging solution. Its
-// flexibility comes from the fact that GTMLogger instances can be customized
-// with user defined formatters, filters, and writers. And these writers,
-// filters, and formatters can be combined, stacked, and customized in arbitrary
-// ways to suit the needs at hand. For example, multiple writers can be used at
-// the same time, and a GTMLogger instance can even be used as another
-// GTMLogger's writer. This allows for arbitrarily deep logging trees.
-//
-// A standard GTMLogger uses a writer that sends messages to standard out, a
-// formatter that smacks a timestamp and a few other bits of interesting
-// information on the message, and a filter that filters out debug messages from
-// release builds. Using the standard log settings, a log message will look like
-// the following:
-//
-// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] foo=
-//
-// The output contains the date and time of the log message, the name of the
-// process followed by its process ID/thread ID, the log level at which the
-// message was logged (in the previous example the level was 1:
-// kGTMLoggerLevelDebug), and finally, the user-specified log message itself (in
-// this case, the log message was @"foo=%@", foo).
-//
-// Multiple instances of GTMLogger can be created, each configured their own
-// way. Though GTMLogger is not a singleton (in the GoF sense), it does provide
-// access to a shared (i.e., globally accessible) GTMLogger instance. This makes
-// it convenient for all code in a process to use the same GTMLogger instance.
-// The shared GTMLogger instance can also be configured in an arbitrary, and
-// these configuration changes will affect all code that logs through the shared
-// instance.
-
-//
-// Log Levels
-// ----------
-// GTMLogger has 3 different log levels: Debug, Info, and Error. GTMLogger
-// doesn't take any special action based on the log level; it simply forwards
-// this information on to formatters, filters, and writers, each of which may
-// optionally take action based on the level. Since log level filtering is
-// performed at runtime, log messages are typically not filtered out at compile
-// time. The exception to this rule is that calls to the GTMLoggerDebug() macro
-// *ARE* filtered out of non-DEBUG builds. This is to be backwards compatible
-// with behavior that many developers are currently used to. Note that this
-// means that GTMLoggerDebug(@"hi") will be compiled out of Release builds, but
-// [[GTMLogger sharedLogger] logDebug:@"hi"] will NOT be compiled out.
-//
-// Standard loggers are created with the GTMLogLevelFilter log filter, which
-// filters out certain log messages based on log level, and some other settings.
-//
-// In addition to the -logDebug:, -logInfo:, and -logError: methods defined on
-// GTMLogger itself, there are also C macros that make usage of the shared
-// GTMLogger instance very convenient. These macros are:
-//
-// GTMLoggerDebug(...)
-// GTMLoggerInfo(...)
-// GTMLoggerError(...)
-//
-// Again, a notable feature of these macros is that GTMLogDebug() calls *will be
-// compiled out of non-DEBUG builds*.
-//
-// Standard Loggers
-// ----------------
-// GTMLogger has the concept of "standard loggers". A standard logger is simply
-// a logger that is pre-configured with some standard/common writer, formatter,
-// and filter combination. Standard loggers are created using the creation
-// methods beginning with "standard". The alternative to a standard logger is a
-// regular logger, which will send messages to stdout, with no special
-// formatting, and no filtering.
-//
-// How do I use GTMLogger?
-// ----------------------
-// The typical way you will want to use GTMLogger is to simply use the
-// GTMLogger*() macros for logging from code. That way we can easily make
-// changes to the GTMLogger class and simply update the macros accordingly. Only
-// your application startup code (perhaps, somewhere in main()) should use the
-// GTMLogger class directly in order to configure the shared logger, which all
-// of the code using the macros will be using. Again, this is just the typical
-// situation.
-//
-// To be complete, there are cases where you may want to use GTMLogger directly,
-// or even create separate GTMLogger instances for some reason. That's fine,
-// too.
-//
-// Examples
-// --------
-// The following show some common GTMLogger use cases.
-//
-// 1. You want to log something as simply as possible. Also, this call will only
-// appear in debug builds. In non-DEBUG builds it will be completely removed.
-//
-// GTMLoggerDebug(@"foo = %@", foo);
-//
-// 2. The previous example is similar to the following. The major difference is
-// that the previous call (example 1) will be compiled out of Release builds
-// but this statement will not be compiled out.
-//
-// [[GTMLogger sharedLogger] logDebug:@"foo = %@", foo];
-//
-// 3. Send all logging output from the shared logger to a file. We do this by
-// creating an NSFileHandle for writing associated with a file, and setting
-// that file handle as the logger's writer.
-//
-// NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log"
-// create:YES];
-// [[GTMLogger sharedLogger] setWriter:f];
-// GTMLoggerError(@"hi"); // This will be sent to /tmp/f.log
-//
-// 4. Create a new GTMLogger that will log to a file. This example differs from
-// the previous one because here we create a new GTMLogger that is different
-// from the shared logger.
-//
-// GTMLogger *logger = [GTMLogger standardLoggerWithPath:@"/tmp/temp.log"];
-// [logger logInfo:@"hi temp log file"];
-//
-// 5. Create a logger that writes to stdout and does NOT do any formatting to
-// the log message. This might be useful, for example, when writing a help
-// screen for a command-line tool to standard output.
-//
-// GTMLogger *logger = [GTMLogger logger];
-// [logger logInfo:@"%@ version 0.1 usage", progName];
-//
-// 6. Send log output to stdout AND to a log file. The trick here is that
-// NSArrays function as composite log writers, which means when an array is
-// set as the log writer, it forwards all logging messages to all of its
-// contained GTMLogWriters.
-//
-// // Create array of GTMLogWriters
-// NSArray *writers = [NSArray arrayWithObjects:
-// [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" create:YES],
-// [NSFileHandle fileHandleWithStandardOutput], nil];
-//
-// GTMLogger *logger = [GTMLogger standardLogger];
-// [logger setWriter:writers];
-// [logger logInfo:@"hi"]; // Output goes to stdout and /tmp/f.log
-//
-// For futher details on log writers, formatters, and filters, see the
-// documentation below.
-//
-// NOTE: GTMLogger is application level logging. By default it does nothing
-// with _GTMDevLog/_GTMDevAssert (see GTMDefines.h). An application can choose
-// to bridge _GTMDevLog/_GTMDevAssert to GTMLogger by providing macro
-// definitions in its prefix header (see GTMDefines.h for how one would do
-// that).
-//
-@interface GTMLogger : NSObject {
- @private
- id writer_;
- id formatter_;
- id filter_;
-}
-
-//
-// Accessors for the shared logger instance
-//
-
-// Returns a shared/global standard GTMLogger instance. Callers should typically
-// use this method to get a GTMLogger instance, unless they explicitly want
-// their own instance to configure for their own needs. This is the only method
-// that returns a shared instance; all the rest return new GTMLogger instances.
-+ (id)sharedLogger;
-
-// Sets the shared logger instance to |logger|. Future calls to +sharedLogger
-// will return |logger| instead.
-+ (void)setSharedLogger:(GTMLogger *)logger;
-
-//
-// Creation methods
-//
-
-// Returns a new autoreleased GTMLogger instance that will log to stdout, using
-// the GTMLogStandardFormatter, and the GTMLogLevelFilter filter.
-+ (id)standardLogger;
-
-// Same as +standardLogger, but logs to stderr.
-+ (id)standardLoggerWithStderr;
-
-// Same as +standardLogger but levels >= kGTMLoggerLevelError are routed to
-// stderr, everything else goes to stdout.
-+ (id)standardLoggerWithStdoutAndStderr;
-
-// Returns a new standard GTMLogger instance with a log writer that will
-// write to the file at |path|, and will use the GTMLogStandardFormatter and
-// GTMLogLevelFilter classes. If |path| does not exist, it will be created.
-+ (id)standardLoggerWithPath:(NSString *)path;
-
-// Returns an autoreleased GTMLogger instance that will use the specified
-// |writer|, |formatter|, and |filter|.
-+ (id)loggerWithWriter:(id)writer
- formatter:(id)formatter
- filter:(id)filter;
-
-// Returns an autoreleased GTMLogger instance that logs to stdout, with the
-// basic formatter, and no filter. The returned logger differs from the logger
-// returned by +standardLogger because this one does not do any filtering and
-// does not do any special log formatting; this is the difference between a
-// "regular" logger and a "standard" logger.
-+ (id)logger;
-
-// Designated initializer. This method returns a GTMLogger initialized with the
-// specified |writer|, |formatter|, and |filter|. See the setter methods below
-// for what values will be used if nil is passed for a parameter.
-- (id)initWithWriter:(id)writer
- formatter:(id)formatter
- filter:(id)filter;
-
-//
-// Logging methods
-//
-
-// Logs a message at the debug level (kGTMLoggerLevelDebug).
-- (void)logDebug:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
-// Logs a message at the info level (kGTMLoggerLevelInfo).
-- (void)logInfo:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
-// Logs a message at the error level (kGTMLoggerLevelError).
-- (void)logError:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
-// Logs a message at the assert level (kGTMLoggerLevelAssert).
-- (void)logAssert:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
-
-
-//
-// Accessors
-//
-
-// Accessor methods for the log writer. If the log writer is set to nil,
-// [NSFileHandle fileHandleWithStandardOutput] is used.
-- (id)writer;
-- (void)setWriter:(id)writer;
-
-// Accessor methods for the log formatter. If the log formatter is set to nil,
-// GTMLogBasicFormatter is used. This formatter will format log messages in a
-// plain printf style.
-- (id)formatter;
-- (void)setFormatter:(id)formatter;
-
-// Accessor methods for the log filter. If the log filter is set to nil,
-// GTMLogNoFilter is used, which allows all log messages through.
-- (id