From 8bfefdec4c90de429f0b8aba08068fea15ab9638 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Latour Date: Sat, 29 Mar 2014 21:29:07 -0700 Subject: [PATCH] Added GCDWebServerGetPrimaryIPv4Address() and server URL properties --- CGDWebServer/GCDWebServer.h | 11 +++-- CGDWebServer/GCDWebServer.m | 92 ++++++++++++++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 11 deletions(-) diff --git a/CGDWebServer/GCDWebServer.h b/CGDWebServer/GCDWebServer.h index 80dc14c..90e4bc7 100644 --- a/CGDWebServer/GCDWebServer.h +++ b/CGDWebServer/GCDWebServer.h @@ -41,6 +41,7 @@ NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension); NSString* GCDWebServerEscapeURLString(NSString* string); NSString* GCDWebServerUnescapeURLString(NSString* string); NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form); +NSString* GCDWebServerGetPrimaryIPv4Address(); // Returns IPv4 address of primary connected service on OS X or of WiFi interface on iOS if connected #ifdef __cplusplus } @@ -53,7 +54,7 @@ NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form); - (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock; - (void)removeAllHandlers; -- (BOOL)start; // Default is port 8080 (Mac & iOS Simulator) or 80 (iOS) and computer name +- (BOOL)start; // Default is port 8080 (OS X & iOS Simulator) or 80 (iOS) and computer name - (BOOL)startWithPort:(NSUInteger)port bonjourName:(NSString*)name; // Pass nil name to disable Bonjour or empty string to use computer name - (void)stop; @end @@ -63,13 +64,13 @@ NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form); + (NSString*)serverName; // Default is class name @end -#if !TARGET_OS_IPHONE - @interface GCDWebServer (Extensions) +@property(nonatomic, readonly) NSURL* serverURL; // Only non-nil if server is running +@property(nonatomic, readonly) NSURL* bonjourServerURL; // Only non-nil if server is running and Bonjour registration is active +#if !TARGET_OS_IPHONE - (BOOL)runWithPort:(NSUInteger)port; // Starts then automatically stops on SIGINT i.e. Ctrl-C (use on main thread only) -@end - #endif +@end @interface GCDWebServer (Handlers) - (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block; diff --git a/CGDWebServer/GCDWebServer.m b/CGDWebServer/GCDWebServer.m index aed884e..5d6590e 100644 --- a/CGDWebServer/GCDWebServer.m +++ b/CGDWebServer/GCDWebServer.m @@ -28,9 +28,14 @@ #import #if TARGET_OS_IPHONE #import +#else +#import #endif #import +#import +#import +#import #import "GCDWebServerPrivate.h" @@ -146,6 +151,52 @@ NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) { return parameters; } +NSString* GCDWebServerGetPrimaryIPv4Address() { + NSString* address = nil; +#if TARGET_OS_IPHONE +#if !TARGET_IPHONE_SIMULATOR + const char* primaryInterface = "en0"; // WiFi interface on iOS +#endif +#else + const char* primaryInterface = NULL; + SCDynamicStoreRef store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("GCDWebServer"), NULL, NULL); + if (store) { + CFPropertyListRef info = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4")); + if (info) { + primaryInterface = [[(ARC_BRIDGE NSDictionary*)info objectForKey:@"PrimaryInterface"] UTF8String]; + CFRelease(info); + } + CFRelease(store); + } + if (primaryInterface == NULL) { + DNOT_REACHED(); + return nil; + } +#endif + struct ifaddrs* list; + if (getifaddrs(&list) >= 0) { + for (struct ifaddrs* ifap = list; ifap; ifap = ifap->ifa_next) { +#if TARGET_IPHONE_SIMULATOR + if (strcmp(ifap->ifa_name, "en0") && strcmp(ifap->ifa_name, "en1")) // Assume en0 is Ethernet and en1 is WiFi since there is no way to use SystemConfiguration framework in iOS Simulator +#else + if (strcmp(ifap->ifa_name, primaryInterface)) +#endif + { + 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]; + } + break; + } + } + freeifaddrs(list); + } + return address; +} + #if !TARGET_OS_IPHONE static void _SignalHandler(int signal) { @@ -227,7 +278,8 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er if (error->error) { LOG_ERROR(@"Bonjour error %i (domain %i)", (int)error->error, (int)error->domain); } else { - LOG_VERBOSE(@"Registered Bonjour service \"%@\" in domain \"%@\" with type '%@' on port %i", CFNetServiceGetName(service), CFNetServiceGetDomain(service), CFNetServiceGetType(service), (int)CFNetServiceGetPortNumber(service)); + GCDWebServer* server = (ARC_BRIDGE GCDWebServer*)info; + LOG_VERBOSE(@"%@ now reachable at %@", [server class], server.bonjourServerURL); } } } @@ -323,7 +375,7 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er } dispatch_resume(_source); - LOG_VERBOSE(@"%@ started on port %i", [self class], (int)_port); + LOG_VERBOSE(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL); } else { LOG_ERROR(@"Failed listening on socket (%i): %s", errno, strerror(errno)); close(listeningSocket); @@ -375,10 +427,38 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er @end -#if !TARGET_OS_IPHONE - @implementation GCDWebServer (Extensions) +- (NSURL*)serverURL { + if (_source) { + NSString* ipAddress = GCDWebServerGetPrimaryIPv4Address(); + if (ipAddress) { + if (_port != 80) { + return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%i/", ipAddress, (int)_port]]; + } else { + return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/", ipAddress]]; + } + } + } + return nil; +} + +- (NSURL*)bonjourServerURL { + if (_source && _service) { + CFStringRef name = CFNetServiceGetName(_service); + if (name && CFStringGetLength(name)) { + if (_port != 80) { + return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@.local:%i/", name, (int)_port]]; + } else { + return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@.local/", name]]; + } + } + } + return nil; +} + +#if !TARGET_OS_IPHONE + - (BOOL)runWithPort:(NSUInteger)port { BOOL success = NO; _run = YES; @@ -396,10 +476,10 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er return success; } -@end - #endif +@end + @implementation GCDWebServer (Handlers) - (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {