#85 Added support for IPv6

This commit is contained in:
Pierre-Olivier Latour
2014-10-14 00:45:08 -07:00
parent 4449e42601
commit 75d018a375
7 changed files with 224 additions and 174 deletions
+186 -145
View File
@@ -155,7 +155,7 @@ static void _ExecuteMainThreadRunLoopSources() {
@private
id<GCDWebServerDelegate> __unsafe_unretained _delegate;
dispatch_queue_t _syncQueue;
dispatch_semaphore_t _sourceSemaphore;
dispatch_group_t _sourceGroup;
NSMutableArray* _handlers;
NSInteger _activeConnections; // Accessed through _syncQueue only
BOOL _connected; // Accessed on main thread only
@@ -170,7 +170,8 @@ static void _ExecuteMainThreadRunLoopSources() {
BOOL _mapHEADToGET;
CFTimeInterval _disconnectDelay;
NSUInteger _port;
dispatch_source_t _source;
dispatch_source_t _source4;
dispatch_source_t _source6;
CFNetServiceRef _registrationService;
CFNetServiceRef _resolutionService;
#if TARGET_OS_IPHONE
@@ -196,7 +197,7 @@ static void _ExecuteMainThreadRunLoopSources() {
- (instancetype)init {
if ((self = [super init])) {
_syncQueue = dispatch_queue_create([NSStringFromClass([self class]) UTF8String], DISPATCH_QUEUE_SERIAL);
_sourceSemaphore = dispatch_semaphore_create(0);
_sourceGroup = dispatch_group_create();
_handlers = [[NSMutableArray alloc] init];
#if TARGET_OS_IPHONE
_backgroundTask = UIBackgroundTaskInvalid;
@@ -212,7 +213,7 @@ static void _ExecuteMainThreadRunLoopSources() {
GWS_DCHECK(_disconnectTimer == NULL); // The server can never be dealloc'ed while the disconnect timer is pending because of the retain-cycle
ARC_RELEASE(_handlers);
ARC_DISPATCH_RELEASE(_sourceSemaphore);
ARC_DISPATCH_RELEASE(_sourceGroup);
ARC_DISPATCH_RELEASE(_syncQueue);
ARC_DEALLOC(super);
@@ -281,7 +282,7 @@ static void _ExecuteMainThreadRunLoopSources() {
- (void)_endBackgroundTask {
GWS_DCHECK([NSThread isMainThread]);
if (_backgroundTask != UIBackgroundTaskInvalid) {
if (_suspendInBackground && ([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground) && _source) {
if (_suspendInBackground && ([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground) && _source4) {
[self _stop];
}
[[UIApplication sharedApplication] endBackgroundTask:_backgroundTask];
@@ -316,7 +317,7 @@ static void _ExecuteMainThreadRunLoopSources() {
_activeConnections -= 1;
if (_activeConnections == 0) {
dispatch_async(dispatch_get_main_queue(), ^{
if ((_disconnectDelay > 0.0) && (_source != NULL)) {
if ((_disconnectDelay > 0.0) && (_source4 != NULL)) {
if (_disconnectTimer) {
CFRunLoopTimerInvalidate(_disconnectTimer);
CFRelease(_disconnectTimer);
@@ -409,160 +410,197 @@ static inline NSString* _EncodeBase64(NSString* string) {
return ARC_AUTORELEASE([[NSString alloc] initWithData:[data base64EncodedDataWithOptions:0] encoding:NSASCIIStringEncoding]);
}
- (BOOL)_start:(NSError**)error {
GWS_DCHECK(_source == NULL);
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
NSString* bonjourName = _GetOption(_options, GCDWebServerOption_BonjourName, @"");
NSString* bonjourType = _GetOption(_options, GCDWebServerOption_BonjourType, @"_http._tcp");
NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];
int listeningSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
- (int)_createListeningSocket:(BOOL)useIPv6
localAddress:(const void*)address
length:(socklen_t)length
maxPendingConnections:(NSUInteger)maxPendingConnections
error:(NSError**)error {
int listeningSocket = socket(useIPv6 ? PF_INET6 : PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listeningSocket > 0) {
int yes = 1;
setsockopt(listeningSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
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 = htonl(INADDR_ANY);
if (bind(listeningSocket, (void*)&addr4, sizeof(addr4)) == 0) {
if (bind(listeningSocket, address, length) == 0) {
if (listen(listeningSocket, (int)maxPendingConnections) == 0) {
GWS_LOG_DEBUG(@"Did open listening socket %i", listeningSocket);
_serverName = [_GetOption(_options, GCDWebServerOption_ServerName, NSStringFromClass([self class])) copy];
NSString* authenticationMethod = _GetOption(_options, GCDWebServerOption_AuthenticationMethod, nil);
if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_Basic]) {
_authenticationRealm = [_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy];
_authenticationBasicAccounts = [[NSMutableDictionary alloc] init];
NSDictionary* accounts = _GetOption(_options, GCDWebServerOption_AuthenticationAccounts, @{});
[accounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* password, BOOL* stop) {
[_authenticationBasicAccounts setObject:_EncodeBase64([NSString stringWithFormat:@"%@:%@", username, password]) forKey:username];
}];
} else if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_DigestAccess]) {
_authenticationRealm = [_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy];
_authenticationDigestAccounts = [[NSMutableDictionary alloc] init];
NSDictionary* accounts = _GetOption(_options, GCDWebServerOption_AuthenticationAccounts, @{});
[accounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* password, BOOL* stop) {
[_authenticationDigestAccounts setObject:GCDWebServerComputeMD5Digest(@"%@:%@:%@", username, _authenticationRealm, password) forKey:username];
}];
}
_connectionClass = _GetOption(_options, GCDWebServerOption_ConnectionClass, [GCDWebServerConnection class]);
_mapHEADToGET = [_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue];
_disconnectDelay = [_GetOption(_options, GCDWebServerOption_ConnectedStateCoalescingInterval, @1.0) doubleValue];
_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, listeningSocket, 0, kGCDWebServerGCDQueue);
dispatch_source_set_cancel_handler(_source, ^{
@autoreleasepool {
int result = close(listeningSocket);
if (result != 0) {
GWS_LOG_ERROR(@"Failed closing listening socket: %s (%i)", strerror(errno), errno);
} else {
GWS_LOG_DEBUG(@"Did close listening socket %i", listeningSocket);
}
}
dispatch_semaphore_signal(_sourceSemaphore);
});
dispatch_source_set_event_handler(_source, ^{
@autoreleasepool {
struct sockaddr remoteSockAddr;
socklen_t remoteAddrLen = sizeof(remoteSockAddr);
int socket = accept(listeningSocket, &remoteSockAddr, &remoteAddrLen);
if (socket > 0) {
NSData* remoteAddress = [NSData dataWithBytes:&remoteSockAddr length:remoteAddrLen];
struct sockaddr localSockAddr;
socklen_t localAddrLen = sizeof(localSockAddr);
NSData* localAddress = nil;
if (getsockname(socket, &localSockAddr, &localAddrLen) == 0) {
localAddress = [NSData dataWithBytes:&localSockAddr length:localAddrLen];
} else {
GWS_DNOT_REACHED();
}
int noSigPipe = 1;
setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(noSigPipe)); // Make sure this socket cannot generate SIG_PIPE
GCDWebServerConnection* connection = [[_connectionClass alloc] initWithServer:self localAddress:localAddress remoteAddress:remoteAddress socket:socket]; // Connection will automatically retain itself while opened
#if __has_feature(objc_arc)
[connection self]; // Prevent compiler from complaining about unused variable / useless statement
#else
[connection release];
#endif
} else {
GWS_LOG_ERROR(@"Failed accepting socket: %s (%i)", strerror(errno), errno);
}
}
});
if (port == 0) {
struct sockaddr addr;
socklen_t addrlen = sizeof(addr);
if (getsockname(listeningSocket, &addr, &addrlen) == 0) {
struct sockaddr_in* sockaddr = (struct sockaddr_in*)&addr;
_port = ntohs(sockaddr->sin_port);
} else {
GWS_LOG_ERROR(@"Failed retrieving socket address: %s (%i)", strerror(errno), errno);
}
} else {
_port = port;
}
if (bonjourName) {
_registrationService = CFNetServiceCreate(kCFAllocatorDefault, CFSTR("local."), (ARC_BRIDGE CFStringRef)bonjourType, (ARC_BRIDGE CFStringRef)(bonjourName.length ? bonjourName : _serverName), (SInt32)_port);
if (_registrationService) {
CFNetServiceClientContext context = {0, (ARC_BRIDGE void*)self, NULL, NULL, NULL};
CFNetServiceSetClient(_registrationService, _NetServiceRegisterCallBack, &context);
CFNetServiceScheduleWithRunLoop(_registrationService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
CFStreamError streamError = {0};
CFNetServiceRegisterWithOptions(_registrationService, 0, &streamError);
_resolutionService = CFNetServiceCreateCopy(kCFAllocatorDefault, _registrationService);
if (_resolutionService) {
CFNetServiceSetClient(_resolutionService, _NetServiceResolveCallBack, &context);
CFNetServiceScheduleWithRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
} else {
GWS_LOG_ERROR(@"Failed creating CFNetService");
}
}
dispatch_resume(_source);
GWS_LOG_INFO(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL);
if ([_delegate respondsToSelector:@selector(webServerDidStart:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webServerDidStart:self];
});
}
GWS_LOG_DEBUG(@"Did open %s listening socket %i", useIPv6 ? "IPv6" : "IPv4", listeningSocket);
return listeningSocket;
} else {
if (error) {
*error = GCDWebServerMakePosixError(errno);
}
GWS_LOG_ERROR(@"Failed starting listening socket: %s (%i)", strerror(errno), errno);
GWS_LOG_ERROR(@"Failed starting %s listening socket: %s (%i)", useIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
close(listeningSocket);
}
} else {
if (error) {
*error = GCDWebServerMakePosixError(errno);
}
GWS_LOG_ERROR(@"Failed binding listening socket: %s (%i)", strerror(errno), errno);
GWS_LOG_ERROR(@"Failed binding %s listening socket: %s (%i)", useIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
close(listeningSocket);
}
} else {
if (error) {
*error = GCDWebServerMakePosixError(errno);
}
GWS_LOG_ERROR(@"Failed creating listening socket: %s (%i)", strerror(errno), errno);
GWS_LOG_ERROR(@"Failed creating %s listening socket: %s (%i)", useIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
}
return (_source ? YES : NO);
return -1;
}
- (dispatch_source_t)_createDispatchSourceWithListeningSocket:(int)listeningSocket isIPv6:(BOOL)isIPv6 {
dispatch_group_enter(_sourceGroup);
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, listeningSocket, 0, kGCDWebServerGCDQueue);
dispatch_source_set_cancel_handler(source, ^{
@autoreleasepool {
int result = close(listeningSocket);
if (result != 0) {
GWS_LOG_ERROR(@"Failed closing %s listening socket: %s (%i)", isIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
} else {
GWS_LOG_DEBUG(@"Did close %s listening socket %i", isIPv6 ? "IPv6" : "IPv4", listeningSocket);
}
}
dispatch_group_leave(_sourceGroup);
});
dispatch_source_set_event_handler(source, ^{
@autoreleasepool {
struct sockaddr remoteSockAddr;
socklen_t remoteAddrLen = sizeof(remoteSockAddr);
int socket = accept(listeningSocket, &remoteSockAddr, &remoteAddrLen);
if (socket > 0) {
NSData* remoteAddress = [NSData dataWithBytes:&remoteSockAddr length:remoteAddrLen];
struct sockaddr localSockAddr;
socklen_t localAddrLen = sizeof(localSockAddr);
NSData* localAddress = nil;
if (getsockname(socket, &localSockAddr, &localAddrLen) == 0) {
localAddress = [NSData dataWithBytes:&localSockAddr length:localAddrLen];
GWS_DCHECK((!isIPv6 && localSockAddr.sa_family == AF_INET) || (isIPv6 && localSockAddr.sa_family == AF_INET6));
} else {
GWS_DNOT_REACHED();
}
int noSigPipe = 1;
setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(noSigPipe)); // Make sure this socket cannot generate SIG_PIPE
GCDWebServerConnection* connection = [[_connectionClass alloc] initWithServer:self localAddress:localAddress remoteAddress:remoteAddress socket:socket]; // Connection will automatically retain itself while opened
#if __has_feature(objc_arc)
[connection self]; // Prevent compiler from complaining about unused variable / useless statement
#else
[connection release];
#endif
} else {
GWS_LOG_ERROR(@"Failed accepting %s socket: %s (%i)", isIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
}
}
});
return source;
}
- (BOOL)_start:(NSError**)error {
GWS_DCHECK(_source4 == NULL);
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];
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 = htonl(INADDR_ANY);
int listeningSocket4 = [self _createListeningSocket:NO localAddress:&addr4 length:sizeof(addr4) maxPendingConnections:maxPendingConnections error:error];
if (listeningSocket4 <= 0) {
return NO;
}
if (port == 0) {
struct sockaddr addr;
socklen_t addrlen = sizeof(addr);
if (getsockname(listeningSocket4, &addr, &addrlen) == 0) {
struct sockaddr_in* sockaddr = (struct sockaddr_in*)&addr;
port = ntohs(sockaddr->sin_port);
} else {
GWS_LOG_ERROR(@"Failed retrieving socket address: %s (%i)", strerror(errno), errno);
}
}
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 = in6addr_any;
int listeningSocket6 = [self _createListeningSocket:YES localAddress:&addr6 length:sizeof(addr6) maxPendingConnections:maxPendingConnections error:error];
if (listeningSocket6 <= 0) {
close(listeningSocket4);
return NO;
}
_serverName = [_GetOption(_options, GCDWebServerOption_ServerName, NSStringFromClass([self class])) copy];
NSString* authenticationMethod = _GetOption(_options, GCDWebServerOption_AuthenticationMethod, nil);
if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_Basic]) {
_authenticationRealm = [_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy];
_authenticationBasicAccounts = [[NSMutableDictionary alloc] init];
NSDictionary* accounts = _GetOption(_options, GCDWebServerOption_AuthenticationAccounts, @{});
[accounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* password, BOOL* stop) {
[_authenticationBasicAccounts setObject:_EncodeBase64([NSString stringWithFormat:@"%@:%@", username, password]) forKey:username];
}];
} else if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_DigestAccess]) {
_authenticationRealm = [_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy];
_authenticationDigestAccounts = [[NSMutableDictionary alloc] init];
NSDictionary* accounts = _GetOption(_options, GCDWebServerOption_AuthenticationAccounts, @{});
[accounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* password, BOOL* stop) {
[_authenticationDigestAccounts setObject:GCDWebServerComputeMD5Digest(@"%@:%@:%@", username, _authenticationRealm, password) forKey:username];
}];
}
_connectionClass = _GetOption(_options, GCDWebServerOption_ConnectionClass, [GCDWebServerConnection class]);
_mapHEADToGET = [_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue];
_disconnectDelay = [_GetOption(_options, GCDWebServerOption_ConnectedStateCoalescingInterval, @1.0) doubleValue];
_source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO];
_source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES];
_port = port;
NSString* bonjourName = _GetOption(_options, GCDWebServerOption_BonjourName, @"");
NSString* bonjourType = _GetOption(_options, GCDWebServerOption_BonjourType, @"_http._tcp");
if (bonjourName) {
_registrationService = CFNetServiceCreate(kCFAllocatorDefault, CFSTR("local."), (ARC_BRIDGE CFStringRef)bonjourType, (ARC_BRIDGE CFStringRef)(bonjourName.length ? bonjourName : _serverName), (SInt32)_port);
if (_registrationService) {
CFNetServiceClientContext context = {0, (ARC_BRIDGE void*)self, NULL, NULL, NULL};
CFNetServiceSetClient(_registrationService, _NetServiceRegisterCallBack, &context);
CFNetServiceScheduleWithRunLoop(_registrationService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
CFStreamError streamError = {0};
CFNetServiceRegisterWithOptions(_registrationService, 0, &streamError);
_resolutionService = CFNetServiceCreateCopy(kCFAllocatorDefault, _registrationService);
if (_resolutionService) {
CFNetServiceSetClient(_resolutionService, _NetServiceResolveCallBack, &context);
CFNetServiceScheduleWithRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
} else {
GWS_LOG_ERROR(@"Failed creating CFNetService");
}
}
dispatch_resume(_source4);
dispatch_resume(_source6);
GWS_LOG_INFO(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL);
if ([_delegate respondsToSelector:@selector(webServerDidStart:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webServerDidStart:self];
});
}
return YES;
}
- (void)_stop {
GWS_DCHECK(_source != NULL);
GWS_DCHECK(_source4 != NULL);
if (_registrationService) {
if (_resolutionService) {
@@ -579,10 +617,13 @@ static inline NSString* _EncodeBase64(NSString* string) {
_registrationService = NULL;
}
dispatch_source_cancel(_source);
dispatch_semaphore_wait(_sourceSemaphore, DISPATCH_TIME_FOREVER); // Wait until the cancellation handler has been called which guarantees the listening socket is closed
ARC_DISPATCH_RELEASE(_source);
_source = NULL;
dispatch_source_cancel(_source6);
dispatch_source_cancel(_source4);
dispatch_group_wait(_sourceGroup, DISPATCH_TIME_FOREVER); // Wait until the cancellation handlers have been called which guarantees the listening sockets are closed
ARC_DISPATCH_RELEASE(_source6);
_source6 = NULL;
ARC_DISPATCH_RELEASE(_source4);
_source4 = NULL;
_port = 0;
ARC_RELEASE(_serverName);
@@ -616,7 +657,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
- (void)_didEnterBackground:(NSNotification*)notification {
GWS_DCHECK([NSThread isMainThread]);
GWS_LOG_DEBUG(@"Did enter background");
if ((_backgroundTask == UIBackgroundTaskInvalid) && _source) {
if ((_backgroundTask == UIBackgroundTaskInvalid) && _source4) {
[self _stop];
}
}
@@ -624,7 +665,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
- (void)_willEnterForeground:(NSNotification*)notification {
GWS_DCHECK([NSThread isMainThread]);
GWS_LOG_DEBUG(@"Will enter foreground");
if (!_source) {
if (!_source4) {
[self _start:NULL]; // TODO: There's probably nothing we can do on failure
}
}
@@ -670,7 +711,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
}
#endif
if (_source) {
if (_source4) {
[self _stop];
}
ARC_RELEASE(_options);
@@ -685,8 +726,8 @@ static inline NSString* _EncodeBase64(NSString* string) {
@implementation GCDWebServer (Extensions)
- (NSURL*)serverURL {
if (_source) {
NSString* ipAddress = GCDWebServerGetPrimaryIPv4Address();
if (_source4) {
NSString* ipAddress = GCDWebServerGetPrimaryIPAddress(NO); // We can't really use IPv6 anyway as it doesn't work great with HTTP URLs in practice
if (ipAddress) {
if (_port != 80) {
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%i/", ipAddress, (int)_port]];
@@ -699,7 +740,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
}
- (NSURL*)bonjourServerURL {
if (_source && _resolutionService) {
if (_source4 && _resolutionService) {
NSString* name = (ARC_BRIDGE NSString*)CFNetServiceGetTargetHost(_resolutionService);
if (name.length) {
name = [name substringToIndex:(name.length - 1)]; // Strip trailing period at end of domain
+7 -2
View File
@@ -48,6 +48,11 @@
*/
@property(nonatomic, readonly) GCDWebServer* server;
/**
* Returns true if the connection is using IPv6.
*/
@property(nonatomic, readonly, getter=isUsingIPv6) BOOL usingIPv6;
/**
* Returns the address of the local peer (i.e. server) of the connection
* as a raw "struct sockaddr".
@@ -56,7 +61,7 @@
/**
* Returns the address of the local peer (i.e. server) of the connection
* as a dotted string.
* as a string.
*/
@property(nonatomic, readonly) NSString* localAddressString;
@@ -68,7 +73,7 @@
/**
* Returns the address of the remote peer (i.e. client) of the connection
* as a dotted string.
* as a string.
*/
@property(nonatomic, readonly) NSString* remoteAddressString;
+7 -15
View File
@@ -365,6 +365,11 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
}
}
- (BOOL)isUsingIPv6 {
const struct sockaddr* localSockAddr = _localAddress.bytes;
return (localSockAddr->sa_family == AF_INET6);
}
- (void)_initializeResponseHeadersWithStatusCode:(NSInteger)statusCode {
_statusCode = statusCode;
_responseMessage = CFHTTPMessageCreateResponse(kCFAllocatorDefault, statusCode, NULL, kCFHTTPVersion1_1);
@@ -622,25 +627,12 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
return self;
}
static NSString* _StringFromAddressData(NSData* data) {
NSString* string = nil;
const struct sockaddr* addr = data.bytes;
char hostBuffer[NI_MAXHOST];
char serviceBuffer[NI_MAXSERV];
if (getnameinfo(addr, addr->sa_len, hostBuffer, sizeof(hostBuffer), serviceBuffer, sizeof(serviceBuffer), NI_NUMERICHOST | NI_NUMERICSERV | NI_NOFQDN) >= 0) {
string = [NSString stringWithFormat:@"%s:%s", hostBuffer, serviceBuffer];
} else {
GWS_DNOT_REACHED();
}
return string;
}
- (NSString*)localAddressString {
return _StringFromAddressData(_localAddress);
return GCDWebServerStringFromSockAddr(_localAddress.bytes, YES);
}
- (NSString*)remoteAddressString {
return _StringFromAddressData(_remoteAddress);
return GCDWebServerStringFromSockAddr(_remoteAddress.bytes, YES);
}
- (void)dealloc {
+5 -5
View File
@@ -57,13 +57,13 @@ NSString* GCDWebServerUnescapeURLString(NSString* string);
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form);
/**
* On OS X, returns the IPv4 address as a dotted string of the primary connected
* service or nil if not available.
* On OS X, returns the IPv4 or IPv6 address as a string of the primary
* connected service or nil if not available.
*
* On iOS, returns the IPv4 address as a dotted string of the WiFi interface
* if connected or nil otherwise.
* On iOS, returns the IPv4 or IPv6 address as a string of the WiFi
* interface if connected or nil otherwise.
*/
NSString* GCDWebServerGetPrimaryIPv4Address();
NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6);
/**
* Converts a date into a string using RFC822 formatting.
+16 -7
View File
@@ -223,7 +223,19 @@ NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
return parameters;
}
NSString* GCDWebServerGetPrimaryIPv4Address() {
NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService) {
NSString* string = nil;
char hostBuffer[NI_MAXHOST];
char serviceBuffer[NI_MAXSERV];
if (getnameinfo(addr, addr->sa_len, hostBuffer, sizeof(hostBuffer), serviceBuffer, sizeof(serviceBuffer), NI_NUMERICHOST | NI_NUMERICSERV | NI_NOFQDN) >= 0) {
string = includeService ? [NSString stringWithFormat:@"%s:%s", hostBuffer, serviceBuffer] : [NSString stringWithUTF8String:hostBuffer];
} else {
GWS_DNOT_REACHED();
}
return string;
}
NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) {
NSString* address = nil;
#if TARGET_OS_IPHONE
#if !TARGET_IPHONE_SIMULATOR
@@ -233,7 +245,7 @@ NSString* GCDWebServerGetPrimaryIPv4Address() {
const char* primaryInterface = NULL;
SCDynamicStoreRef store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("GCDWebServer"), NULL, NULL);
if (store) {
CFPropertyListRef info = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4"));
CFPropertyListRef info = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4")); // There is no equivalent for IPv6 but the primary interface should be the same
if (info) {
primaryInterface = [[NSString stringWithString:[(ARC_BRIDGE NSDictionary*)info objectForKey:@"PrimaryInterface"]] UTF8String];
CFRelease(info);
@@ -255,11 +267,8 @@ NSString* GCDWebServerGetPrimaryIPv4Address() {
{
continue;
}
if ((ifap->ifa_flags & IFF_UP) && (ifap->ifa_addr->sa_family == AF_INET)) {
char buffer[NI_MAXHOST];
if (getnameinfo(ifap->ifa_addr, ifap->ifa_addr->sa_len, buffer, sizeof(buffer), NULL, 0, NI_NUMERICHOST | NI_NOFQDN) >= 0) {
address = [NSString stringWithUTF8String:buffer];
}
if ((ifap->ifa_flags & IFF_UP) && ((!useIPv6 && (ifap->ifa_addr->sa_family == AF_INET)) || (useIPv6 && (ifap->ifa_addr->sa_family == AF_INET6)))) {
address = GCDWebServerStringFromSockAddr(ifap->ifa_addr, NO);
break;
}
}
+2
View File
@@ -26,6 +26,7 @@
*/
#import <os/object.h>
#import <sys/socket.h>
/**
* ARC <-> MRC compatibility macros.
@@ -212,6 +213,7 @@ extern NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset)
extern BOOL GCDWebServerIsTextContentType(NSString* type);
extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType);
extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_FUNCTION(1,2);
extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService);
@interface GCDWebServerConnection ()
- (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket;
+1
View File
@@ -24,6 +24,7 @@ Extra built-in features:
* [HTTP range](https://en.wikipedia.org/wiki/Byte_serving) support for requests of local files
* [Basic](https://en.wikipedia.org/wiki/Basic_access_authentication) and [Digest Access](https://en.wikipedia.org/wiki/Digest_access_authentication) authentications for password protection
* Automatically handle transitions between foreground, background and suspended modes in iOS apps
* Full support for both IPv4 and IPv6
Included extensions:
* [GCDWebUploader](GCDWebUploader/GCDWebUploader.h): subclass of ```GCDWebServer``` that implements an interface for uploading and downloading files using a web browser