Authored by DreamPiggy
Committed by GitHub

Merge pull request #2204 from dreampiggy/fix_downloader_headers_thread_safe

Use a lock to ensure headers mutable dictionary thread-safe
@@ -40,6 +40,7 @@ @@ -40,6 +40,7 @@
40 @property (strong, nonatomic, nonnull) NSMutableDictionary<NSURL *, SDWebImageDownloaderOperation *> *URLOperations; 40 @property (strong, nonatomic, nonnull) NSMutableDictionary<NSURL *, SDWebImageDownloaderOperation *> *URLOperations;
41 @property (strong, nonatomic, nullable) SDHTTPHeadersMutableDictionary *HTTPHeaders; 41 @property (strong, nonatomic, nullable) SDHTTPHeadersMutableDictionary *HTTPHeaders;
42 @property (strong, nonatomic, nonnull) dispatch_semaphore_t operationsLock; // a lock to keep the access to `URLOperations` thread-safe 42 @property (strong, nonatomic, nonnull) dispatch_semaphore_t operationsLock; // a lock to keep the access to `URLOperations` thread-safe
  43 +@property (strong, nonatomic, nonnull) dispatch_semaphore_t headersLock; // a lock to keep the access to `HTTPHeaders` thread-safe
43 44
44 // The session in which data tasks will run 45 // The session in which data tasks will run
45 @property (strong, nonatomic) NSURLSession *session; 46 @property (strong, nonatomic) NSURLSession *session;
@@ -99,6 +100,7 @@ @@ -99,6 +100,7 @@
99 _HTTPHeaders = [@{@"Accept": @"image/*;q=0.8"} mutableCopy]; 100 _HTTPHeaders = [@{@"Accept": @"image/*;q=0.8"} mutableCopy];
100 #endif 101 #endif
101 _operationsLock = dispatch_semaphore_create(1); 102 _operationsLock = dispatch_semaphore_create(1);
  103 + _headersLock = dispatch_semaphore_create(1);
102 _downloadTimeout = 15.0; 104 _downloadTimeout = 15.0;
103 105
104 [self createNewSessionWithConfiguration:sessionConfiguration]; 106 [self createNewSessionWithConfiguration:sessionConfiguration];
@@ -144,15 +146,27 @@ @@ -144,15 +146,27 @@
144 } 146 }
145 147
146 - (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field { 148 - (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field {
  149 + LOCK(self.headersLock);
147 if (value) { 150 if (value) {
148 self.HTTPHeaders[field] = value; 151 self.HTTPHeaders[field] = value;
149 } else { 152 } else {
150 [self.HTTPHeaders removeObjectForKey:field]; 153 [self.HTTPHeaders removeObjectForKey:field];
151 } 154 }
  155 + UNLOCK(self.headersLock);
152 } 156 }
153 157
154 - (nullable NSString *)valueForHTTPHeaderField:(nullable NSString *)field { 158 - (nullable NSString *)valueForHTTPHeaderField:(nullable NSString *)field {
155 - return self.HTTPHeaders[field]; 159 + if (!field) {
  160 + return nil;
  161 + }
  162 + return [[self allHTTPHeaderFields] objectForKey:field];
  163 +}
  164 +
  165 +- (nonnull SDHTTPHeadersDictionary *)allHTTPHeaderFields {
  166 + LOCK(self.headersLock);
  167 + SDHTTPHeadersDictionary *allHTTPHeaderFields = [self.HTTPHeaders copy];
  168 + UNLOCK(self.headersLock);
  169 + return allHTTPHeaderFields;
156 } 170 }
157 171
158 - (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads { 172 - (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads {
@@ -201,10 +215,10 @@ @@ -201,10 +215,10 @@
201 request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies); 215 request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
202 request.HTTPShouldUsePipelining = YES; 216 request.HTTPShouldUsePipelining = YES;
203 if (sself.headersFilter) { 217 if (sself.headersFilter) {
204 - request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]); 218 + request.allHTTPHeaderFields = sself.headersFilter(url, [sself allHTTPHeaderFields]);
205 } 219 }
206 else { 220 else {
207 - request.allHTTPHeaderFields = sself.HTTPHeaders; 221 + request.allHTTPHeaderFields = [sself allHTTPHeaderFields];
208 } 222 }
209 SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options]; 223 SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
210 operation.shouldDecompressImages = sself.shouldDecompressImages; 224 operation.shouldDecompressImages = sself.shouldDecompressImages;