-
Notifications
You must be signed in to change notification settings - Fork 151
/
Copy pathGTMSessionFetcher.h
1338 lines (1163 loc) · 60.2 KB
/
GTMSessionFetcher.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* 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.
*/
// GTMSessionFetcher is a wrapper around NSURLSession for http operations.
//
// What does this offer on top of of NSURLSession?
//
// - Block-style callbacks for useful functionality like progress rather
// than delegate methods.
// - Out-of-process uploads and downloads using NSURLSession, including
// management of fetches after relaunch.
// - Integration with GTMAppAuth for invisible management and refresh of
// authorization tokens.
// - Pretty-printed http logging.
// - Cookies handling that does not interfere with or get interfered with
// by WebKit cookies or on Mac by Safari and other apps.
// - Credentials handling for the http operation.
// - Rate-limiting and cookie grouping when fetchers are created with
// GTMSessionFetcherService.
//
// If the bodyData or bodyFileURL property is set, then a POST request is assumed.
//
// Each fetcher is assumed to be for a one-shot fetch request; don't reuse the object
// for a second fetch.
//
// The fetcher will be self-retained as long as a connection is pending.
//
// To keep user activity private, URLs must have an https scheme (unless the property
// allowedInsecureSchemes is set to permit the scheme.)
//
// Callbacks will be released when the fetch completes or is stopped, so there is no need
// to use weak self references in the callback blocks.
//
// Sample usage:
//
// _fetcherService = [[GTMSessionFetcherService alloc] init];
//
// GTMSessionFetcher *myFetcher = [_fetcherService fetcherWithURLString:myURLString];
// myFetcher.retryEnabled = YES;
// myFetcher.comment = @"First profile image";
//
// // Optionally specify a file URL or NSData for the request body to upload.
// myFetcher.bodyData = [postString dataUsingEncoding:NSUTF8StringEncoding];
//
// [myFetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error) {
// if (error != nil) {
// // Server status code or network error.
// //
// // If the domain is kGTMSessionFetcherStatusDomain then the error code
// // is a failure status from the server.
// } else {
// // Fetch succeeded.
// }
// }];
//
// There is also a beginFetch call that takes a pointer and selector for the completion handler;
// a pointer and selector is a better style when the callback is a substantial, separate method.
//
// NOTE: Fetches may retrieve data from the server even though the server
// returned an error, so the criteria for success is a non-nil error.
// The completion handler is called when the server status is >= 300 with an NSError
// having domain kGTMSessionFetcherStatusDomain and code set to the server status.
//
// Status codes are at <http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html>
//
//
// Background session support:
//
// Out-of-process uploads and downloads may be created by setting the fetcher's
// useBackgroundSession property. Data to be uploaded should be provided via
// the uploadFileURL property; the download destination should be specified with
// the destinationFileURL. NOTE: Background upload files should be in a location
// that will be valid even after the device is restarted, so the file should not
// be uploaded from a system temporary or cache directory.
//
// Background session transfers are slower, and should typically be used only
// for very large downloads or uploads (hundreds of megabytes).
//
// When background sessions are used in iOS apps, the application delegate must
// pass through the parameters from UIApplicationDelegate's
// application:handleEventsForBackgroundURLSession:completionHandler: to the
// fetcher class.
//
// When the application has been relaunched, it may also create a new fetcher
// instance to handle completion of the transfers.
//
// - (void)application:(UIApplication *)application
// handleEventsForBackgroundURLSession:(NSString *)identifier
// completionHandler:(void (^)())completionHandler {
// // Application was re-launched on completing an out-of-process download.
//
// // Pass the URLSession info related to this re-launch to the fetcher class.
// [GTMSessionFetcher application:application
// handleEventsForBackgroundURLSession:identifier
// completionHandler:completionHandler];
//
// // Get a fetcher related to this re-launch and re-hook up a completionHandler to it.
// GTMSessionFetcher *fetcher = [GTMSessionFetcher fetcherWithSessionIdentifier:identifier];
// NSURL *destinationFileURL = fetcher.destinationFileURL;
// fetcher.completionHandler = ^(NSData *data, NSError *error) {
// [self downloadCompletedToFile:destinationFileURL error:error];
// };
// }
//
//
// Threading and queue support:
//
// Networking always happens on a background thread; there is no advantage to
// changing thread or queue to create or start a fetcher.
//
// Callbacks are run on the main thread; alternatively, the app may set the
// fetcher's callbackQueue to a dispatch queue.
//
// Once the fetcher's beginFetch method has been called, the fetcher's methods and
// properties may be accessed from any thread.
//
// Downloading to disk:
//
// To have downloaded data saved directly to disk, specify a file URL for the
// destinationFileURL property.
//
// HTTP methods and headers:
//
// Alternative HTTP methods, like PUT, and custom headers can be specified by
// creating the fetcher with an appropriate NSMutableURLRequest.
//
//
// Caching:
//
// The fetcher avoids caching. That is best for API requests, but may hurt
// repeat fetches of static data. Apps may enable a persistent disk cache by
// customizing the config:
//
// fetcher.configurationBlock = ^(GTMSessionFetcher *configFetcher,
// NSURLSessionConfiguration *config) {
// config.URLCache = [NSURLCache sharedURLCache];
// };
//
// Or use the standard system config to share cookie storage with web views
// and to enable disk caching:
//
// fetcher.configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
//
//
// Cookies:
//
// There are three supported mechanisms for remembering cookies between fetches.
//
// By default, a standalone GTMSessionFetcher uses a mutable array held
// statically to track cookies for all instantiated fetchers. This avoids
// cookies being set by servers for the application from interfering with
// Safari and WebKit cookie settings, and vice versa.
// The fetcher cookies are lost when the application quits.
//
// To rely instead on WebKit's global NSHTTPCookieStorage, set the fetcher's
// cookieStorage property:
// myFetcher.cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
//
// To share cookies with other apps, use the method introduced in iOS 9/OS X 10.11:
// myFetcher.cookieStorage =
// [NSHTTPCookieStorage sharedCookieStorageForGroupContainerIdentifier:kMyCompanyContainedID];
//
// To ignore existing cookies and only have cookies related to the single fetch
// be applied, make a temporary cookie storage object:
// myFetcher.cookieStorage = [[GTMSessionCookieStorage alloc] init];
//
// Note: cookies set while following redirects will be sent to the server, as
// the redirects are followed by the fetcher.
//
// To completely disable cookies, similar to setting cookieStorageMethod to
// kGTMHTTPFetcherCookieStorageMethodNone, adjust the session configuration
// appropriately in the fetcher or fetcher service:
// fetcher.configurationBlock = ^(GTMSessionFetcher *configFetcher,
// NSURLSessionConfiguration *config) {
// config.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyNever;
// config.HTTPShouldSetCookies = NO;
// };
//
// If the fetcher is created from a GTMSessionFetcherService object
// then the cookie storage mechanism is set to use the cookie storage in the
// service object rather than the static storage. Disabling cookies in the
// session configuration set on a service object will disable cookies for all
// fetchers created from that GTMSessionFetcherService object, since the session
// configuration is propagated to the fetcher.
//
//
// Monitoring data transfers.
//
// The fetcher supports a variety of properties for progress monitoring
// progress with callback blocks.
// GTMSessionFetcherSendProgressBlock sendProgressBlock
// GTMSessionFetcherReceivedProgressBlock receivedProgressBlock
// GTMSessionFetcherDownloadProgressBlock downloadProgressBlock
//
// If supplied by the server, the anticipated total download size is available
// as [[myFetcher response] expectedContentLength] (and may be -1 for unknown
// download sizes.)
//
//
// Automatic retrying of fetches
//
// The fetcher can optionally create a timer and reattempt certain kinds of
// fetch failures (status codes 408, request timeout; 502, gateway failure;
// 503, service unavailable; 504, gateway timeout; networking errors
// NSURLErrorTimedOut and NSURLErrorNetworkConnectionLost.) The user may
// set a retry selector to customize the type of errors which will be retried.
//
// Retries are done in an exponential-backoff fashion (that is, after 1 second,
// 2, 4, 8, and so on.)
//
// Enabling automatic retries looks like this:
// myFetcher.retryEnabled = YES;
//
// With retries enabled, the completion callbacks are called only
// when no more retries will be attempted. Calling the fetcher's stopFetching
// method will terminate the retry timer, without the finished or failure
// selectors being invoked.
//
// Optionally, the client may set the maximum retry interval:
// myFetcher.maxRetryInterval = 60.0; // in seconds; default is 60 seconds
// // for downloads, 600 for uploads
//
// Servers should never send a 400 or 500 status for errors that are retryable
// by clients, as those values indicate permanent failures. In nearly all
// cases, the default standard retry behavior is correct for clients, and no
// custom client retry behavior is needed or appropriate. Servers that send
// non-retryable status codes and expect the client to retry the request are
// faulty.
//
// Still, the client may provide a block to determine if a status code or other
// error should be retried. The block returns YES to set the retry timer or NO
// to fail without additional fetch attempts.
//
// The retry method may return the |suggestedWillRetry| argument to get the
// default retry behavior. Server status codes are present in the
// error argument, and have the domain kGTMSessionFetcherStatusDomain. The
// user's method may look something like this:
//
// myFetcher.retryBlock = ^(BOOL suggestedWillRetry, NSError *error,
// GTMSessionFetcherRetryResponse response) {
// // Perhaps examine error.domain and error.code, or fetcher.retryCount
// //
// // Respond with YES to start the retry timer, NO to proceed to the failure
// // callback, or suggestedWillRetry to get default behavior for the
// // current error domain and code values.
// response(suggestedWillRetry);
// };
#import <Foundation/Foundation.h>
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#endif
#if TARGET_OS_WATCH
#import <WatchKit/WatchKit.h>
#endif
// By default it is stripped from non DEBUG builds. Developers can override
// this in their project settings.
#ifndef STRIP_GTM_FETCH_LOGGING
#if !DEBUG
#define STRIP_GTM_FETCH_LOGGING 1
#else
#define STRIP_GTM_FETCH_LOGGING 0
#endif
#endif
// Logs in debug builds.
#ifndef GTMSESSION_LOG_DEBUG
#if DEBUG
#define GTMSESSION_LOG_DEBUG(...) NSLog(__VA_ARGS__)
#else
#define GTMSESSION_LOG_DEBUG(...) do { } while (0)
#endif
#endif
// Asserts in debug builds (or logs in debug builds if GTMSESSION_ASSERT_AS_LOG
// or NS_BLOCK_ASSERTIONS are defined.)
#ifndef GTMSESSION_ASSERT_DEBUG
#if DEBUG && !defined(NS_BLOCK_ASSERTIONS) && !GTMSESSION_ASSERT_AS_LOG
#undef GTMSESSION_ASSERT_AS_LOG
#define GTMSESSION_ASSERT_AS_LOG 1
#endif
#if DEBUG && !GTMSESSION_ASSERT_AS_LOG
#define GTMSESSION_ASSERT_DEBUG(...) NSAssert(__VA_ARGS__)
#elif DEBUG
#define GTMSESSION_ASSERT_DEBUG(pred, ...) if (!(pred)) { NSLog(__VA_ARGS__); }
#else
#define GTMSESSION_ASSERT_DEBUG(pred, ...) do { } while (0)
#endif
#endif
// Asserts in debug builds, logs in release builds (or logs in debug builds if
// GTMSESSION_ASSERT_AS_LOG is defined.)
#ifndef GTMSESSION_ASSERT_DEBUG_OR_LOG
#if DEBUG && !GTMSESSION_ASSERT_AS_LOG
#define GTMSESSION_ASSERT_DEBUG_OR_LOG(...) NSAssert(__VA_ARGS__)
#else
#define GTMSESSION_ASSERT_DEBUG_OR_LOG(pred, ...) if (!(pred)) { NSLog(__VA_ARGS__); }
#endif
#endif
// Macro useful for examining messages from NSURLSession during debugging.
#if 0
#define GTM_LOG_SESSION_DELEGATE(...) GTMSESSION_LOG_DEBUG(__VA_ARGS__)
#else
#define GTM_LOG_SESSION_DELEGATE(...)
#endif
#ifndef GTM_NULLABLE
#if __has_feature(nullability) // Available starting in Xcode 6.3
#define GTM_NULLABLE_TYPE __nullable
#define GTM_NONNULL_TYPE __nonnull
#define GTM_NULLABLE nullable
#define GTM_NONNULL_DECL nonnull // GTM_NONNULL is used by GTMDefines.h
#define GTM_NULL_RESETTABLE null_resettable
#define GTM_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
#define GTM_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
#else
#define GTM_NULLABLE_TYPE
#define GTM_NONNULL_TYPE
#define GTM_NULLABLE
#define GTM_NONNULL_DECL
#define GTM_NULL_RESETTABLE
#define GTM_ASSUME_NONNULL_BEGIN
#define GTM_ASSUME_NONNULL_END
#endif // __has_feature(nullability)
#endif // GTM_NULLABLE
#ifndef GTM_DECLARE_GENERICS
#if __has_feature(objc_generics)
#define GTM_DECLARE_GENERICS 1
#else
#define GTM_DECLARE_GENERICS 0
#endif
#endif
#ifndef GTM_NSArrayOf
#if GTM_DECLARE_GENERICS
#define GTM_NSArrayOf(value) NSArray<value>
#define GTM_NSDictionaryOf(key, value) NSDictionary<key, value>
#else
#define GTM_NSArrayOf(value) NSArray
#define GTM_NSDictionaryOf(key, value) NSDictionary
#endif // __has_feature(objc_generics)
#endif // GTM_NSArrayOf
// For iOS, the fetcher can declare itself a background task to allow fetches
// to finish when the app leaves the foreground.
//
// (This is unrelated to providing a background configuration, which allows
// out-of-process uploads and downloads.)
//
// To disallow use of background tasks during fetches, the target should define
// GTM_BACKGROUND_TASK_FETCHING to 0, or alternatively may set the
// skipBackgroundTask property to YES.
#if TARGET_OS_IPHONE && !TARGET_OS_WATCH && !defined(GTM_BACKGROUND_TASK_FETCHING)
#define GTM_BACKGROUND_TASK_FETCHING 1
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if (TARGET_OS_TV \
|| TARGET_OS_WATCH \
|| (!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))
#ifndef GTM_USE_SESSION_FETCHER
#define GTM_USE_SESSION_FETCHER 1
#endif
#endif
#if !defined(GTMBridgeFetcher)
// These bridge macros should be identical in GTMHTTPFetcher.h and GTMSessionFetcher.h
#if GTM_USE_SESSION_FETCHER
// Macros to new fetcher class.
#define GTMBridgeFetcher GTMSessionFetcher
#define GTMBridgeFetcherService GTMSessionFetcherService
#define GTMBridgeFetcherServiceProtocol GTMSessionFetcherServiceProtocol
#define GTMBridgeAssertValidSelector GTMSessionFetcherAssertValidSelector
#define GTMBridgeCookieStorage GTMSessionCookieStorage
#define GTMBridgeCleanedUserAgentString GTMFetcherCleanedUserAgentString
#define GTMBridgeSystemVersionString GTMFetcherSystemVersionString
#define GTMBridgeApplicationIdentifier GTMFetcherApplicationIdentifier
#define kGTMBridgeFetcherStatusDomain kGTMSessionFetcherStatusDomain
#define kGTMBridgeFetcherStatusBadRequest GTMSessionFetcherStatusBadRequest
#else
// Macros to old fetcher class.
#define GTMBridgeFetcher GTMHTTPFetcher
#define GTMBridgeFetcherService GTMHTTPFetcherService
#define GTMBridgeFetcherServiceProtocol GTMHTTPFetcherServiceProtocol
#define GTMBridgeAssertValidSelector GTMAssertSelectorNilOrImplementedWithArgs
#define GTMBridgeCookieStorage GTMCookieStorage
#define GTMBridgeCleanedUserAgentString GTMCleanedUserAgentString
#define GTMBridgeSystemVersionString GTMSystemVersionString
#define GTMBridgeApplicationIdentifier GTMApplicationIdentifier
#define kGTMBridgeFetcherStatusDomain kGTMHTTPFetcherStatusDomain
#define kGTMBridgeFetcherStatusBadRequest kGTMHTTPFetcherStatusBadRequest
#endif // GTM_USE_SESSION_FETCHER
#endif
// When creating background sessions to perform out-of-process uploads and
// downloads, on app launch any background sessions must be reconnected in
// order to receive events that occurred while the app was not running.
//
// The fetcher will automatically attempt to recreate the sessions on app
// start, but doing so reads from NSUserDefaults. This may have launch-time
// performance impacts.
//
// To avoid launch performance impacts, on iPhone/iPad with iOS 13+ the
// GTMSessionFetcher class will register for the app launch notification and
// perform the reconnect then.
//
// Apps targeting Mac or older iOS SDKs can opt into the new behavior by defining
// GTMSESSION_RECONNECT_BACKGROUND_SESSIONS_ON_LAUNCH=1.
//
// Apps targeting new SDKs can force the old behavior by defining
// GTMSESSION_RECONNECT_BACKGROUND_SESSIONS_ON_LAUNCH = 0.
#ifndef GTMSESSION_RECONNECT_BACKGROUND_SESSIONS_ON_LAUNCH
// Default to the on-launch behavior for iOS 13+.
#if TARGET_OS_IOS && defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
#define GTMSESSION_RECONNECT_BACKGROUND_SESSIONS_ON_LAUNCH 1
#else
#define GTMSESSION_RECONNECT_BACKGROUND_SESSIONS_ON_LAUNCH 0
#endif
#endif
GTM_ASSUME_NONNULL_BEGIN
// Notifications
//
// Fetch started and stopped, and fetch retry delay started and stopped.
extern NSString *const kGTMSessionFetcherStartedNotification;
extern NSString *const kGTMSessionFetcherStoppedNotification;
extern NSString *const kGTMSessionFetcherRetryDelayStartedNotification;
extern NSString *const kGTMSessionFetcherRetryDelayStoppedNotification;
// Completion handler notification. This is intended for use by code capturing
// and replaying fetch requests and results for testing. For fetches where
// destinationFileURL or accumulateDataBlock is set for the fetcher, the data
// will be nil for successful fetches.
//
// This notification is posted on the main thread.
extern NSString *const kGTMSessionFetcherCompletionInvokedNotification;
extern NSString *const kGTMSessionFetcherCompletionDataKey;
extern NSString *const kGTMSessionFetcherCompletionErrorKey;
// Constants for NSErrors created by the fetcher (excluding server status errors,
// and error objects originating in the OS.)
extern NSString *const kGTMSessionFetcherErrorDomain;
// The fetcher turns server error status values (3XX, 4XX, 5XX) into NSErrors
// with domain kGTMSessionFetcherStatusDomain.
//
// Any server response body data accompanying the status error is added to the
// userInfo dictionary with key kGTMSessionFetcherStatusDataKey.
extern NSString *const kGTMSessionFetcherStatusDomain;
extern NSString *const kGTMSessionFetcherStatusDataKey;
extern NSString *const kGTMSessionFetcherStatusDataContentTypeKey;
// When a fetch fails with an error, these keys are included in the error userInfo
// dictionary if retries were attempted.
extern NSString *const kGTMSessionFetcherNumberOfRetriesDoneKey;
extern NSString *const kGTMSessionFetcherElapsedIntervalWithRetriesKey;
// Background session support requires access to NSUserDefaults.
// If [NSUserDefaults standardUserDefaults] doesn't yield the correct NSUserDefaults for your usage,
// ie for an App Extension, then implement this class/method to return the correct NSUserDefaults.
// https://developer.apple.com/library/ios/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW6
@interface GTMSessionFetcherUserDefaultsFactory : NSObject
+ (NSUserDefaults *)fetcherUserDefaults;
@end
#ifdef __cplusplus
}
#endif
typedef NS_ENUM(NSInteger, GTMSessionFetcherError) {
GTMSessionFetcherErrorDownloadFailed = -1,
GTMSessionFetcherErrorUploadChunkUnavailable = -2,
GTMSessionFetcherErrorBackgroundExpiration = -3,
GTMSessionFetcherErrorBackgroundFetchFailed = -4,
GTMSessionFetcherErrorInsecureRequest = -5,
GTMSessionFetcherErrorTaskCreationFailed = -6,
};
typedef NS_ENUM(NSInteger, GTMSessionFetcherStatus) {
// Standard http status codes.
GTMSessionFetcherStatusNotModified = 304,
GTMSessionFetcherStatusBadRequest = 400,
GTMSessionFetcherStatusUnauthorized = 401,
GTMSessionFetcherStatusForbidden = 403,
GTMSessionFetcherStatusPreconditionFailed = 412
};
#ifdef __cplusplus
extern "C" {
#endif
@class GTMSessionCookieStorage;
@class GTMSessionFetcher;
// The configuration block is for modifying the NSURLSessionConfiguration only.
// DO NOT change any fetcher properties in the configuration block.
typedef void (^GTMSessionFetcherConfigurationBlock)(GTMSessionFetcher *fetcher,
NSURLSessionConfiguration *configuration);
typedef void (^GTMSessionFetcherSystemCompletionHandler)(void);
typedef void (^GTMSessionFetcherCompletionHandler)(NSData * GTM_NULLABLE_TYPE data,
NSError * GTM_NULLABLE_TYPE error);
typedef void (^GTMSessionFetcherBodyStreamProviderResponse)(NSInputStream *bodyStream);
typedef void (^GTMSessionFetcherBodyStreamProvider)(GTMSessionFetcherBodyStreamProviderResponse response);
typedef void (^GTMSessionFetcherDidReceiveResponseDispositionBlock)(NSURLSessionResponseDisposition disposition);
typedef void (^GTMSessionFetcherDidReceiveResponseBlock)(NSURLResponse *response,
GTMSessionFetcherDidReceiveResponseDispositionBlock dispositionBlock);
typedef void (^GTMSessionFetcherChallengeDispositionBlock)(NSURLSessionAuthChallengeDisposition disposition,
NSURLCredential * GTM_NULLABLE_TYPE credential);
typedef void (^GTMSessionFetcherChallengeBlock)(GTMSessionFetcher *fetcher,
NSURLAuthenticationChallenge *challenge,
GTMSessionFetcherChallengeDispositionBlock dispositionBlock);
typedef void (^GTMSessionFetcherWillRedirectResponse)(NSURLRequest * GTM_NULLABLE_TYPE redirectedRequest);
typedef void (^GTMSessionFetcherWillRedirectBlock)(NSHTTPURLResponse *redirectResponse,
NSURLRequest *redirectRequest,
GTMSessionFetcherWillRedirectResponse response);
typedef void (^GTMSessionFetcherAccumulateDataBlock)(NSData * GTM_NULLABLE_TYPE buffer);
typedef void (^GTMSessionFetcherSimulateByteTransferBlock)(NSData * GTM_NULLABLE_TYPE buffer,
int64_t bytesWritten,
int64_t totalBytesWritten,
int64_t totalBytesExpectedToWrite);
typedef void (^GTMSessionFetcherReceivedProgressBlock)(int64_t bytesWritten,
int64_t totalBytesWritten);
typedef void (^GTMSessionFetcherDownloadProgressBlock)(int64_t bytesWritten,
int64_t totalBytesWritten,
int64_t totalBytesExpectedToWrite);
typedef void (^GTMSessionFetcherSendProgressBlock)(int64_t bytesSent,
int64_t totalBytesSent,
int64_t totalBytesExpectedToSend);
typedef void (^GTMSessionFetcherWillCacheURLResponseResponse)(NSCachedURLResponse * GTM_NULLABLE_TYPE cachedResponse);
typedef void (^GTMSessionFetcherWillCacheURLResponseBlock)(NSCachedURLResponse *proposedResponse,
GTMSessionFetcherWillCacheURLResponseResponse responseBlock);
typedef void (^GTMSessionFetcherRetryResponse)(BOOL shouldRetry);
typedef void (^GTMSessionFetcherRetryBlock)(BOOL suggestedWillRetry,
NSError * GTM_NULLABLE_TYPE error,
GTMSessionFetcherRetryResponse response);
API_AVAILABLE(ios(10.0), macosx(10.12), tvos(10.0), watchos(3.0))
typedef void (^GTMSessionFetcherMetricsCollectionBlock)(NSURLSessionTaskMetrics *metrics);
typedef void (^GTMSessionFetcherTestResponse)(NSHTTPURLResponse * GTM_NULLABLE_TYPE response,
NSData * GTM_NULLABLE_TYPE data,
NSError * GTM_NULLABLE_TYPE error);
typedef void (^GTMSessionFetcherTestBlock)(GTMSessionFetcher *fetcherToTest,
GTMSessionFetcherTestResponse testResponse);
void GTMSessionFetcherAssertValidSelector(id GTM_NULLABLE_TYPE obj, SEL GTM_NULLABLE_TYPE sel, ...);
// Utility functions for applications self-identifying to servers via a
// user-agent header
// The "standard" user agent includes the application identifier, taken from the bundle,
// followed by a space and the system version string. Pass nil to use +mainBundle as the source
// of the bundle identifier.
//
// Applications may use this as a starting point for their own user agent strings, perhaps
// with additional sections appended. Use GTMFetcherCleanedUserAgentString() below to
// clean up any string being added to the user agent.
NSString *GTMFetcherStandardUserAgentString(NSBundle * GTM_NULLABLE_TYPE bundle);
// Make a generic name and version for the current application, like
// com.example.MyApp/1.2.3 relying on the bundle identifier and the
// CFBundleShortVersionString or CFBundleVersion.
//
// The bundle ID may be overridden as the base identifier string by
// adding to the bundle's Info.plist a "GTMUserAgentID" key.
//
// If no bundle ID or override is available, the process name preceded
// by "proc_" is used.
NSString *GTMFetcherApplicationIdentifier(NSBundle * GTM_NULLABLE_TYPE bundle);
// Make an identifier like "MacOSX/10.7.1" or "iPod_Touch/4.1 hw/iPod1_1"
NSString *GTMFetcherSystemVersionString(void);
// Make a parseable user-agent identifier from the given string, replacing whitespace
// and commas with underscores, and removing other characters that may interfere
// with parsing of the full user-agent string.
//
// For example, @"[My App]" would become @"My_App"
NSString *GTMFetcherCleanedUserAgentString(NSString *str);
// Grab the data from an input stream. Since streams cannot be assumed to be rewindable,
// this may be destructive; the caller can try to rewind the stream (by setting the
// NSStreamFileCurrentOffsetKey property) or can just use the NSData to make a new
// NSInputStream. This function is intended to facilitate testing rather than be used in
// production.
//
// This function operates synchronously on the current thread. Depending on how the
// input stream is implemented, it may be appropriate to dispatch to a different
// queue before calling this function.
//
// Failure is indicated by a returned data value of nil.
NSData * GTM_NULLABLE_TYPE GTMDataFromInputStream(NSInputStream *inputStream, NSError **outError);
#ifdef __cplusplus
} // extern "C"
#endif
#if !GTM_USE_SESSION_FETCHER
@protocol GTMHTTPFetcherServiceProtocol;
#endif
// This protocol allows abstract references to the fetcher service, primarily for
// fetchers (which may be compiled without the fetcher service class present.)
//
// Apps should not need to use this protocol.
@protocol GTMSessionFetcherServiceProtocol <NSObject>
// This protocol allows us to call into the service without requiring
// GTMSessionFetcherService sources in this project
@property(atomic, strong) dispatch_queue_t callbackQueue;
- (BOOL)fetcherShouldBeginFetching:(GTMSessionFetcher *)fetcher;
- (void)fetcherDidCreateSession:(GTMSessionFetcher *)fetcher;
- (void)fetcherDidBeginFetching:(GTMSessionFetcher *)fetcher;
- (void)fetcherDidStop:(GTMSessionFetcher *)fetcher;
- (GTMSessionFetcher *)fetcherWithRequest:(NSURLRequest *)request;
- (BOOL)isDelayingFetcher:(GTMSessionFetcher *)fetcher;
@property(atomic, assign) BOOL reuseSession;
- (GTM_NULLABLE NSURLSession *)session;
- (GTM_NULLABLE NSURLSession *)sessionForFetcherCreation;
- (GTM_NULLABLE id<NSURLSessionDelegate>)sessionDelegate;
- (GTM_NULLABLE NSDate *)stoppedAllFetchersDate;
// Methods for compatibility with the old GTMHTTPFetcher.
@property(atomic, readonly, strong, GTM_NULLABLE) NSOperationQueue *delegateQueue;
@end // @protocol GTMSessionFetcherServiceProtocol
#ifndef GTM_FETCHER_AUTHORIZATION_PROTOCOL
#define GTM_FETCHER_AUTHORIZATION_PROTOCOL 1
@protocol GTMFetcherAuthorizationProtocol <NSObject>
@required
// This protocol allows us to call the authorizer without requiring its sources
// in this project.
- (void)authorizeRequest:(GTM_NULLABLE NSMutableURLRequest *)request
delegate:(id)delegate
didFinishSelector:(SEL)sel;
- (void)stopAuthorization;
- (void)stopAuthorizationForRequest:(NSURLRequest *)request;
- (BOOL)isAuthorizingRequest:(NSURLRequest *)request;
- (BOOL)isAuthorizedRequest:(NSURLRequest *)request;
@property(atomic, strong, readonly, GTM_NULLABLE) NSString *userEmail;
@optional
// Indicate if authorization may be attempted. Even if this succeeds,
// authorization may fail if the user's permissions have been revoked.
@property(atomic, readonly) BOOL canAuthorize;
// For development only, allow authorization of non-SSL requests, allowing
// transmission of the bearer token unencrypted.
@property(atomic, assign) BOOL shouldAuthorizeAllRequests;
- (void)authorizeRequest:(GTM_NULLABLE NSMutableURLRequest *)request
completionHandler:(void (^)(NSError * GTM_NULLABLE_TYPE error))handler;
#if GTM_USE_SESSION_FETCHER
@property(atomic, weak, GTM_NULLABLE) id<GTMSessionFetcherServiceProtocol> fetcherService;
#else
@property(atomic, weak, GTM_NULLABLE) id<GTMHTTPFetcherServiceProtocol> fetcherService;
#endif
- (BOOL)primeForRefresh;
@end
#endif // GTM_FETCHER_AUTHORIZATION_PROTOCOL
#if GTM_BACKGROUND_TASK_FETCHING
// A protocol for an alternative target for messages from GTMSessionFetcher to UIApplication.
// Set the target using +[GTMSessionFetcher setSubstituteUIApplication:]
@protocol GTMUIApplicationProtocol <NSObject>
- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithName:(nullable NSString *)taskName
expirationHandler:(void(^ __nullable)(void))handler;
- (void)endBackgroundTask:(UIBackgroundTaskIdentifier)identifier;
@end
#endif
#pragma mark -
// GTMSessionFetcher objects are used for async retrieval of an http get or post
//
// See additional comments at the beginning of this file
@interface GTMSessionFetcher : NSObject <NSURLSessionDelegate>
// Create a fetcher
//
// fetcherWithRequest will return an autoreleased fetcher, but if
// the connection is successfully created, the connection should retain the
// fetcher for the life of the connection as well. So the caller doesn't have
// to retain the fetcher explicitly unless they want to be able to cancel it.
+ (instancetype)fetcherWithRequest:(GTM_NULLABLE NSURLRequest *)request;
// Convenience methods that make a request, like +fetcherWithRequest
+ (instancetype)fetcherWithURL:(NSURL *)requestURL;
+ (instancetype)fetcherWithURLString:(NSString *)requestURLString;
// Methods for creating fetchers to continue previous fetches.
+ (instancetype)fetcherWithDownloadResumeData:(NSData *)resumeData;
+ (GTM_NULLABLE instancetype)fetcherWithSessionIdentifier:(NSString *)sessionIdentifier;
// Returns an array of currently active fetchers for background sessions,
// both restarted and newly created ones.
+ (GTM_NSArrayOf(GTMSessionFetcher *) *)fetchersForBackgroundSessions;
// Designated initializer.
//
// Applications should create fetchers with a "fetcherWith..." method on a fetcher
// service or a class method, not with this initializer.
//
// The configuration should typically be nil. Applications needing to customize
// the configuration may do so by setting the configurationBlock property.
- (instancetype)initWithRequest:(GTM_NULLABLE NSURLRequest *)request
configuration:(GTM_NULLABLE NSURLSessionConfiguration *)configuration;
// The fetcher's request. This may not be set after beginFetch has been invoked. The request
// may change due to redirects.
@property(atomic, strong, GTM_NULLABLE) NSURLRequest *request;
// Set a header field value on the request. Header field value changes will not
// affect a fetch after the fetch has begun.
- (void)setRequestValue:(GTM_NULLABLE NSString *)value forHTTPHeaderField:(NSString *)field;
// Data used for resuming a download task.
@property(atomic, readonly, GTM_NULLABLE) NSData *downloadResumeData;
// The configuration; this must be set before the fetch begins. If no configuration is
// set or inherited from the fetcher service, then the fetcher uses an ephemeral config.
//
// NOTE: This property should typically be nil. Applications needing to customize
// the configuration should do so by setting the configurationBlock property.
// That allows the fetcher to pick an appropriate base configuration, with the
// application setting only the configuration properties it needs to customize.
@property(atomic, strong, GTM_NULLABLE) NSURLSessionConfiguration *configuration;
// A block the client may use to customize the configuration used to create the session.
//
// This is called synchronously, either on the thread that begins the fetch or, during a retry,
// on the main thread. The configuration block may be called repeatedly if multiple fetchers are
// created.
//
// The configuration block is for modifying the NSURLSessionConfiguration only.
// DO NOT change any fetcher properties in the configuration block. Fetcher properties
// may be set in the fetcher service prior to fetcher creation, or on the fetcher prior
// to invoking beginFetch.
@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherConfigurationBlock configurationBlock;
// A session is created as needed by the fetcher. A fetcher service object
// may maintain sessions for multiple fetches to the same host.
@property(atomic, strong, GTM_NULLABLE) NSURLSession *session;
// The task in flight.
@property(atomic, readonly, GTM_NULLABLE) NSURLSessionTask *sessionTask;
// The background session identifier.
@property(atomic, readonly, GTM_NULLABLE) NSString *sessionIdentifier;
// Indicates a fetcher created to finish a background session task.
@property(atomic, readonly) BOOL wasCreatedFromBackgroundSession;
// Indicates the client has committed to reconnecting this background session when
// the app restarts. If this value is YES, the session fetcher will not automatically
// call beginFetchWithCompletionHandler: on the restored fetcher on app start, and
// the session will not handle system events until the client explicitly does.
@property(atomic, assign) BOOL clientWillReconnectBackgroundSession;
// Additional user-supplied data to encode into the session identifier. Since session identifier
// length limits are unspecified, this should be kept small. Key names beginning with an underscore
// are reserved for use by the fetcher.
@property(atomic, strong, GTM_NULLABLE) GTM_NSDictionaryOf(NSString *, NSString *) *sessionUserInfo;
// The human-readable description to be assigned to the task.
@property(atomic, copy, GTM_NULLABLE) NSString *taskDescription;
// The priority assigned to the task, if any. Use NSURLSessionTaskPriorityLow,
// NSURLSessionTaskPriorityDefault, or NSURLSessionTaskPriorityHigh.
@property(atomic, assign) float taskPriority;
// The fetcher encodes information used to resume a session in the session identifier.
// This method, intended for internal use returns the encoded information. The sessionUserInfo
// dictionary is stored as identifier metadata.
- (GTM_NULLABLE GTM_NSDictionaryOf(NSString *, NSString *) *)sessionIdentifierMetadata;
#if TARGET_OS_IPHONE && !TARGET_OS_WATCH
// The app should pass to this method the completion handler passed in the app delegate method
// application:handleEventsForBackgroundURLSession:completionHandler:
+ (void)application:(UIApplication *)application
handleEventsForBackgroundURLSession:(NSString *)identifier
completionHandler:(GTMSessionFetcherSystemCompletionHandler)completionHandler;
#endif
// Indicate that a newly created session should be a background session.
// A new session identifier will be created by the fetcher.
//
// Warning: The only thing background sessions are for is rare download
// of huge, batched files of data. And even just for those, there's a lot
// of pain and hackery needed to get transfers to actually happen reliably
// with background sessions.
//
// Don't try to upload or download in many background sessions, since the system
// will impose an exponentially increasing time penalty to prevent the app from
// getting too much background execution time.
//
// References:
//
// "Moving to Fewer, Larger Transfers"
// https://forums.developer.apple.com/thread/14853
//
// "NSURLSession’s Resume Rate Limiter"
// https://forums.developer.apple.com/thread/14854
//
// "Background Session Task state persistence"
// https://forums.developer.apple.com/thread/11554
//
@property(atomic, assign) BOOL useBackgroundSession;
// Indicates if the fetcher was started using a background session.
@property(atomic, readonly, getter=isUsingBackgroundSession) BOOL usingBackgroundSession;
// Indicates if uploads should use an upload task. This is always set for file or stream-provider
// bodies, but may be set explicitly for NSData bodies.
@property(atomic, assign) BOOL useUploadTask;
// Indicates that the fetcher is using a session that may be shared with other fetchers.
@property(atomic, readonly) BOOL canShareSession;
// By default, the fetcher allows only secure (https) schemes unless this
// property is set, or the GTM_ALLOW_INSECURE_REQUESTS build flag is set.
//
// For example, during debugging when fetching from a development server that lacks SSL support,
// this may be set to @[ @"http" ], or when the fetcher is used to retrieve local files,
// this may be set to @[ @"file" ].
//
// This should be left as nil for release builds to avoid creating the opportunity for
// leaking private user behavior and data. If a server is providing insecure URLs
// for fetching by the client app, report the problem as server security & privacy bug.
//
// For builds with the iOS 9/OS X 10.11 and later SDKs, this property is required only when
// the app specifies NSAppTransportSecurity/NSAllowsArbitraryLoads in the main bundle's Info.plist.
@property(atomic, copy, GTM_NULLABLE) GTM_NSArrayOf(NSString *) *allowedInsecureSchemes;
// By default, the fetcher prohibits localhost requests unless this property is set,
// or the GTM_ALLOW_INSECURE_REQUESTS build flag is set.
//
// For localhost requests, the URL scheme is not checked when this property is set.
//
// For builds with the iOS 9/OS X 10.11 and later SDKs, this property is required only when
// the app specifies NSAppTransportSecurity/NSAllowsArbitraryLoads in the main bundle's Info.plist.
@property(atomic, assign) BOOL allowLocalhostRequest;
// By default, the fetcher requires valid server certs. This may be bypassed
// temporarily for development against a test server with an invalid cert.
@property(atomic, assign) BOOL allowInvalidServerCertificates;
// Cookie storage object for this fetcher. If nil, the fetcher will use a static cookie
// storage instance shared among fetchers. If this fetcher was created by a fetcher service
// object, it will be set to use the service object's cookie storage. See Cookies section above for
// the full discussion.
//
// Because as of Jan 2014 standalone instances of NSHTTPCookieStorage do not actually
// store any cookies (Radar 15735276) we use our own subclass, GTMSessionCookieStorage,
// to hold cookies in memory.
@property(atomic, strong, GTM_NULLABLE) NSHTTPCookieStorage *cookieStorage;
// Setting the credential is optional; it is used if the connection receives
// an authentication challenge.
@property(atomic, strong, GTM_NULLABLE) NSURLCredential *credential;
// Setting the proxy credential is optional; it is used if the connection
// receives an authentication challenge from a proxy.
@property(atomic, strong, GTM_NULLABLE) NSURLCredential *proxyCredential;
// If body data, body file URL, or body stream provider is not set, then a GET request
// method is assumed.
@property(atomic, strong, GTM_NULLABLE) NSData *bodyData;
// File to use as the request body. This forces use of an upload task.
@property(atomic, strong, GTM_NULLABLE) NSURL *bodyFileURL;
// Length of body to send, expected or actual.
@property(atomic, readonly) int64_t bodyLength;
// The body stream provider may be called repeatedly to provide a body.
// Setting a body stream provider forces use of an upload task.
@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherBodyStreamProvider bodyStreamProvider;
// Object to add authorization to the request, if needed.
//
// This may not be changed once beginFetch has been invoked.
@property(atomic, strong, GTM_NULLABLE) id<GTMFetcherAuthorizationProtocol> authorizer;
// The service object that created and monitors this fetcher, if any.
@property(atomic, strong) id<GTMSessionFetcherServiceProtocol> service;
// The host, if any, used to classify this fetcher in the fetcher service.
@property(atomic, copy, GTM_NULLABLE) NSString *serviceHost;
// The priority, if any, used for starting fetchers in the fetcher service.
//
// Lower values are higher priority; the default is 0, and values may
// be negative or positive. This priority affects only the start order of
// fetchers that are being delayed by a fetcher service when the running fetchers
// exceeds the service's maxRunningFetchersPerHost. A priority of NSIntegerMin will
// exempt this fetcher from delay.
@property(atomic, assign) NSInteger servicePriority;
// The delegate's optional didReceiveResponse block may be used to inspect or alter
// the session task response.
//
// This is called on the callback queue.
@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherDidReceiveResponseBlock didReceiveResponseBlock;
// The delegate's optional challenge block may be used to inspect or alter
// the session task challenge.
//
// If this block is not set, the fetcher's default behavior for the NSURLSessionTask
// didReceiveChallenge: delegate method is to use the fetcher's respondToChallenge: method
// which relies on the fetcher's credential and proxyCredential properties.
//
// Warning: This may be called repeatedly if the challenge fails. Check
// challenge.previousFailureCount to identify repeated invocations.
//
// This is called on the callback queue.
@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherChallengeBlock challengeBlock;
// The delegate's optional willRedirect block may be used to inspect or alter
// the redirection.
//
// This is called on the callback queue.
@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherWillRedirectBlock willRedirectBlock;
// The optional send progress block reports body bytes uploaded.
//
// This is called on the callback queue.
@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherSendProgressBlock sendProgressBlock;
// The optional accumulate block may be set by clients wishing to accumulate data
// themselves rather than let the fetcher append each buffer to an NSData.
//
// When this is called with nil data (such as on redirect) the client
// should empty its accumulation buffer.
//
// This is called on the callback queue.
@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherAccumulateDataBlock accumulateDataBlock;
// The optional received progress block may be used to monitor data
// received from a data task.
//
// This is called on the callback queue.
@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherReceivedProgressBlock receivedProgressBlock;
// The delegate's optional downloadProgress block may be used to monitor download
// progress in writing to disk.
//
// This is called on the callback queue.
@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherDownloadProgressBlock downloadProgressBlock;
// The delegate's optional willCacheURLResponse block may be used to alter the cached
// NSURLResponse. The user may prevent caching by passing nil to the block's response.
//
// This is called on the callback queue.
@property(atomic, copy, GTM_NULLABLE) GTMSessionFetcherWillCacheURLResponseBlock willCacheURLResponseBlock;
// Enable retrying; see comments at the top of this file. Setting
// retryEnabled=YES resets the min and max retry intervals.
@property(atomic, assign, getter=isRetryEnabled) BOOL retryEnabled;