Bladeren bron

Merge pull request #3526 from alexbird/bugs/header-threading-crash

Fixed crashes due to race conditions with NSMutableDictionary access in AFHTTPRequestSerialiser
Kevin Harwood 9 jaren geleden
bovenliggende
commit
2a53b2c3f4
2 gewijzigde bestanden met toevoegingen van 46 en 6 verwijderingen
  1. 24 6
      AFNetworking/AFURLRequestSerialization.m
  2. 22 0
      Tests/Tests/AFHTTPRequestSerializationTests.m

+ 24 - 6
AFNetworking/AFURLRequestSerialization.m

@@ -186,6 +186,7 @@ static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerOb
 @interface AFHTTPRequestSerializer ()
 @interface AFHTTPRequestSerializer ()
 @property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths;
 @property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths;
 @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders;
 @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders;
+@property (readwrite, nonatomic, strong) dispatch_queue_t requestHeaderModificationQueue;
 @property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle;
 @property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle;
 @property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization;
 @property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization;
 @end
 @end
@@ -205,6 +206,7 @@ static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerOb
     self.stringEncoding = NSUTF8StringEncoding;
     self.stringEncoding = NSUTF8StringEncoding;
 
 
     self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];
     self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];
+    self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT);
 
 
     // Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
     // Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
     NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
     NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
@@ -300,17 +302,27 @@ static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerOb
 #pragma mark -
 #pragma mark -
 
 
 - (NSDictionary *)HTTPRequestHeaders {
 - (NSDictionary *)HTTPRequestHeaders {
-    return [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
+    NSDictionary __block *value;
+    dispatch_sync(self.requestHeaderModificationQueue, ^{
+        value = [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
+    });
+    return value;
 }
 }
 
 
 - (void)setValue:(NSString *)value
 - (void)setValue:(NSString *)value
 forHTTPHeaderField:(NSString *)field
 forHTTPHeaderField:(NSString *)field
 {
 {
-	[self.mutableHTTPRequestHeaders setValue:value forKey:field];
+    dispatch_barrier_async(self.requestHeaderModificationQueue, ^{
+        [self.mutableHTTPRequestHeaders setValue:value forKey:field];
+    });
 }
 }
 
 
 - (NSString *)valueForHTTPHeaderField:(NSString *)field {
 - (NSString *)valueForHTTPHeaderField:(NSString *)field {
-    return [self.mutableHTTPRequestHeaders valueForKey:field];
+    NSString __block *value;
+    dispatch_sync(self.requestHeaderModificationQueue, ^{
+        value = [self.mutableHTTPRequestHeaders valueForKey:field];
+    });
+    return value;
 }
 }
 
 
 - (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
 - (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
@@ -322,7 +334,9 @@ forHTTPHeaderField:(NSString *)field
 }
 }
 
 
 - (void)clearAuthorizationHeader {
 - (void)clearAuthorizationHeader {
-	[self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"];
+    dispatch_barrier_async(self.requestHeaderModificationQueue, ^{
+        [self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"];
+    });
 }
 }
 
 
 #pragma mark -
 #pragma mark -
@@ -554,7 +568,9 @@ forHTTPHeaderField:(NSString *)field
 }
 }
 
 
 - (void)encodeWithCoder:(NSCoder *)coder {
 - (void)encodeWithCoder:(NSCoder *)coder {
-    [coder encodeObject:self.mutableHTTPRequestHeaders forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))];
+    dispatch_sync(self.requestHeaderModificationQueue, ^{
+        [coder encodeObject:self.mutableHTTPRequestHeaders forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))];
+    });
     [coder encodeInteger:self.queryStringSerializationStyle forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))];
     [coder encodeInteger:self.queryStringSerializationStyle forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))];
 }
 }
 
 
@@ -562,7 +578,9 @@ forHTTPHeaderField:(NSString *)field
 
 
 - (instancetype)copyWithZone:(NSZone *)zone {
 - (instancetype)copyWithZone:(NSZone *)zone {
     AFHTTPRequestSerializer *serializer = [[[self class] allocWithZone:zone] init];
     AFHTTPRequestSerializer *serializer = [[[self class] allocWithZone:zone] init];
-    serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone];
+    dispatch_sync(self.requestHeaderModificationQueue, ^{
+        serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone];
+    });
     serializer.queryStringSerializationStyle = self.queryStringSerializationStyle;
     serializer.queryStringSerializationStyle = self.queryStringSerializationStyle;
     serializer.queryStringSerialization = self.queryStringSerialization;
     serializer.queryStringSerialization = self.queryStringSerialization;
 
 

+ 22 - 0
Tests/Tests/AFHTTPRequestSerializationTests.m

@@ -189,6 +189,28 @@
     XCTAssertFalse([serializer.HTTPRequestHeaders.allKeys containsObject:headerField]);
     XCTAssertFalse([serializer.HTTPRequestHeaders.allKeys containsObject:headerField]);
 }
 }
 
 
+- (void)testThatHTTPHeaderValueCanBeSetToReferenceCountedStringFromMultipleThreadsWithoutCrashing {
+    @autoreleasepool {
+        int dispatchTarget = 1000;
+        __block int completionCount = 0;
+        for(int i=0; i<dispatchTarget; i++) {
+            NSString *nonStaticNonTaggedPointerString = [NSString stringWithFormat:@"%@", [NSDate dateWithTimeIntervalSince1970:i]];
+            dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
+            dispatch_async(queue, ^{
+                
+                [self.requestSerializer setValue:nonStaticNonTaggedPointerString forHTTPHeaderField:@"FrequentlyUpdatedHeaderField"];
+                
+                dispatch_sync(dispatch_get_main_queue(), ^{
+                    completionCount++;
+                });
+            });
+        }
+        while (completionCount < dispatchTarget) {
+            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
+        }
+    } // Test succeeds if it does not EXC_BAD_ACCESS when cleaning up the @autoreleasepool
+}
+
 #pragma mark - Helper Methods
 #pragma mark - Helper Methods
 
 
 - (void)testQueryStringFromParameters {
 - (void)testQueryStringFromParameters {