diff --git a/SPDY/SPDYSession.h b/SPDY/SPDYSession.h index d8ae1df..0355e26 100644 --- a/SPDY/SPDYSession.h +++ b/SPDY/SPDYSession.h @@ -16,10 +16,17 @@ @class SPDYOrigin; @interface SPDYSession : NSObject + @property (nonatomic, readonly) SPDYOrigin *origin; +@property (nonatomic, readonly) bool isCellular; @property (nonatomic, readonly) bool isOpen; -- (id)initWithOrigin:(SPDYOrigin *)origin configuration:(SPDYConfiguration *)configuration error:(NSError **)pError; + +- (id)initWithOrigin:(SPDYOrigin *)origin + configuration:(SPDYConfiguration *)configuration + cellular:(bool)cellular + error:(NSError **)pError; - (void)issueRequest:(SPDYProtocol *)protocol; - (void)cancelRequest:(SPDYProtocol *)protocol; - (void)close; + @end diff --git a/SPDY/SPDYSession.m b/SPDY/SPDYSession.m index f3b787b..e6fd33b 100644 --- a/SPDY/SPDYSession.m +++ b/SPDY/SPDYSession.m @@ -69,9 +69,13 @@ @implementation SPDYSession bool _enableSettingsMinorVersion; bool _receivedGoAwayFrame; bool _sentGoAwayFrame; + bool _cellular; } -- (id)initWithOrigin:(SPDYOrigin *)origin configuration:(SPDYConfiguration *)configuration error:(NSError **)pError +- (id)initWithOrigin:(SPDYOrigin *)origin + configuration:(SPDYConfiguration *)configuration + cellular:(bool)cellular + error:(NSError **)pError { NSParameterAssert(origin != nil); @@ -98,6 +102,9 @@ - (id)initWithOrigin:(SPDYOrigin *)origin configuration:(SPDYConfiguration *)con _origin = origin; SPDY_INFO(@"session connecting to %@", _origin); + // TODO: for accuracy confirm this later from the socket + _cellular = cellular; + if ([_origin.scheme isEqualToString:@"https"]) { SPDY_DEBUG(@"session using TLS"); [_socket secureWithTLS:configuration.tlsSettings]; @@ -221,6 +228,11 @@ - (void)dealloc [_socket disconnect]; } +- (bool)isCellular +{ + return _cellular; +} + - (bool)isOpen { return (!_receivedGoAwayFrame && !_sentGoAwayFrame); diff --git a/SPDY/SPDYSessionManager.h b/SPDY/SPDYSessionManager.h index 9a15a39..bb50d11 100644 --- a/SPDY/SPDYSessionManager.h +++ b/SPDY/SPDYSessionManager.h @@ -15,7 +15,9 @@ @class SPDYSession; @interface SPDYSessionManager : NSObject -+ (void)setConfiguration:(SPDYConfiguration *)configuration; + + (SPDYSession *)sessionForURL:(NSURL *)url error:(NSError **)pError; + (void)sessionClosed:(SPDYSession *)session; ++ (void)setConfiguration:(SPDYConfiguration *)configuration; + @end diff --git a/SPDY/SPDYSessionManager.m b/SPDY/SPDYSessionManager.m index 72332b1..7e31fd7 100644 --- a/SPDY/SPDYSessionManager.m +++ b/SPDY/SPDYSessionManager.m @@ -13,34 +13,59 @@ #error "This file requires ARC support." #endif +#import +#import +#import "SPDYCommonLogger.h" #import "SPDYOrigin.h" -#import "SPDYSessionManager.h" #import "SPDYProtocol.h" #import "SPDYSession.h" +#import "SPDYSessionManager.h" @interface SPDYSessionManager () -+ (NSMutableDictionary *)activeSessions; ++ (NSMutableDictionary *)_sessionPool:(bool)network; @end static NSString *const SPDYSessionManagerKey = @"com.twitter.SPDYSessionManager"; static SPDYConfiguration *currentConfiguration; +static volatile bool reachabilityIsWWAN; + +#if TARGET_OS_IPHONE +static char *const SPDYReachabilityQueue = "com.twitter.SPDYReachabilityQueue"; + +static SCNetworkReachabilityRef reachabilityRef; +static dispatch_queue_t reachabilityQueue; + +static void SPDYReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info); +#endif @implementation SPDYSessionManager + (void)initialize { currentConfiguration = [SPDYConfiguration defaultConfiguration]; -} + reachabilityIsWWAN = NO; -+ (NSMutableDictionary *)activeSessions -{ - NSMutableDictionary *threadDictionary = [NSThread currentThread].threadDictionary; - NSMutableDictionary *activeSessions = threadDictionary[SPDYSessionManagerKey]; - if (!activeSessions) { - activeSessions = [NSMutableDictionary new]; - threadDictionary[SPDYSessionManagerKey] = activeSessions; +#if TARGET_OS_IPHONE + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = (uint8_t)sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + + SCNetworkReachabilityContext context = {0, NULL, NULL, NULL, NULL}; + reachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)&zeroAddress); + + if (SCNetworkReachabilitySetCallback(reachabilityRef, SPDYReachabilityCallback, &context)) { + reachabilityQueue = dispatch_queue_create(SPDYReachabilityQueue, DISPATCH_QUEUE_SERIAL); + SCNetworkReachabilitySetDispatchQueue(reachabilityRef, reachabilityQueue); } - return activeSessions; + + dispatch_async(reachabilityQueue, ^{ + SCNetworkReachabilityFlags flags; + if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) { + SPDYReachabilityCallback(reachabilityRef, flags, NULL); + } + }); +#endif } + (void)setConfiguration:(SPDYConfiguration *)configuration @@ -51,11 +76,12 @@ + (void)setConfiguration:(SPDYConfiguration *)configuration + (SPDYSession *)sessionForURL:(NSURL *)url error:(NSError **)pError { SPDYOrigin *origin = [[SPDYOrigin alloc] initWithURL:url error:pError]; - NSMutableDictionary *activeSessions = [SPDYSessionManager activeSessions]; + NSMutableDictionary *activeSessions = [SPDYSessionManager _sessionPool:reachabilityIsWWAN]; SPDYSession *session = activeSessions[origin]; if (!session || !session.isOpen) { session = [[SPDYSession alloc] initWithOrigin:origin configuration:currentConfiguration + cellular:reachabilityIsWWAN error:pError]; if (session) { activeSessions[origin] = session; @@ -67,10 +93,36 @@ + (SPDYSession *)sessionForURL:(NSURL *)url error:(NSError **)pError + (void)sessionClosed:(SPDYSession *)session { SPDYOrigin *origin = session.origin; - NSMutableDictionary *activeSessions = [SPDYSessionManager activeSessions]; + NSMutableDictionary *activeSessions = [SPDYSessionManager _sessionPool:session.isCellular]; if (activeSessions[origin] == session) { [activeSessions removeObjectForKey:origin]; } } ++ (NSMutableDictionary *)_sessionPool:(bool)cellular +{ + NSMutableDictionary *threadDictionary = [NSThread currentThread].threadDictionary; + NSArray *sessionPools = threadDictionary[SPDYSessionManagerKey]; + if (!sessionPools) { + threadDictionary[SPDYSessionManagerKey] = @[ + [NSMutableDictionary new], + [NSMutableDictionary new] // WWAN + ]; + } + + SPDY_DEBUG(@"using %@ session pool", cellular ? @"cellular" : @"standard"); + return sessionPools[cellular ? 1 : 0]; +} + @end + +#if TARGET_OS_IPHONE +static void SPDYReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) +{ + // Only update if the network is actually reachable + if (flags & kSCNetworkReachabilityFlagsReachable) { + reachabilityIsWWAN = (flags & kSCNetworkReachabilityFlagsIsWWAN) != 0; + SPDY_DEBUG(@"reachability updated: %@", reachabilityIsWWAN ? @"WWAN" : @"WLAN"); + } +} +#endif diff --git a/SPDY/SPDYSocket.m b/SPDY/SPDYSocket.m index 2f32936..39a61a6 100644 --- a/SPDY/SPDYSocket.m +++ b/SPDY/SPDYSocket.m @@ -15,11 +15,11 @@ #error "This file requires ARC support." #endif +#import +#import +#import #import "SPDYSocket.h" #import "SPDYCommonLogger.h" -#import -#import -#import #pragma mark Declarations