Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

Commit

Permalink
Reconnect sockets on iOS when resuming from background
Browse files Browse the repository at this point in the history
This allows playback to be restarted, and GCDWebServer doesn't have to be reinitialized.
  • Loading branch information
Mbaize authored and Joe Hainline and Micah Baize committed Feb 7, 2017
1 parent fbcf3fa commit f459630
Showing 1 changed file with 112 additions and 12 deletions.
124 changes: 112 additions & 12 deletions GCDWebServer/Core/GCDWebServer.m
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ @interface GCDWebServer () {
dispatch_source_t _source6;
CFNetServiceRef _registrationService;
CFNetServiceRef _resolutionService;
int ipv4ListeningSocket;
int ipv6ListeningSocket;
DNSServiceRef _dnsService;
CFSocketRef _dnsSocket;
CFRunLoopSourceRef _dnsSource;
Expand Down Expand Up @@ -463,6 +465,11 @@ - (int)_createListeningSocket:(BOOL)useIPv6
error:(NSError**)error {
int listeningSocket = socket(useIPv6 ? PF_INET6 : PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listeningSocket > 0) {
if (!useIPv6) {
ipv4ListeningSocket = listeningSocket;
} else {
ipv6ListeningSocket = listeningSocket;
}
int yes = 1;
setsockopt(listeningSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));

Expand Down Expand Up @@ -570,12 +577,7 @@ - (BOOL)_start:(NSError**)error {
}
}

struct sockaddr_in6 addr6;
bzero(&addr6, sizeof(addr6));
addr6.sin6_len = sizeof(addr6);
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(port);
addr6.sin6_addr = bindToLocalhost ? in6addr_loopback : in6addr_any;
struct sockaddr_in6 addr6 = [self generateAddressWithPort:port bindToLocalhost:bindToLocalhost];
int listeningSocket6 = [self _createListeningSocket:YES localAddress:&addr6 length:sizeof(addr6) maxPendingConnections:maxPendingConnections error:error];
if (listeningSocket6 <= 0) {
close(listeningSocket4);
Expand Down Expand Up @@ -740,6 +742,81 @@ - (void)_stop {

#if TARGET_OS_IPHONE

- (void)resetIpv4SocketIfError {
int error = 0;
socklen_t len = sizeof(error);
int retval = getsockopt(ipv4ListeningSocket, SOL_SOCKET, SO_ERROR, &error, &len);

if (retval != 0) {
/* there was a problem getting the error code */
GWS_LOG_ERROR(@"error getting socket error code: %s\n", strerror(retval));
return;
}

if (error != 0) {
/* socket has a non zero error status */
GWS_LOG_INFO(@"Socket error: %s on socket %d\n", strerror(error), ipv4ListeningSocket);
dispatch_source_cancel(_source4);
_source4 = nil;
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
BOOL bindToLocalhost = [_GetOption(_options, GCDWebServerOption_BindToLocalhost, @NO) boolValue];
NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];

struct sockaddr_in addr4 = [self generateIpv4AddressWithPort:port bindToLocalhost:bindToLocalhost];
NSError* nsError = nil;
int listeningSocket4 = [self _createListeningSocket:NO
localAddress:&addr4
length:sizeof(addr4)
maxPendingConnections:maxPendingConnections
error:&nsError];

if (listeningSocket4 <= 0) {
GWS_LOG_ERROR(@"Failed to create the IPv4 socket\n");
}
_source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO];
// Need to investigate
dispatch_resume(_source4);
}
}

- (void)resetIpv6SocketIfError {
int error = 0;
NSError* nsError = nil;
socklen_t len = sizeof(error);
int retval = getsockopt(ipv6ListeningSocket, SOL_SOCKET, SO_ERROR, &error, &len);

if (retval != 0) {
/* there was a problem getting the error code */
GWS_LOG_ERROR(@"error getting socket error code: %s\n", strerror(retval));
return;
}

if (error != 0) {
/* socket has a non zero error status */
GWS_LOG_INFO(@"Socket error: %s on socket %d\n", strerror(error), ipv6ListeningSocket);

dispatch_source_cancel(_source6);
_source6 = nil;
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
BOOL bindToLocalhost = [_GetOption(_options, GCDWebServerOption_BindToLocalhost, @NO) boolValue];
NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];

struct sockaddr_in6 addr6 = [self generateAddressWithPort:port bindToLocalhost:bindToLocalhost];
int listeningSocket6 = [self _createListeningSocket:YES
localAddress:&addr6
length:sizeof(addr6)
maxPendingConnections:maxPendingConnections
error:&nsError];
if (listeningSocket6 <= 0) {
GWS_LOG_ERROR(@"Failed to create the IPv6 socket\n");
}
_source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES];

// Need to investigate
dispatch_resume(_source6);
}
}

- (void)_didEnterBackground:(NSNotification*)notification {
GWS_DCHECK([NSThread isMainThread]);
GWS_LOG_DEBUG(@"Did enter background");
Expand All @@ -751,13 +828,39 @@ - (void)_didEnterBackground:(NSNotification*)notification {
- (void)_willEnterForeground:(NSNotification*)notification {
GWS_DCHECK([NSThread isMainThread]);
GWS_LOG_DEBUG(@"Will enter foreground");
if (!_source4) {

if (_suspendInBackground && !_source4) {
[self _start:NULL]; // TODO: There's probably nothing we can do on failure
}

if ([self isRunning]) {
[self resetIpv4SocketIfError];
[self resetIpv6SocketIfError];
}
}

#endif

- (struct sockaddr_in)generateIpv4AddressWithPort:(NSInteger)port bindToLocalhost:(BOOL)bindToLocalhost {
struct sockaddr_in addr4;
bzero(&addr4, sizeof(addr4));
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(port);
addr4.sin_addr.s_addr = bindToLocalhost ? htonl(INADDR_LOOPBACK) : htonl(INADDR_ANY);
return addr4;
}

- (struct sockaddr_in6)generateAddressWithPort:(NSInteger)port bindToLocalhost:(BOOL)bindToLocalhost {
struct sockaddr_in6 addr6;
bzero(&addr6, sizeof(addr6));
addr6.sin6_len = sizeof(addr6);
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(port);
addr6.sin6_addr = bindToLocalhost ? in6addr_loopback : in6addr_any;
return addr6;
}

- (BOOL)startWithOptions:(NSDictionary*)options error:(NSError**)error {
if (_options == nil) {
_options = options ? [options copy] : @{};
Expand All @@ -774,8 +877,8 @@ - (BOOL)startWithOptions:(NSDictionary*)options error:(NSError**)error {
#if TARGET_OS_IPHONE
if (_suspendInBackground) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
#endif
return YES;
} else {
Expand All @@ -791,10 +894,7 @@ - (BOOL)isRunning {
- (void)stop {
if (_options) {
#if TARGET_OS_IPHONE
if (_suspendInBackground) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
}
[[NSNotificationCenter defaultCenter] removeObserver:self];
#endif
if (_source4) {
[self _stop];
Expand Down

0 comments on commit f459630

Please sign in to comment.