# HG changeset patch # User Jens Alfke # Date 1246482872 25200 # Node ID 63baa74c903f0d36bbcae9123cdf1531000c1906 # Parent 20cccc7c26eed98989eb4281cbf4f033c5a9d494 Fix to BLIPMessage for Chatty (mark new outgoing BLIPMessages as "complete".) Lots of fixes for Bonjour stuff, including making the hostname lookup asynchronous in BonjourService. diff -r 20cccc7c26ee -r 63baa74c903f BLIP/BLIPMessage.m --- a/BLIP/BLIPMessage.m Sun May 24 15:03:39 2009 -0700 +++ b/BLIP/BLIPMessage.m Wed Jul 01 14:14:32 2009 -0700 @@ -39,6 +39,7 @@ _body = body.copy; _properties = [[BLIPMutableProperties alloc] init]; _propertiesAvailable = YES; + _complete = YES; } else { _encodedBody = body.mutableCopy; } diff -r 20cccc7c26ee -r 63baa74c903f BLIP/BLIPRequest.m --- a/BLIP/BLIPRequest.m Sun May 24 15:03:39 2009 -0700 +++ b/BLIP/BLIPRequest.m Wed Jul 01 14:14:32 2009 -0700 @@ -262,7 +262,7 @@ if( complete && _onComplete ) { @try{ [_onComplete invokeWithSender: self]; - }catchAndReport(@"BLIPResponse onComplete target"); + }catchAndReport(@"BLIPRequest onComplete target"); } } diff -r 20cccc7c26ee -r 63baa74c903f Bonjour/MYAddressLookup.h --- a/Bonjour/MYAddressLookup.h Sun May 24 15:03:39 2009 -0700 +++ b/Bonjour/MYAddressLookup.h Wed Jul 01 14:14:32 2009 -0700 @@ -7,11 +7,13 @@ // #import "MYDNSService.h" +@class MYBonjourService; /** An asynchronous DNS address lookup. Supports both Bonjour services and traditional hostnames. */ @interface MYAddressLookup : MYDNSService { + MYBonjourService *_service; NSString *_hostname; UInt16 _interfaceIndex; NSMutableSet *_addresses; @@ -24,6 +26,8 @@ to access its addressLookup property instead of creating your own instance.) */ - (id) initWithHostname: (NSString*)hostname; +@property (readonly, copy) NSString *hostname; + /** The port number; this will be copied into the resulting IPAddress objects. Defaults to zero, but you can set it before calling -start. */ @property UInt16 port; @@ -42,4 +46,9 @@ query will continue to update them. */ @property (readonly) NSTimeInterval timeToLive; + +//internal: +- (id) _initWithBonjourService: (MYBonjourService*)service; +- (void) _serviceGotResponse; + @end diff -r 20cccc7c26ee -r 63baa74c903f Bonjour/MYAddressLookup.m --- a/Bonjour/MYAddressLookup.m Sun May 24 15:03:39 2009 -0700 +++ b/Bonjour/MYAddressLookup.m Wed Jul 01 14:14:32 2009 -0700 @@ -7,6 +7,7 @@ // #import "MYAddressLookup.h" +#import "MYBonjourService.h" #import "IPAddress.h" #import "ExceptionUtils.h" #import "Test.h" @@ -14,6 +15,11 @@ #import +@interface MYAddressLookup () +@property (copy) NSString *hostname; +@end + + @implementation MYAddressLookup - (id) initWithHostname: (NSString*)hostname @@ -30,10 +36,21 @@ return self; } + +- (id) _initWithBonjourService: (MYBonjourService*)service { + self = [super init]; + if (self) { + _service = [service retain]; + _addresses = [[NSMutableSet alloc] init]; + } + return self; +} + - (void) dealloc { [_hostname release]; [_addresses release]; + [_service release]; [super dealloc]; } @@ -44,7 +61,7 @@ } -@synthesize port=_port, interfaceIndex=_interfaceIndex, addresses=_addresses; +@synthesize hostname=_hostname, port=_port, interfaceIndex=_interfaceIndex, addresses=_addresses; - (NSTimeInterval) timeToLive { @@ -52,6 +69,38 @@ } +- (BOOL) start { + if (_hostname) { + return [super start]; + } else { + // Service doesn't know its hostname yet; wait till it does: + LogTo(DNS,@"MYAddressLookup requesting hostname of %@ ...", _service); + Assert(_service); + return [_service start]; + } +} + + +// Called by my _service's gotResponse method: +- (void) _serviceGotResponse { + Assert(_service); + DNSServiceErrorType err = _service.error; + if (err) { + [self cancel]; + self.error = err; + } else { + NSString *hostname = _service.hostname; + UInt16 port = _service.port; + if (port!=_port || !$equal(hostname,_hostname)) { + self.hostname = hostname; + self.port = port; + if (_hostname) + [self start]; + } + } +} + + - (void) priv_resolvedAddress: (const struct sockaddr*)sockaddr ttl: (uint32_t)ttl flags: (DNSServiceFlags)flags @@ -85,7 +134,7 @@ { MYAddressLookup *lookup = context; @try{ - //LogTo(Bonjour, @"lookupCallback for %s (err=%i)", hostname,errorCode); + LogTo(DNS, @"lookupCallback for %s (err=%i)", hostname,errorCode); if (errorCode) [lookup setError: errorCode]; else @@ -96,10 +145,12 @@ - (DNSServiceErrorType) createServiceRef: (DNSServiceRef*)sdRefPtr { + Assert(_hostname); kvSetSet(self, @"addresses", _addresses, nil); return DNSServiceGetAddrInfo(sdRefPtr, kDNSServiceFlagsShareConnection, - _interfaceIndex, 0, + _interfaceIndex, + kDNSServiceProtocol_IPv4, _hostname.UTF8String, &lookupCallback, self); } diff -r 20cccc7c26ee -r 63baa74c903f Bonjour/MYBonjourBrowser.h --- a/Bonjour/MYBonjourBrowser.h Sun May 24 15:03:39 2009 -0700 +++ b/Bonjour/MYBonjourBrowser.h Wed Jul 01 14:14:32 2009 -0700 @@ -22,7 +22,7 @@ MYBonjourRegistration *_myRegistration; } -/** Initializes a new BonjourBrowser. +/** Initializes a new MYBonjourBrowser. Call -start to begin browsing. @param serviceType The name of the service type to look for, e.g. "_http._tcp". */ - (id) initWithServiceType: (NSString*)serviceType; @@ -31,12 +31,12 @@ @property (readonly) BOOL browsing; /** The set of currently found services. These are instances of the serviceClass, - which is BonjourService by default. + which is MYBonjourService by default. This is KV-observable. */ @property (readonly) NSSet *services; /** The class of objects to create to represent services. - The default value is [BonjourService class]; you can change this, but only + The default value is [MYBonjourService class]; you can change this, but only to a subclass of that. */ @property Class serviceClass; diff -r 20cccc7c26ee -r 63baa74c903f Bonjour/MYBonjourBrowser.m --- a/Bonjour/MYBonjourBrowser.m Sun May 24 15:03:39 2009 -0700 +++ b/Bonjour/MYBonjourBrowser.m Wed Jul 01 14:14:32 2009 -0700 @@ -51,7 +51,7 @@ - (void) dealloc { - LogTo(Bonjour,@"DEALLOC BonjourBrowser"); + LogTo(Bonjour,@"DEALLOC MYBonjourBrowser"); [_myRegistration cancel]; [_myRegistration release]; [_serviceType release]; @@ -160,18 +160,18 @@ } -static void browseCallback (DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, - const char *serviceName, - const char *regtype, - const char *replyDomain, - void *context) +static void browseCallback (DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *serviceName, + const char *regtype, + const char *replyDomain, + void *context) { MYBonjourBrowser *browser = context; @try{ - //LogTo(Bonjour,@"browseCallback (error=%i, name='%s')", errorCode,serviceName); + LogTo(Bonjour,@"browseCallback (error=%i, name='%s', intf=%u)", errorCode,serviceName,interfaceIndex); if (!errorCode) [browser priv_gotServiceName: [NSString stringWithUTF8String: serviceName] type: [NSString stringWithUTF8String: regtype] @@ -253,9 +253,8 @@ if( [[change objectForKey: NSKeyValueChangeKindKey] intValue]==NSKeyValueChangeInsertion ) { NSSet *newServices = [change objectForKey: NSKeyValueChangeNewKey]; for( MYBonjourService *service in newServices ) { - NSString *hostname = service.hostname; // block till it's resolved Log(@"##### %@ : at %@:%hu, TXT=%@", - service, hostname, service.port, service.txtRecord); + service, service.hostname, service.port, service.txtRecord); service.addressLookup.continuous = YES; [service.addressLookup addObserver: self forKeyPath: @"addresses" diff -r 20cccc7c26ee -r 63baa74c903f Bonjour/MYBonjourRegistration.m --- a/Bonjour/MYBonjourRegistration.m Sun May 24 15:03:39 2009 -0700 +++ b/Bonjour/MYBonjourRegistration.m Wed Jul 01 14:14:32 2009 -0700 @@ -60,6 +60,7 @@ [_name release]; [_type release]; [_domain release]; + [_txtRecord release]; [super dealloc]; } @@ -149,6 +150,8 @@ + (NSData*) dataFromTXTRecordDictionary: (NSDictionary*)txtDict { + if (!txtDict) + return nil; // First translate any non-NSData values into UTF-8 formatted description data: NSMutableDictionary *encodedDict = $mdict(); for (NSString *key in txtDict) { @@ -166,7 +169,7 @@ [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(updateTxtRecord) object: nil]; if (self.serviceRef) { NSData *data = [[self class] dataFromTXTRecordDictionary: _txtRecord]; - Assert(data!=nil, @"Can't convert dictionary to TXT record"); + Assert(data!=nil || _txtRecord==nil, @"Can't convert dictionary to TXT record: %@", _txtRecord); DNSServiceErrorType err = DNSServiceUpdateRecord(self.serviceRef, NULL, 0, @@ -187,10 +190,7 @@ - (void) setTxtRecord: (NSDictionary*)txtDict { if (!$equal(_txtRecord,txtDict)) { - if (txtDict) - [_txtRecord setDictionary: txtDict]; - else - setObj(&_txtRecord,nil); + setObjCopy(&_txtRecord, txtDict); [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(updateTxtRecord) object: nil]; [self performSelector: @selector(updateTxtRecord) withObject: nil afterDelay: 0.1]; } @@ -235,6 +235,7 @@ - (void) updateTXT { NSDictionary *txt = $dict({@"time", $sprintf(@"%.3lf", CFAbsoluteTimeGetCurrent())}); _reg.txtRecord = txt; + CAssertEqual(_reg.txtRecord, txt); [self performSelector: @selector(updateTXT) withObject: nil afterDelay: 3.0]; } diff -r 20cccc7c26ee -r 63baa74c903f Bonjour/MYBonjourService.h --- a/Bonjour/MYBonjourService.h Sun May 24 15:03:39 2009 -0700 +++ b/Bonjour/MYBonjourService.h Wed Jul 01 14:14:32 2009 -0700 @@ -10,7 +10,7 @@ @class MYBonjourQuery, MYAddressLookup; -/** Represents a Bonjour service discovered by a BonjourBrowser. */ +/** Represents a Bonjour service discovered by a MYBonjourBrowser. */ @interface MYBonjourService : MYDNSService { @private diff -r 20cccc7c26ee -r 63baa74c903f Bonjour/MYBonjourService.m --- a/Bonjour/MYBonjourService.m Sun May 24 15:03:39 2009 -0700 +++ b/Bonjour/MYBonjourService.m Wed Jul 01 14:14:32 2009 -0700 @@ -21,6 +21,8 @@ @interface MYBonjourService () +@property (copy) NSString *hostname; +@property UInt16 port; @end @@ -60,7 +62,8 @@ } -@synthesize name=_name, type=_type, domain=_domain, fullName=_fullName, interfaceIndex=_interfaceIndex; +@synthesize name=_name, type=_type, domain=_domain, fullName=_fullName, + hostname=_hostname, port=_port, interfaceIndex=_interfaceIndex; - (NSString*) description { @@ -105,21 +108,15 @@ } -- (void) priv_finishResolve { - // If I haven't finished my resolve yet, run it *synchronously* now so I can return a valid value: +- (NSString*) hostname { if (!_startedResolve ) [self start]; - if (self.serviceRef) - [self waitForReply]; -} - -- (NSString*) hostname { - if (!_hostname) [self priv_finishResolve]; return _hostname; } - (UInt16) port { - if (!_port) [self priv_finishResolve]; + if (!_startedResolve ) + [self start]; return _port; } @@ -185,16 +182,19 @@ LogTo(Bonjour, @"%@: hostname=%@, port=%u, txt=%u bytes", self, hostname, port, txtData.length); - // Don't call a setter method to set these properties: the getters are synchronous, so - // I might already be blocked in a call to one of them, in which case creating a KV - // notification could cause trouble... - _hostname = hostname.copy; - _port = port; + if (port!=_port || !$equal(hostname,_hostname)) { + self.hostname = hostname; + self.port = port; + } - // TXT getter is async, though, so I can use a setter to announce the data's availability: [self setTxtData: txtData]; } +- (void) gotResponse: (DNSServiceErrorType)errorCode { + [super gotResponse: errorCode]; + [_addressLookup _serviceGotResponse]; +} + static void resolveCallback(DNSServiceRef sdRef, DNSServiceFlags flags, @@ -236,8 +236,7 @@ - (MYAddressLookup*) addressLookup { if (!_addressLookup) { // Create the lookup the first time this is called: - _addressLookup = [[MYAddressLookup alloc] initWithHostname: self.hostname]; - _addressLookup.port = _port; + _addressLookup = [[MYAddressLookup alloc] _initWithBonjourService: self]; _addressLookup.interfaceIndex = _interfaceIndex; } // (Re)start the lookup if it's expired: diff -r 20cccc7c26ee -r 63baa74c903f PortMapper/MYDNSService.h --- a/PortMapper/MYDNSService.h Sun May 24 15:03:39 2009 -0700 +++ b/PortMapper/MYDNSService.h Wed Jul 01 14:14:32 2009 -0700 @@ -38,6 +38,9 @@ /** Stops the service. */ - (void) stop; +/** Has the service started up? */ +@property (readonly) BOOL isRunning; + /** The error status, a DNSServiceErrorType enum; nonzero if something went wrong. This property is KV observable. */ diff -r 20cccc7c26ee -r 63baa74c903f PortMapper/MYDNSService.m --- a/PortMapper/MYDNSService.m Sun May 24 15:03:39 2009 -0700 +++ b/PortMapper/MYDNSService.m Wed Jul 01 14:14:32 2009 -0700 @@ -143,6 +143,11 @@ } +- (BOOL) isRunning { + return _serviceRef != NULL; +} + + + (NSString*) fullNameOfService: (NSString*)serviceName ofType: (NSString*)type inDomain: (NSString*)domain @@ -291,8 +296,12 @@ NSAutoreleasePool *pool = [NSAutoreleasePool new]; LogTo(DNS,@"---serviceCallback----"); DNSServiceErrorType err = DNSServiceProcessResult(_connectionRef); - if (err) + if (err) { Warn(@"%@: DNSServiceProcessResult failed, err=%i !!!", self,err); + //FIX: Are errors here fatal, meaning I should close the connection? + // I've run into infinite loops constantly getting kDNSServiceErr_ServiceNotRunning + // or kDNSServiceErr_BadReference ... + } [pool drain]; return !err; }