diff --git a/CHANGELOG.md b/CHANGELOG.md index 33e13514..a648a15d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ _You can download previous versions [here](https://github.com/AliSoftware/OHHTTPStubs/tags) and latest version [here](https://github.com/AliSoftware/OHHTTPStubs/zipball/master) (ZIP files generated by GitHub on the fly)_ +## [3.1.13](https://github.com/AliSoftware/OHHTTPStubs/releases/tag/3.1.13) + +* Fix threading in NSURLProtocol subclass calling NSURLProtocolClient callbacks from wrong thread. ([@nsprogrammer](https://github.com/nsprogrammer), [#96](https://github.com/AliSoftware/OHHTTPStubs/pull/96)) + ## [3.1.12](https://github.com/AliSoftware/OHHTTPStubs/releases/tag/3.1.12) * Fixed issue with HTTP 300 return code (multiple-choice) that is not supposed to redirect. ([@tarbrain](https://github.com/tarbrain), [#92](https://github.com/AliSoftware/OHHTTPStubs/pull/92)) @@ -212,4 +216,4 @@ _You will now have to indicate the folder containing headers for `OHHTTPStubs` i ## [0.1.0](https://github.com/AliSoftware/OHHTTPStubs/tree/0.1.0) -* Initial version \ No newline at end of file +* Initial version diff --git a/OHHTTPStubs/Sources/OHHTTPStubs.m b/OHHTTPStubs/Sources/OHHTTPStubs.m index 85d808eb..0e349c0d 100644 --- a/OHHTTPStubs/Sources/OHHTTPStubs.m +++ b/OHHTTPStubs/Sources/OHHTTPStubs.m @@ -280,6 +280,8 @@ - (OHHTTPStubsDescriptor*)firstStubPassingTestForRequest:(NSURLRequest*)request @interface OHHTTPStubsProtocol() @property(assign) BOOL stopped; @property(strong) OHHTTPStubsDescriptor* stub; +@property(assign) CFRunLoopRef clientRunLoop; +- (void)executeOnClientRunLoopAfterDelay:(NSTimeInterval)delayInSeconds block:(dispatch_block_t)block; @end @implementation OHHTTPStubsProtocol @@ -309,6 +311,7 @@ - (NSCachedURLResponse *)cachedResponse - (void)startLoading { + self.clientRunLoop = CFRunLoopGetCurrent(); NSURLRequest* request = self.request; id client = self.client; @@ -365,16 +368,16 @@ - (void)startLoading if (((responseStub.statusCode > 300) && (responseStub.statusCode < 400)) && redirectLocationURL) { NSURLRequest* redirectRequest = [NSURLRequest requestWithURL:redirectLocationURL]; - execute_after(responseStub.requestTime, ^{ + [self executeOnClientRunLoopAfterDelay:responseStub.requestTime block:^{ if (!self.stopped) { [client URLProtocol:self wasRedirectedToRequest:redirectRequest redirectResponse:urlResponse]; } - }); + }]; } else { - execute_after(responseStub.requestTime,^{ + [self executeOnClientRunLoopAfterDelay:responseStub.requestTime block:^{ if (!self.stopped) { [client URLProtocol:self didReceiveResponse:urlResponse cacheStoragePolicy:NSURLCacheStorageNotAllowed]; @@ -397,16 +400,16 @@ - (void)startLoading } }]; } - }); + }]; } } else { // Send the canned error - execute_after(responseStub.responseTime, ^{ + [self executeOnClientRunLoopAfterDelay:responseStub.responseTime block:^{ if (!self.stopped) { [client URLProtocol:self didFailWithError:responseStub.error]; } - }); + }]; } } @@ -482,10 +485,10 @@ - (void) streamDataForClient:(id)client if (chunkSizeToRead == 0) { // Nothing to read at this pass, but probably later - execute_after(timingInfo.slotTime, ^{ + [self executeOnClientRunLoopAfterDelay:timingInfo.slotTime block:^{ [self streamDataForClient:client fromStream:inputStream timingInfo:timingInfo completion:completion]; - }); + }]; } else { uint8_t* buffer = (uint8_t*)malloc(sizeof(uint8_t)*chunkSizeToRead); NSInteger bytesRead = [inputStream read:buffer maxLength:chunkSizeToRead]; @@ -494,11 +497,11 @@ - (void) streamDataForClient:(id)client NSData * data = [NSData dataWithBytes:buffer length:bytesRead]; // Wait for 'slotTime' seconds before sending the chunk. // If bytesRead < chunkSizePerSlot (because we are near the EOF), adjust slotTime proportionally to the bytes remaining - execute_after(((double)bytesRead / (double)chunkSizeToRead) * timingInfo.slotTime, ^{ + [self executeOnClientRunLoopAfterDelay:((double)bytesRead / (double)chunkSizeToRead) * timingInfo.slotTime block:^{ [client URLProtocol:self didLoadData:data]; [self streamDataForClient:client fromStream:inputStream timingInfo:timingInfo completion:completion]; - }); + }]; } else { @@ -526,10 +529,13 @@ - (void) streamDataForClient:(id)client // Delayed execution utility methods ///////////////////////////////////////////// -static void execute_after(NSTimeInterval delayInSeconds, dispatch_block_t block) +- (void)executeOnClientRunLoopAfterDelay:(NSTimeInterval)delayInSeconds block:(dispatch_block_t)block { dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); - dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); + dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + CFRunLoopPerformBlock(self.clientRunLoop, kCFRunLoopDefaultMode, block); + CFRunLoopWakeUp(self.clientRunLoop); + }); } @end