Skip to content

Commit a553f8e

Browse files
committed
Merge pull request #48 from thomasvl/docs_n_threading
Comment doc updates and threading fixup
2 parents d9f49be + bfd1a1b commit a553f8e

12 files changed

+193
-89
lines changed

Source/GTMMIMEDocument.m

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -318,16 +318,14 @@ + (NSData *)dataWithHeaders:(NSDictionary *)headers {
318318

319319
#if DEBUG
320320
// Look for troublesome characters in the header keys & values.
321-
static NSCharacterSet *badChars = nil;
322-
if (!badChars) {
323-
badChars = [NSCharacterSet characterSetWithCharactersInString:@":\r\n"];
324-
}
321+
NSCharacterSet *badKeyChars = [NSCharacterSet characterSetWithCharactersInString:@":\r\n"];
322+
NSCharacterSet *badValueChars = [NSCharacterSet characterSetWithCharactersInString:@"\r\n"];
325323

326-
NSRange badRange = [key rangeOfCharacterFromSet:badChars];
327-
NSAssert1(badRange.location == NSNotFound, @"invalid key: %@", key);
324+
NSRange badRange = [key rangeOfCharacterFromSet:badKeyChars];
325+
NSAssert(badRange.location == NSNotFound, @"invalid key: %@", key);
328326

329-
badRange = [value rangeOfCharacterFromSet:badChars];
330-
NSAssert1(badRange.location == NSNotFound, @"invalid value: %@", value);
327+
badRange = [value rangeOfCharacterFromSet:badValueChars];
328+
NSAssert(badRange.location == NSNotFound, @"invalid value: %@", value);
331329
#endif
332330

333331
[headerString appendFormat:@"%@: %@\r\n", key, value];

Source/GTMSessionFetcher.h

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,13 @@
329329
#endif // __has_feature(nullability)
330330
#endif // GTM_NULLABLE
331331

332+
#if ((!TARGET_OS_IPHONE && defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12) \
333+
|| (TARGET_OS_IPHONE && defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0))
334+
#define GTMSESSION_DEPRECATE_ON_2016_SDKS __attribute__((deprecated))
335+
#else
336+
#define GTMSESSION_DEPRECATE_ON_2016_SDKS
337+
#endif
338+
332339
#ifndef GTM_DECLARE_GENERICS
333340
#if __has_feature(objc_generics) \
334341
&& ((!TARGET_OS_IPHONE && defined(MAC_OS_X_VERSION_10_11) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_11) \
@@ -707,20 +714,40 @@ NSData * GTM_NULLABLE_TYPE GTMDataFromInputStream(NSInputStream *inputStream, NS
707714
+ (GTM_NSArrayOf(GTMSessionFetcher *) *)fetchersForBackgroundSessions;
708715

709716
// Designated initializer.
717+
//
718+
// Applications should create fetchers with a "fetcherWith..." method on a fetcher
719+
// service or a class method, not with this initializer.
720+
//
721+
// The configuration should typically be nil. Applications needing to customize
722+
// the configuration may do so by setting the configurationBlock property.
710723
- (instancetype)initWithRequest:(GTM_NULLABLE NSURLRequest *)request
711724
configuration:(GTM_NULLABLE NSURLSessionConfiguration *)configuration;
712725

713-
// The fetcher's request
726+
// The fetcher's request. This may not be set after beginFetch has been invoked. The request
727+
// may change due to redirects.
728+
@property(strong, GTM_NULLABLE) NSURLRequest *request;
729+
730+
// Set a header field value on the request. Header field value changes will not
731+
// affect a fetch after the fetch has begun.
732+
- (void)setRequestValue:(GTM_NULLABLE NSString *)value forHTTPHeaderField:(NSString *)field;
733+
734+
// The fetcher's request (deprecated.)
714735
//
715-
// The underlying request is mutable and may be modified by the caller. Request changes will not
716-
// affect a fetch after it has begun.
717-
@property(readonly, GTM_NULLABLE) NSMutableURLRequest *mutableRequest;
736+
// Exposing a mutable object in the interface was convenient but a bad design decision due
737+
// to thread-safety requirements. Clients should use the request property and
738+
// setRequestValue:forHTTPHeaderField: instead.
739+
@property(readonly, GTM_NULLABLE) NSMutableURLRequest *mutableRequest GTMSESSION_DEPRECATE_ON_2016_SDKS;
718740

719741
// Data used for resuming a download task.
720742
@property(readonly, GTM_NULLABLE) NSData *downloadResumeData;
721743

722744
// The configuration; this must be set before the fetch begins. If no configuration is
723745
// set or inherited from the fetcher service, then the fetcher uses an ephemeral config.
746+
//
747+
// NOTE: This property should typically be nil. Applications needing to customize
748+
// the configuration should do so by setting the configurationBlock property.
749+
// That allows the fetcher to pick an appropriate base configuration, with the
750+
// application setting only the configuration properties it needs to customize.
724751
@property(strong, GTM_NULLABLE) NSURLSessionConfiguration *configuration;
725752

726753
// A block the client may use to customize the configuration used to create the session.

Source/GTMSessionFetcher.m

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@
8080
@interface GTMSessionFetcher ()
8181

8282
@property(strong, readwrite, GTM_NULLABLE) NSData *downloadedData;
83-
@property(strong, readwrite, GTM_NULLABLE) NSMutableURLRequest *mutableRequest;
8483
@property(strong, readwrite, GTM_NULLABLE) NSData *downloadResumeData;
8584

8685
#if GTM_BACKGROUND_TASK_FETCHING
@@ -118,7 +117,7 @@ static BOOL IsLocalhost(NSString * GTM_NULLABLE_TYPE host) {
118117
static GTMSessionFetcherTestBlock GTM_NULLABLE_TYPE gGlobalTestBlock;
119118

120119
@implementation GTMSessionFetcher {
121-
NSMutableURLRequest *_request; // may change due to redirects
120+
NSMutableURLRequest *_request; // after beginFetch, changed only in delegate callbacks
122121
BOOL _useUploadTask; // immutable after beginFetch
123122
NSURL *_bodyFileURL; // immutable after beginFetch
124123
GTMSessionFetcherBodyStreamProvider _bodyStreamProvider; // immutable after beginFetch
@@ -322,7 +321,7 @@ - (id)copyWithZone:(NSZone *)zone {
322321
}
323322

324323
- (NSString *)description {
325-
NSString *requestStr = self.mutableRequest.URL.description;
324+
NSString *requestStr = self.request.URL.description;
326325
if (requestStr.length == 0) {
327326
if (self.downloadResumeData.length > 0) {
328327
requestStr = @"<download resume data>";
@@ -351,6 +350,8 @@ - (void)dealloc {
351350
// for the duration of the fetch connection.
352351

353352
- (void)beginFetchWithCompletionHandler:(GTM_NULLABLE GTMSessionFetcherCompletionHandler)handler {
353+
GTMSessionCheckNotSynchronized(self);
354+
354355
_completionHandler = [handler copy];
355356

356357
// The user may have called setDelegate: earlier if they want to use other
@@ -381,6 +382,8 @@ - (GTMSessionFetcherCompletionHandler)completionHandlerWithTarget:(GTM_NULLABLE_
381382

382383
- (void)beginFetchWithDelegate:(GTM_NULLABLE_TYPE id)target
383384
didFinishSelector:(GTM_NULLABLE_TYPE SEL)finishedSelector {
385+
GTMSessionCheckNotSynchronized(self);
386+
384387
GTMSessionFetcherCompletionHandler handler = [self completionHandlerWithTarget:target
385388
didFinishSelector:finishedSelector];
386389
[self beginFetchWithCompletionHandler:handler];
@@ -389,8 +392,9 @@ - (void)beginFetchWithDelegate:(GTM_NULLABLE_TYPE id)target
389392
- (void)beginFetchMayDelay:(BOOL)mayDelay
390393
mayAuthorize:(BOOL)mayAuthorize {
391394
// This is the internal entry point for re-starting fetches.
395+
GTMSessionCheckNotSynchronized(self);
392396

393-
NSMutableURLRequest *fetchRequest = self.mutableRequest;
397+
NSMutableURLRequest *fetchRequest = _request; // The request property is now externally immutable.
394398
NSURL *fetchRequestURL = fetchRequest.URL;
395399
NSString *priorSessionIdentifier = self.sessionIdentifier;
396400

@@ -671,19 +675,20 @@ - (void)beginFetchMayDelay:(BOOL)mayDelay
671675
BOOL needsUploadTask = (self.useUploadTask || self.bodyFileURL || self.bodyStreamProvider);
672676
if (_bodyData || self.bodyStreamProvider || fetchRequest.HTTPBodyStream) {
673677
if (isEffectiveHTTPGet) {
674-
[fetchRequest setHTTPMethod:@"POST"];
678+
fetchRequest.HTTPMethod = @"POST";
675679
isEffectiveHTTPGet = NO;
676680
}
677681

678682
if (_bodyData) {
679683
if (!needsUploadTask) {
680-
[fetchRequest setHTTPBody:_bodyData];
684+
fetchRequest.HTTPBody = _bodyData;
681685
}
682686
#if !STRIP_GTM_FETCH_LOGGING
683687
} else if (fetchRequest.HTTPBodyStream) {
684688
if ([self respondsToSelector:@selector(loggedInputStreamForInputStream:)]) {
685-
fetchRequest.HTTPBodyStream = [self performSelector:@selector(loggedInputStreamForInputStream:)
686-
withObject:fetchRequest.HTTPBodyStream];
689+
fetchRequest.HTTPBodyStream =
690+
[self performSelector:@selector(loggedInputStreamForInputStream:)
691+
withObject:fetchRequest.HTTPBodyStream];
687692
}
688693
#endif
689694
}
@@ -1502,7 +1507,8 @@ - (void)authorizeRequest {
15021507
SEL asyncAuthSel = @selector(authorizeRequest:delegate:didFinishSelector:);
15031508
if ([authorizer respondsToSelector:asyncAuthSel]) {
15041509
SEL callbackSel = @selector(authorizer:request:finishedWithError:);
1505-
[authorizer authorizeRequest:self.mutableRequest
1510+
NSMutableURLRequest *mutableRequest = [self.request mutableCopy];
1511+
[authorizer authorizeRequest:mutableRequest
15061512
delegate:self
15071513
didFinishSelector:callbackSel];
15081514
} else {
@@ -1516,12 +1522,17 @@ - (void)authorizeRequest {
15161522
}
15171523

15181524
- (void)authorizer:(id<GTMFetcherAuthorizationProtocol>)auth
1519-
request:(NSMutableURLRequest *)request
1525+
request:(NSMutableURLRequest *)authorizedRequest
15201526
finishedWithError:(NSError *)error {
1527+
GTMSessionCheckNotSynchronized(self);
1528+
15211529
if (error != nil) {
15221530
// We can't fetch without authorization
15231531
[self failToBeginFetchWithError:error];
15241532
} else {
1533+
@synchronized(self) {
1534+
_request = authorizedRequest;
1535+
}
15251536
[self beginFetchMayDelay:NO
15261537
mayAuthorize:NO];
15271538
}
@@ -1932,7 +1943,7 @@ - (void)URLSession:(NSURLSession *)session
19321943
}
19331944
if (redirectRequest && redirectResponse) {
19341945
// Copy the original request, including the body.
1935-
NSMutableURLRequest *originalRequest = self.mutableRequest;
1946+
NSURLRequest *originalRequest = self.request;
19361947
NSMutableURLRequest *newRequest = [originalRequest mutableCopy];
19371948

19381949
// Disallow scheme changes (say, from https to http).
@@ -2624,11 +2635,13 @@ - (void)URLSession:(NSURLSession *)session
26242635
// header.
26252636
if ((status == 403) && self.usingBackgroundSession) {
26262637
NSURL *redirectURL = self.response.URL;
2627-
NSMutableURLRequest *request = self.mutableRequest;
2638+
NSURLRequest *request = self.request;
26282639
if (![request.URL isEqual:redirectURL]) {
26292640
NSString *authorizationHeader = [request.allHTTPHeaderFields objectForKey:@"Authorization"];
26302641
if (authorizationHeader != nil) {
2631-
request.URL = redirectURL;
2642+
NSMutableURLRequest *mutableRequest = [request mutableCopy];
2643+
mutableRequest.URL = redirectURL;
2644+
[self updateMutableRequest:mutableRequest];
26322645
// Avoid assuming the session is still valid.
26332646
self.session = nil;
26342647
forceAssumeRetry = YES;
@@ -2904,7 +2917,7 @@ - (void)shouldRetryNowForStatus:(NSInteger)status
29042917
if (hasPrimed) {
29052918
shouldRetryForAuthRefresh = YES;
29062919
_hasAttemptedAuthRefresh = YES;
2907-
[self.mutableRequest setValue:nil forHTTPHeaderField:@"Authorization"];
2920+
[self updateRequestValue:nil forHTTPHeaderField:@"Authorization"];
29082921
}
29092922

29102923
@synchronized(self) {
@@ -3278,19 +3291,46 @@ + (GTMSessionFetcherSystemCompletionHandler GTM_NULLABLE_TYPE)systemCompletionHa
32783291
skipBackgroundTask = _skipBackgroundTask;
32793292
#endif
32803293

3281-
- (GTM_NULLABLE NSMutableURLRequest *)mutableRequest {
3294+
- (GTM_NULLABLE NSURLRequest *)request {
32823295
@synchronized(self) {
32833296
GTMSessionMonitorSynchronized(self);
32843297

3285-
return _request;
3298+
return [_request copy];
3299+
} // @synchronized(self)
3300+
}
3301+
3302+
- (void)setRequest:(GTM_NULLABLE NSURLRequest *)request {
3303+
@synchronized(self) {
3304+
GTMSessionMonitorSynchronized(self);
3305+
3306+
if (![self isFetchingUnsynchronized]) {
3307+
_request = [request mutableCopy];
3308+
} else {
3309+
GTMSESSION_ASSERT_DEBUG(0, @"request may not be set after beginFetch has been invoked");
3310+
}
32863311
} // @synchronized(self)
32873312
}
32883313

3289-
- (GTM_NULLABLE NSMutableURLRequest *)mutableRequestUnsynchronized {
3314+
- (GTM_NULLABLE NSMutableURLRequest *)mutableRequestForTesting {
3315+
// Allow tests only to modify the request, useful during retries.
32903316
return _request;
32913317
}
32923318

3319+
- (GTM_NULLABLE NSMutableURLRequest *)mutableRequest {
3320+
@synchronized(self) {
3321+
GTMSessionMonitorSynchronized(self);
3322+
3323+
GTMSESSION_LOG_DEBUG(@"[GTMSessionFetcher mutableRequest] is deprecated; use -request or"
3324+
@" -setRequestVaue:forHTTPHeaderField:");
3325+
3326+
return _request;
3327+
} // @synchronized(self)
3328+
}
3329+
32933330
- (void)setMutableRequest:(GTM_NULLABLE NSMutableURLRequest *)request {
3331+
GTMSESSION_LOG_DEBUG(@"[GTMSessionFetcher setMutableRequest:] is deprecated; use -request or"
3332+
@" -setRequestVaue:forHTTPHeaderField:");
3333+
32943334
GTMSESSION_ASSERT_DEBUG(![self isFetching],
32953335
@"mutableRequest should not change after beginFetch has been invoked");
32963336
[self updateMutableRequest:request];
@@ -3305,7 +3345,26 @@ - (void)updateMutableRequest:(GTM_NULLABLE NSMutableURLRequest *)request {
33053345
} // @synchronized(self)
33063346
}
33073347

3308-
- (void)setResponse:(GTM_NULLABLE NSURLResponse *)response {
3348+
// Set a header field value on the request. Header field value changes will not
3349+
// affect a fetch after the fetch has begun.
3350+
- (void)setRequestValue:(GTM_NULLABLE NSString *)value forHTTPHeaderField:(NSString *)field {
3351+
if (![self isFetching]) {
3352+
[self updateRequestValue:value forHTTPHeaderField:field];
3353+
} else {
3354+
GTMSESSION_ASSERT_DEBUG(0, @"request may not be set after beginFetch has been invoked");
3355+
}
3356+
}
3357+
3358+
// Internal method for updating request headers.
3359+
- (void)updateRequestValue:(GTM_NULLABLE NSString *)value forHTTPHeaderField:(NSString *)field {
3360+
@synchronized(self) {
3361+
GTMSessionMonitorSynchronized(self);
3362+
3363+
[_request setValue:value forHTTPHeaderField:field];
3364+
} // @synchronized(self)
3365+
}
3366+
3367+
- (void)setResponse:(NSURLResponse * GTM_NULLABLE_TYPE)response {
33093368
@synchronized(self) {
33103369
GTMSessionMonitorSynchronized(self);
33113370

Source/GTMSessionFetcherLogging.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ - (void)logFetchWithError:(NSError *)error {
540540
[outputHTML appendFormat:@"elapsed: %5.3fsec<br>", elapsed];
541541

542542
// write the request URL
543-
NSURLRequest *request = self.mutableRequest;
543+
NSURLRequest *request = self.request;
544544
NSString *requestMethod = request.HTTPMethod;
545545
NSURL *requestURL = request.URL;
546546

@@ -595,7 +595,7 @@ - (void)logFetchWithError:(NSError *)error {
595595
} else {
596596
bodyData = self.bodyData;
597597
if (bodyData == nil) {
598-
bodyData = self.mutableRequest.HTTPBody;
598+
bodyData = self.request.HTTPBody;
599599
}
600600
}
601601
uint64_t bodyDataLength = bodyData.length;

Source/GTMSessionFetcherService.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ extern NSString *const kGTMSessionFetcherServiceSessionKey;
111111
- (GTMSessionFetcher *)fetcherWithRequest:(NSURLRequest *)request;
112112
- (GTMSessionFetcher *)fetcherWithURL:(NSURL *)requestURL;
113113
- (GTMSessionFetcher *)fetcherWithURLString:(NSString *)requestURLString;
114+
115+
// Common method for fetcher creation.
116+
//
117+
// -fetcherWithRequest:fetcherClass: may be overridden to customize creation of
118+
// fetchers. This is the ONLY method in the GTMSessionFetcher library intended to
119+
// be overridden.
114120
- (id)fetcherWithRequest:(NSURLRequest *)request
115121
fetcherClass:(Class)fetcherClass;
116122

@@ -139,7 +145,7 @@ extern NSString *const kGTMSessionFetcherServiceSessionKey;
139145
- (GTM_NULLABLE id<NSURLSessionDelegate>)sessionDelegate;
140146
- (GTM_NULLABLE NSDate *)stoppedAllFetchersDate;
141147

142-
// The testBlock can inspect its fetcher parameter's mutableRequest property to
148+
// The testBlock can inspect its fetcher parameter's request property to
143149
// determine which fetcher is being faked.
144150
@property(copy, GTM_NULLABLE) GTMSessionFetcherTestBlock testBlock;
145151

Source/GTMSessionFetcherService.m

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ - (void)dealloc {
159159

160160
#pragma mark Generate a new fetcher
161161

162+
// Clients may override this method. Clients should not override any other library methods.
162163
- (id)fetcherWithRequest:(NSURLRequest *)request
163164
fetcherClass:(Class)fetcherClass {
164165
GTMSessionFetcher *fetcher = [[fetcherClass alloc] initWithRequest:request
@@ -191,8 +192,8 @@ - (id)fetcherWithRequest:(NSURLRequest *)request
191192
NSString *userAgent = self.userAgent;
192193
if (userAgent.length > 0
193194
&& [request valueForHTTPHeaderField:@"User-Agent"] == nil) {
194-
[fetcher.mutableRequest setValue:userAgent
195-
forHTTPHeaderField:@"User-Agent"];
195+
[fetcher setRequestValue:userAgent
196+
forHTTPHeaderField:@"User-Agent"];
196197
}
197198
fetcher.testBlock = self.testBlock;
198199

@@ -302,7 +303,7 @@ - (BOOL)isDelayingFetcher:(GTMSessionFetcher *)fetcher {
302303
@synchronized(self) {
303304
GTMSessionMonitorSynchronized(self);
304305

305-
NSString *host = fetcher.mutableRequest.URL.host;
306+
NSString *host = fetcher.request.URL.host;
306307
if (host == nil) {
307308
return NO;
308309
}
@@ -315,7 +316,7 @@ - (BOOL)isDelayingFetcher:(GTMSessionFetcher *)fetcher {
315316

316317
- (BOOL)fetcherShouldBeginFetching:(GTMSessionFetcher *)fetcher {
317318
// Entry point from the fetcher
318-
NSURL *requestURL = fetcher.mutableRequest.URL;
319+
NSURL *requestURL = fetcher.request.URL;
319320
NSString *host = requestURL.host;
320321

321322
// Addresses "file:///path" case where localhost is the implicit host.
@@ -578,7 +579,7 @@ - (NSArray *)issuedFetchersWithRequestURL:(NSURL *)requestURL {
578579
NSIndexSet *indexes = [allFetchers indexesOfObjectsPassingTest:^BOOL(GTMSessionFetcher *fetcher,
579580
NSUInteger idx,
580581
BOOL *stop) {
581-
NSURL *fetcherURL = [fetcher.mutableRequest.URL absoluteURL];
582+
NSURL *fetcherURL = [fetcher.request.URL absoluteURL];
582583
return [fetcherURL isEqual:targetURL];
583584
}];
584585

0 commit comments

Comments
 (0)