AutoRecoveringHttpDataSource.m 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /**********************************************************************************
  2. AudioPlayer.m
  3. Created by Thong Nguyen on 16/10/2012.
  4. https://github.com/tumtumtum/audjustable
  5. Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
  6. Redistribution and use in source and binary forms, with or without
  7. modification, are permitted provided that the following conditions are met:
  8. 1. Redistributions of source code must retain the above copyright
  9. notice, this list of conditions and the following disclaimer.
  10. 2. Redistributions in binary form must reproduce the above copyright
  11. notice, this list of conditions and the following disclaimer in the
  12. documentation and/or other materials provided with the distribution.
  13. 3. All advertising materials mentioning features or use of this software
  14. must display the following acknowledgement:
  15. This product includes software developed by Thong Nguyen (tumtumtum@gmail.com)
  16. 4. Neither the name of Thong Nguyen nor the
  17. names of its contributors may be used to endorse or promote products
  18. derived from this software without specific prior written permission.
  19. THIS SOFTWARE IS PROVIDED BY Thong Nguyen''AS IS'' AND ANY
  20. EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  21. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  22. DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  23. DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  24. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  25. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  26. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  28. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. **********************************************************************************/
  30. #import <sys/socket.h>
  31. #import <netinet/in.h>
  32. #import <netinet6/in6.h>
  33. #import <arpa/inet.h>
  34. #import <ifaddrs.h>
  35. #import <netdb.h>
  36. #import <Foundation/Foundation.h>
  37. #import <SystemConfiguration/SystemConfiguration.h>
  38. #import "AutoRecoveringHttpDataSource.h"
  39. #define MAX_IMMEDIATE_RECONNECT_ATTEMPTS (8)
  40. #define MAX_ATTEMPTS_WITH_SERVER_ERROR (MAX_IMMEDIATE_RECONNECT_ATTEMPTS + 2)
  41. @interface AutoRecoveringHttpDataSource()
  42. {
  43. int reconnectAttempts;
  44. BOOL waitingForNetwork;
  45. SCNetworkReachabilityRef reachabilityRef;
  46. }
  47. -(void) reachabilityChanged;
  48. @end
  49. static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
  50. {
  51. @autoreleasepool
  52. {
  53. AutoRecoveringHttpDataSource* dataSource = (__bridge AutoRecoveringHttpDataSource*)info;
  54. [dataSource reachabilityChanged];
  55. }
  56. }
  57. @implementation AutoRecoveringHttpDataSource
  58. -(HttpDataSource*) innerHttpDataSource
  59. {
  60. return (HttpDataSource*)self.innerDataSource;
  61. }
  62. -(id) initWithHttpDataSource:(HttpDataSource*)innerDataSourceIn
  63. {
  64. if (self = [super initWithDataSource:innerDataSourceIn])
  65. {
  66. self.innerDataSource.delegate = self;
  67. struct sockaddr_in zeroAddress;
  68. bzero(&zeroAddress, sizeof(zeroAddress));
  69. zeroAddress.sin_len = sizeof(zeroAddress);
  70. zeroAddress.sin_family = AF_INET;
  71. reachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)&zeroAddress);
  72. }
  73. return self;
  74. }
  75. -(BOOL) startNotifierOnRunLoop:(NSRunLoop*)runLoop
  76. {
  77. BOOL retVal = NO;
  78. SCNetworkReachabilityContext context = { 0, (__bridge void*)self, NULL, NULL, NULL };
  79. if (SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context))
  80. {
  81. if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, runLoop.getCFRunLoop, kCFRunLoopDefaultMode))
  82. {
  83. retVal = YES;
  84. }
  85. }
  86. return retVal;
  87. }
  88. -(BOOL) registerForEvents:(NSRunLoop*)runLoop
  89. {
  90. [super registerForEvents:runLoop];
  91. [self startNotifierOnRunLoop:runLoop];
  92. return YES;
  93. }
  94. -(void) unregisterForEvents
  95. {
  96. [self stopNotifier];
  97. }
  98. -(void) stopNotifier
  99. {
  100. if (reachabilityRef != NULL)
  101. {
  102. SCNetworkReachabilitySetCallback(reachabilityRef, NULL, NULL);
  103. SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  104. }
  105. }
  106. -(BOOL) hasGotNetworkConnection
  107. {
  108. SCNetworkReachabilityFlags flags;
  109. if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
  110. {
  111. return ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
  112. }
  113. return NO;
  114. }
  115. -(void) dealloc
  116. {
  117. self.innerDataSource.delegate = nil;
  118. [self stopNotifier];
  119. [NSObject cancelPreviousPerformRequestsWithTarget:self];
  120. if (reachabilityRef!= NULL)
  121. {
  122. CFRelease(reachabilityRef);
  123. }
  124. }
  125. -(void) reachabilityChanged
  126. {
  127. if (waitingForNetwork)
  128. {
  129. waitingForNetwork = NO;
  130. [self attemptReconnect];
  131. }
  132. }
  133. -(void) dataSourceDataAvailable:(DataSource*)dataSource
  134. {
  135. reconnectAttempts = 0;
  136. [super dataSourceDataAvailable:dataSource];
  137. }
  138. -(void) attemptReconnect
  139. {
  140. reconnectAttempts++;
  141. [self seekToOffset:self.position];
  142. }
  143. -(void) processRetryOnError
  144. {
  145. if (![self hasGotNetworkConnection])
  146. {
  147. waitingForNetwork = YES;
  148. return;
  149. }
  150. if (!(self.innerDataSource.httpStatusCode >= 200 && self.innerDataSource.httpStatusCode <= 299) && reconnectAttempts >= MAX_ATTEMPTS_WITH_SERVER_ERROR)
  151. {
  152. [super dataSourceErrorOccured:self];
  153. }
  154. else if (reconnectAttempts > MAX_IMMEDIATE_RECONNECT_ATTEMPTS)
  155. {
  156. [self performSelector:@selector(attemptReconnect) withObject:nil afterDelay:5];
  157. }
  158. else
  159. {
  160. [self attemptReconnect];
  161. }
  162. }
  163. -(void) dataSourceEof:(DataSource*)dataSource
  164. {
  165. if ([self position] != [self length])
  166. {
  167. [self processRetryOnError];
  168. return;
  169. }
  170. [self.delegate dataSourceEof:self];
  171. }
  172. -(void) dataSourceErrorOccured:(DataSource*)dataSource
  173. {
  174. if (self.innerDataSource.httpStatusCode == 416 /* Range out of bounds */)
  175. {
  176. [super dataSourceEof:dataSource];
  177. }
  178. else
  179. {
  180. [self processRetryOnError];
  181. }
  182. }
  183. -(NSString*) description
  184. {
  185. return [NSString stringWithFormat:@"Auto-recovering HTTP data source with file length: %lld and position: %lld", self.length, self.position];
  186. }
  187. @end