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) {
118117static 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
0 commit comments