diff --git a/Core/DNSSDRegistration.h b/Core/DNSSDRegistration.h new file mode 100644 index 00000000..ae62e60b --- /dev/null +++ b/Core/DNSSDRegistration.h @@ -0,0 +1,124 @@ +/* + File: DNSSDRegistration.h + + Contains: Uses the low-level DNS-SD API to manage a Bonjour service registration. + + Written by: DTS + + Copyright: Copyright (c) 2011 Apple Inc. All Rights Reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. + ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or + redistribution of this Apple software constitutes acceptance of + these terms. If you do not agree with these terms, please do + not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following + terms, and subject to these terms, Apple grants you a personal, + non-exclusive license, under Apple's copyrights in this + original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or + without modifications, in source and/or binary forms; provided + that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the + following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks + or logos of Apple Inc. may be used to endorse or promote + products derived from the Apple Software without specific prior + written permission from Apple. Except as expressly stated in + this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or + by other works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. + APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING + THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, + INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY + OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY + OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR + OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +*/ + +#import + +// forward declarations + +@protocol DNSSDRegistrationDelegate; + +#pragma mark * DNSSDRegistration + +// DNSSDRegistration represents a service that you can register on the network. + +@interface DNSSDRegistration : NSObject + +- (id)initWithDomain:(NSString *)domain type:(NSString *)type name:(NSString *)name port:(NSUInteger)port; + // domain and name can be nil or the empty string to get default behaviour. + // + // type must be of the form "_foo._tcp." or "_foo._udp." (possibly without the + // trailing dot, see below). + // + // port must be in the range 1..65535. + // + // domain and type should include the trailing dot; if they don't, one is added + // and that change is reflected in the domain and type properties. + // + // domain and type must not include a leading dot. + +// properties that are set up by the init method + +@property (copy, readonly ) NSString * domain; +@property (copy, readonly ) NSString * type; +@property (copy, readonly ) NSString * name; +@property (assign, readonly ) NSUInteger port; + +// properties that you can change any time + +@property (assign, readwrite) id delegate; + +- (void)start; + // Starts the registration process. Does nothing if the registration is currently started. + +- (void)stop; + // Stops a registration, deregistering the service from the network. Does nothing if the + // registration is not started. + +// properties that are set up once the registration is in place + +@property (copy, readonly ) NSString * registeredDomain; +@property (copy, readonly ) NSString * registeredName; + +@end + +@protocol DNSSDRegistrationDelegate + +// All delegate methods are called on the main thread. + +@optional + +- (void)dnssdRegistrationWillRegister:(DNSSDRegistration *)sender; + // Called before the registration process starts. + +- (void)dnssdRegistrationDidRegister:(DNSSDRegistration *)sender; + // Called when the service is successfully registered. At this point + // registeredName and registeredDomain are valid. + +- (void)dnssdRegistration:(DNSSDRegistration *)sender didNotRegister:(NSError *)error; + // Called when the service can't be registered. The registration will be stopped + // immediately after this delegate method returns. + +- (void)dnssdRegistrationDidStop:(DNSSDRegistration *)sender; + // Called when the registration stops (except if you call -stop on it). + +@end diff --git a/Core/DNSSDRegistration.m b/Core/DNSSDRegistration.m new file mode 100644 index 00000000..e47aa1f7 --- /dev/null +++ b/Core/DNSSDRegistration.m @@ -0,0 +1,265 @@ +/* + File: DNSSDRegistration.m + + Contains: Uses the low-level DNS-SD API to manage a Bonjour service registration. + + Written by: DTS + + Copyright: Copyright (c) 2011 Apple Inc. All Rights Reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. + ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or + redistribution of this Apple software constitutes acceptance of + these terms. If you do not agree with these terms, please do + not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following + terms, and subject to these terms, Apple grants you a personal, + non-exclusive license, under Apple's copyrights in this + original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or + without modifications, in source and/or binary forms; provided + that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the + following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks + or logos of Apple Inc. may be used to endorse or promote + products derived from the Apple Software without specific prior + written permission from Apple. Except as expressly stated in + this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or + by other works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. + APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING + THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, + INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY + OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY + OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR + OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +*/ + +#import "DNSSDRegistration.h" +#import + +#pragma mark * DNSSDRegistration + +@interface DNSSDRegistration () + +// read-write versions of public properties + +@property (copy, readwrite) NSString * registeredDomain; +@property (copy, readwrite) NSString * registeredName; + +// private properties + +@property (assign, readwrite) DNSServiceRef sdRef; + +// forward declarations + +- (void)stopWithError:(NSError *)error notify:(BOOL)notify; + +@end + +@implementation DNSSDRegistration + +@synthesize domain = domain_; +@synthesize type = type_; +@synthesize name = name_; +@synthesize port = port_; + +@synthesize delegate = delegate_; + +@synthesize registeredDomain = registeredDomain_; +@synthesize registeredName = registeredName_; + +@synthesize sdRef = sdRef_; + +- (id)initWithDomain:(NSString *)domain type:(NSString *)type name:(NSString *)name port:(NSUInteger)port + // See comment in header. +{ + // domain may be nil or empty + assert( ! [domain hasPrefix:@"."] ); + assert(type != nil); + assert([type length] != 0); + assert( ! [type hasPrefix:@"."] ); + assert(port > 0); + assert(port < 65536); + + self = [super init]; + if (self != nil) { + if ( ([domain length] != 0) && ! [domain hasSuffix:@"."] ) { + domain = [domain stringByAppendingString:@"."]; + } + if ( ! [type hasSuffix:@"."] ) { + type = [type stringByAppendingString:@"."]; + } + self->domain_ = [domain copy]; + self->type_ = [type copy]; + self->name_ = [name copy]; + self->port_ = port; + } + return self; +} + +- (void)dealloc +{ + if (self->sdRef_ != NULL) { + DNSServiceRefDeallocate(self->sdRef_); + } + } + +- (void)didRegisterWithDomain:(NSString *)domain name:(NSString *)name + // Called when DNS-SD tells us that a registration has been added. +{ + // On the Mac this routine can get called multiple times, once for the "local." domain and again + // for wide-area domains. As a matter of policy we ignore everything except the "local." + // domain. The "local." domain is really the flagship domain here; that's what our clients + // care about. If it works but a wide-area registration fails, we don't want to error out. + // Conversely, if a wide-area registration succeeds but the "local." domain fails, that + // is a good reason to fail totally. + + assert([domain caseInsensitiveCompare:@"local"] != NSOrderedSame); // DNS-SD always gives us the trailing dot; complain otherwise. + + if ( [domain caseInsensitiveCompare:@"local."] == NSOrderedSame ) { + self.registeredDomain = domain; + self.registeredName = name; + if ([self.delegate respondsToSelector:@selector(dnssdRegistrationDidRegister:)]) { + [self.delegate dnssdRegistrationDidRegister:self]; + } + } +} + +- (void)didUnregisterWithDomain:(NSString *)domain name:(NSString *)name + // Called when DNS-SD tells us that a registration has been removed. +{ + #pragma unused(name) + + // The registration can be removed in the following cases: + // + // A. When you register with the default name (that is, passing the empty string + // to the "name" parameter of DNSServiceRegister) /and/ you specify the 'no rename' + // flags (kDNSServiceFlagsNoAutoRename) /and/ the computer name changes. + // + // B. When you successfully register in a domain and then the domain becomes unavailable + // (for example, if you turn off Back to My Mac after registering). + // + // Case B we ignore based on the same policy outlined in -didRegisterWithDomain:name:. + // + // Case A is interesting and we handle it here. + + assert([domain caseInsensitiveCompare:@"local"] != NSOrderedSame); // DNS-SD always gives us the trailing dot; complain otherwise. + + if ( [domain caseInsensitiveCompare:@"local."] == NSOrderedSame ) { + [self stopWithError:[NSError errorWithDomain:NSNetServicesErrorDomain code:kDNSServiceErr_NameConflict userInfo:nil] notify:YES]; + } +} + +static void RegisterReplyCallback( + DNSServiceRef sdRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + const char * name, + const char * regtype, + const char * domain, + void * context +) + // Called by DNS-SD when something happens with the registered service. +{ + DNSSDRegistration * obj; + #pragma unused(flags) + #pragma unused(regtype) + + assert([NSThread isMainThread]); // because sdRef dispatches to the main queue + + obj = (__bridge DNSSDRegistration *) context; + assert([obj isKindOfClass:[DNSSDRegistration class]]); + assert(sdRef == obj->sdRef_); + #pragma unused(sdRef) + + if (errorCode == kDNSServiceErr_NoError) { + assert([[NSString stringWithUTF8String:regtype] caseInsensitiveCompare:obj.type] == NSOrderedSame); + if (flags & kDNSServiceFlagsAdd) { + [obj didRegisterWithDomain:[NSString stringWithUTF8String:domain] name:[NSString stringWithUTF8String:name]]; + } else { + [obj didUnregisterWithDomain:[NSString stringWithUTF8String:domain] name:[NSString stringWithUTF8String:name]]; + } + } else { + [obj stopWithError:[NSError errorWithDomain:NSNetServicesErrorDomain code:errorCode userInfo:nil] notify:YES]; + } +} + +- (void)start + // See comment in header. +{ + DNSServiceErrorType errorCode; + + if (self.sdRef == NULL) { + NSString * name; + NSString * domain; + + domain = self.domain; + if (domain == nil) { + domain = @""; + } + name = self.name; + if (name == nil) { + name = @""; + } + + errorCode = DNSServiceRegister(&self->sdRef_, kDNSServiceFlagsIncludeP2P, kDNSServiceInterfaceIndexAny, [name UTF8String], [self.type UTF8String], [domain UTF8String], NULL, htons(self.port), 0, NULL, RegisterReplyCallback, (__bridge void *)(self)); + if (errorCode == kDNSServiceErr_NoError) { + errorCode = DNSServiceSetDispatchQueue(self.sdRef, dispatch_get_main_queue()); + } + if (errorCode == kDNSServiceErr_NoError) { + if ([self.delegate respondsToSelector:@selector(dnssdRegistrationWillRegister:)]) { + [self.delegate dnssdRegistrationWillRegister:self]; + } + } else { + [self stopWithError:[NSError errorWithDomain:NSNetServicesErrorDomain code:errorCode userInfo:nil] notify:YES]; + } + } +} + +- (void)stopWithError:(NSError *)error notify:(BOOL)notify + // An internal bottleneck for shutting down the object. +{ + if (notify) { + if (error != nil) { + if ([self.delegate respondsToSelector:@selector(dnssdRegistration:didNotRegister:)]) { + [self.delegate dnssdRegistration:self didNotRegister:error]; + } + } + } + self.registeredDomain = nil; + self.registeredName = nil; + if (self.sdRef != NULL) { + DNSServiceRefDeallocate(self.sdRef); + self.sdRef = NULL; + } + if (notify) { + if ([self.delegate respondsToSelector:@selector(dnssdRegistrationDidStop:)]) { + [self.delegate dnssdRegistrationDidStop:self]; + } + } +} + +- (void)stop + // See comment in header. +{ + [self stopWithError:nil notify:NO]; +} + +@end diff --git a/Core/HTTPServer.h b/Core/HTTPServer.h index 1d37cb69..8c46bb11 100644 --- a/Core/HTTPServer.h +++ b/Core/HTTPServer.h @@ -1,24 +1,10 @@ #import @class GCDAsyncSocket; +@class DNSSDRegistration; @class WebSocket; -#if TARGET_OS_IPHONE - #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 40000 // iPhone 4.0 - #define IMPLEMENTED_PROTOCOLS - #else - #define IMPLEMENTED_PROTOCOLS - #endif -#else - #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 // Mac OS X 10.6 - #define IMPLEMENTED_PROTOCOLS - #else - #define IMPLEMENTED_PROTOCOLS - #endif -#endif - - -@interface HTTPServer : NSObject IMPLEMENTED_PROTOCOLS +@interface HTTPServer : NSObject { // Underlying asynchronous TCP/IP socket GCDAsyncSocket *asyncSocket; @@ -35,13 +21,11 @@ NSString *interface; UInt16 port; - // NSNetService and related variables - NSNetService *netService; + // Bonjour registration and related variables + DNSSDRegistration *netService; NSString *domain; NSString *type; NSString *name; - NSString *publishedName; - NSDictionary *txtRecordDictionary; // Connection management NSMutableArray *connections; @@ -108,6 +92,11 @@ - (UInt16)listeningPort; - (void)setPort:(UInt16)value; +/** + * Publish with bonjour name, type, and domain + **/ +- (void) startPublishingWithName:(NSString*) serviceName type:(NSString*) serviceType domain: (NSString*) serviceDomain; + /** * Bonjour domain for publishing the service. * The default value is "local.". @@ -160,12 +149,6 @@ **/ - (void)republishBonjour; -/** - * -**/ -- (NSDictionary *)TXTRecordDictionary; -- (void)setTXTRecordDictionary:(NSDictionary *)dict; - /** * Attempts to starts the server on the configured port, interface, etc. * diff --git a/Core/HTTPServer.m b/Core/HTTPServer.m index 57384f79..53f4bdf6 100644 --- a/Core/HTTPServer.m +++ b/Core/HTTPServer.m @@ -3,6 +3,7 @@ #import "HTTPConnection.h" #import "WebSocket.h" #import "HTTPLogging.h" +#import "DNSSDRegistration.h" #if ! __has_feature(objc_arc) #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). @@ -12,7 +13,7 @@ // Other flags: trace static const int httpLogLevel = HTTP_LOG_LEVEL_INFO; // | HTTP_LOG_FLAG_TRACE; -@interface HTTPServer (PrivateAPI) +@interface HTTPServer (PrivateAPI) - (void)unpublishBonjour; - (void)publishBonjour; @@ -301,20 +302,7 @@ - (NSString *)publishedName __block NSString *result; dispatch_sync(serverQueue, ^{ - - if (netService == nil) - { - result = nil; - } - else - { - - dispatch_block_t bonjourBlock = ^{ - result = [[netService name] copy]; - }; - - [[self class] performBonjourBlock:bonjourBlock]; - } +// result = [netService registeredName]; }); return result; @@ -355,47 +343,6 @@ - (void)setType:(NSString *)value } -/** - * The extra data to use for this service via Bonjour. -**/ -- (NSDictionary *)TXTRecordDictionary -{ - __block NSDictionary *result; - - dispatch_sync(serverQueue, ^{ - result = txtRecordDictionary; - }); - - return result; -} - -- (void)setTXTRecordDictionary:(NSDictionary *)value -{ - HTTPLogTrace(); - - NSDictionary *valueCopy = [value copy]; - - dispatch_async(serverQueue, ^{ - - txtRecordDictionary = valueCopy; - - // Update the txtRecord of the netService if it has already been published - if (netService) - { - NSNetService *theNetService = netService; - NSData *txtRecordData = nil; - if (txtRecordDictionary) - txtRecordData = [NSNetService dataFromTXTRecordDictionary:txtRecordDictionary]; - - dispatch_block_t bonjourBlock = ^{ - [theNetService setTXTRecordData:txtRecordData]; - }; - - [[self class] performBonjourBlock:bonjourBlock]; - } - }); - -} //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark Server Control @@ -559,6 +506,14 @@ - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSo #pragma mark Bonjour //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) startPublishingWithName:(NSString*) serviceName type:(NSString*) serviceType domain: (NSString*) serviceDomain { + name = [serviceName copy]; + type = [serviceType copy]; + domain = [serviceDomain copy]; + + [self publishBonjour]; +} + - (void)publishBonjour { HTTPLogTrace(); @@ -567,33 +522,18 @@ - (void)publishBonjour if (type) { - netService = [[NSNetService alloc] initWithDomain:domain type:type name:name port:[asyncSocket localPort]]; + if ([asyncSocket localPort] == 0) { + [asyncSocket acceptOnPort:0 error:NULL]; + } + int localPort = [asyncSocket localPort]; + + netService = [[DNSSDRegistration alloc] initWithDomain:domain type:type name:name port:localPort]; [netService setDelegate:self]; - - NSNetService *theNetService = netService; - NSData *txtRecordData = nil; - if (txtRecordDictionary) - txtRecordData = [NSNetService dataFromTXTRecordDictionary:txtRecordDictionary]; - - dispatch_block_t bonjourBlock = ^{ - - [theNetService removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; - [theNetService scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; - [theNetService publish]; - - // Do not set the txtRecordDictionary prior to publishing!!! - // This will cause the OS to crash!!! - if (txtRecordData) - { - [theNetService setTXTRecordData:txtRecordData]; - } - }; - - [[self class] startBonjourThreadIfNeeded]; - [[self class] performBonjourBlock:bonjourBlock]; - } + [netService start]; + }; } + - (void)unpublishBonjour { HTTPLogTrace(); @@ -602,15 +542,7 @@ - (void)unpublishBonjour if (netService) { - NSNetService *theNetService = netService; - - dispatch_block_t bonjourBlock = ^{ - - [theNetService stop]; - }; - - [[self class] performBonjourBlock:bonjourBlock]; - + [netService stop]; netService = nil; } } @@ -634,27 +566,18 @@ - (void)republishBonjour * Called when our bonjour service has been successfully published. * This method does nothing but output a log message telling us about the published service. **/ -- (void)netServiceDidPublish:(NSNetService *)ns -{ - // Override me to do something here... - // - // Note: This method is invoked on our bonjour thread. - - HTTPLogInfo(@"Bonjour Service Published: domain(%@) type(%@) name(%@)", [ns domain], [ns type], [ns name]); +- (void)dnssdRegistrationDidRegister:(DNSSDRegistration *)sender { + HTTPLogInfo(@"Bonjour Service Published: domain(%@) type(%@) name(%@)", domain, type, name); } + /** * Called if our bonjour service failed to publish itself. - * This method does nothing but output a log message telling us about the published service. + * This method does nothing but output a log message telling us about the unpublished service. **/ -- (void)netService:(NSNetService *)ns didNotPublish:(NSDictionary *)errorDict -{ - // Override me to do something here... - // - // Note: This method in invoked on our bonjour thread. - +- (void)dnssdRegistration:(DNSSDRegistration *)sender didNotRegister:(NSError *)error { HTTPLogWarn(@"Failed to Publish Service: domain(%@) type(%@) name(%@) - %@", - [ns domain], [ns type], [ns name], errorDict); + domain, type, name, [error description]); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -693,80 +616,4 @@ - (void)webSocketDidDie:(NSNotification *)notification [webSocketsLock unlock]; } -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark Bonjour Thread -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * NSNetService is runloop based, so it requires a thread with a runloop. - * This gives us two options: - * - * - Use the main thread - * - Setup our own dedicated thread - * - * Since we have various blocks of code that need to synchronously access the netservice objects, - * using the main thread becomes troublesome and a potential for deadlock. -**/ - -static NSThread *bonjourThread; - -+ (void)startBonjourThreadIfNeeded -{ - HTTPLogTrace(); - - static dispatch_once_t predicate; - dispatch_once(&predicate, ^{ - - HTTPLogVerbose(@"%@: Starting bonjour thread...", THIS_FILE); - - bonjourThread = [[NSThread alloc] initWithTarget:self - selector:@selector(bonjourThread) - object:nil]; - [bonjourThread start]; - }); -} - -+ (void)bonjourThread -{ - @autoreleasepool { - - HTTPLogVerbose(@"%@: BonjourThread: Started", THIS_FILE); - - // We can't run the run loop unless it has an associated input source or a timer. - // So we'll just create a timer that will never fire - unless the server runs for 10,000 years. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wundeclared-selector" - [NSTimer scheduledTimerWithTimeInterval:[[NSDate distantFuture] timeIntervalSinceNow] - target:self - selector:@selector(donothingatall:) - userInfo:nil - repeats:YES]; -#pragma clang diagnostic pop - - [[NSRunLoop currentRunLoop] run]; - - HTTPLogVerbose(@"%@: BonjourThread: Aborted", THIS_FILE); - - } -} - -+ (void)executeBonjourBlock:(dispatch_block_t)block -{ - HTTPLogTrace(); - - NSAssert([NSThread currentThread] == bonjourThread, @"Executed on incorrect thread"); - - block(); -} - -+ (void)performBonjourBlock:(dispatch_block_t)block -{ - HTTPLogTrace(); - - [self performSelector:@selector(executeBonjourBlock:) - onThread:bonjourThread - withObject:block - waitUntilDone:YES]; -} - @end diff --git a/README.markdown b/README.markdown index 088f4ab1..48731e3c 100644 --- a/README.markdown +++ b/README.markdown @@ -1,5 +1,9 @@ CocoaHTTPServer is a small, lightweight, embeddable HTTP server for Mac OS X or iOS applications. +**Changes from the trunk:** +- Bonjour services can be seen through bluetooth +- Check for iPhone 4 or greater removed + Sometimes developers need an embedded HTTP server in their app. Perhaps it's a server application with remote monitoring. Or perhaps it's a desktop application using HTTP for the communication backend. Or perhaps it's an iOS app providing over-the-air access to documents. Whatever your reason, CocoaHTTPServer can get the job done. It provides: - Built in support for bonjour broadcasting