Fix to BLIPMessage for Chatty (mark new outgoing BLIPMessages as "complete".)
authorJens Alfke <jens@mooseyard.com>
Wed Jul 01 14:14:32 2009 -0700 (2009-07-01)
changeset 5063baa74c903f
parent 49 20cccc7c26ee
child 57 998dcacd4983
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.
BLIP/BLIPMessage.m
BLIP/BLIPRequest.m
Bonjour/MYAddressLookup.h
Bonjour/MYAddressLookup.m
Bonjour/MYBonjourBrowser.h
Bonjour/MYBonjourBrowser.m
Bonjour/MYBonjourRegistration.m
Bonjour/MYBonjourService.h
Bonjour/MYBonjourService.m
PortMapper/MYDNSService.h
PortMapper/MYDNSService.m
     1.1 --- a/BLIP/BLIPMessage.m	Sun May 24 15:03:39 2009 -0700
     1.2 +++ b/BLIP/BLIPMessage.m	Wed Jul 01 14:14:32 2009 -0700
     1.3 @@ -39,6 +39,7 @@
     1.4              _body = body.copy;
     1.5              _properties = [[BLIPMutableProperties alloc] init];
     1.6              _propertiesAvailable = YES;
     1.7 +            _complete = YES;
     1.8          } else {
     1.9              _encodedBody = body.mutableCopy;
    1.10          }
     2.1 --- a/BLIP/BLIPRequest.m	Sun May 24 15:03:39 2009 -0700
     2.2 +++ b/BLIP/BLIPRequest.m	Wed Jul 01 14:14:32 2009 -0700
     2.3 @@ -262,7 +262,7 @@
     2.4      if( complete && _onComplete ) {
     2.5          @try{
     2.6              [_onComplete invokeWithSender: self];
     2.7 -        }catchAndReport(@"BLIPResponse onComplete target");
     2.8 +        }catchAndReport(@"BLIPRequest onComplete target");
     2.9      }
    2.10  }
    2.11  
     3.1 --- a/Bonjour/MYAddressLookup.h	Sun May 24 15:03:39 2009 -0700
     3.2 +++ b/Bonjour/MYAddressLookup.h	Wed Jul 01 14:14:32 2009 -0700
     3.3 @@ -7,11 +7,13 @@
     3.4  //
     3.5  
     3.6  #import "MYDNSService.h"
     3.7 +@class MYBonjourService;
     3.8  
     3.9  
    3.10  /** An asynchronous DNS address lookup. Supports both Bonjour services and traditional hostnames. */
    3.11  @interface MYAddressLookup : MYDNSService
    3.12  {
    3.13 +    MYBonjourService *_service;
    3.14      NSString *_hostname;
    3.15      UInt16 _interfaceIndex;
    3.16      NSMutableSet *_addresses;
    3.17 @@ -24,6 +26,8 @@
    3.18      to access its addressLookup property instead of creating your own instance.) */
    3.19  - (id) initWithHostname: (NSString*)hostname;
    3.20  
    3.21 +@property (readonly, copy) NSString *hostname;
    3.22 +
    3.23  /** The port number; this will be copied into the resulting IPAddress objects.
    3.24      Defaults to zero, but you can set it before calling -start. */
    3.25  @property UInt16 port;
    3.26 @@ -42,4 +46,9 @@
    3.27      query will continue to update them. */
    3.28  @property (readonly) NSTimeInterval timeToLive;
    3.29  
    3.30 +
    3.31 +//internal:
    3.32 +- (id) _initWithBonjourService: (MYBonjourService*)service;
    3.33 +- (void) _serviceGotResponse;
    3.34 +
    3.35  @end
     4.1 --- a/Bonjour/MYAddressLookup.m	Sun May 24 15:03:39 2009 -0700
     4.2 +++ b/Bonjour/MYAddressLookup.m	Wed Jul 01 14:14:32 2009 -0700
     4.3 @@ -7,6 +7,7 @@
     4.4  //
     4.5  
     4.6  #import "MYAddressLookup.h"
     4.7 +#import "MYBonjourService.h"
     4.8  #import "IPAddress.h"
     4.9  #import "ExceptionUtils.h"
    4.10  #import "Test.h"
    4.11 @@ -14,6 +15,11 @@
    4.12  #import <dns_sd.h>
    4.13  
    4.14  
    4.15 +@interface MYAddressLookup ()
    4.16 +@property (copy) NSString *hostname;
    4.17 +@end
    4.18 +
    4.19 +
    4.20  @implementation MYAddressLookup
    4.21  
    4.22  - (id) initWithHostname: (NSString*)hostname
    4.23 @@ -30,10 +36,21 @@
    4.24      return self;
    4.25  }
    4.26  
    4.27 +
    4.28 +- (id) _initWithBonjourService: (MYBonjourService*)service {
    4.29 +    self = [super init];
    4.30 +    if (self) {
    4.31 +        _service = [service retain];
    4.32 +        _addresses = [[NSMutableSet alloc] init];
    4.33 +    }
    4.34 +    return self;
    4.35 +}
    4.36 +
    4.37  - (void) dealloc
    4.38  {
    4.39      [_hostname release];
    4.40      [_addresses release];
    4.41 +    [_service release];
    4.42      [super dealloc];
    4.43  }
    4.44  
    4.45 @@ -44,7 +61,7 @@
    4.46  }
    4.47  
    4.48  
    4.49 -@synthesize port=_port, interfaceIndex=_interfaceIndex, addresses=_addresses;
    4.50 +@synthesize hostname=_hostname, port=_port, interfaceIndex=_interfaceIndex, addresses=_addresses;
    4.51  
    4.52  
    4.53  - (NSTimeInterval) timeToLive {
    4.54 @@ -52,6 +69,38 @@
    4.55  }
    4.56  
    4.57  
    4.58 +- (BOOL) start {
    4.59 +    if (_hostname) {
    4.60 +        return [super start];
    4.61 +    } else {
    4.62 +        // Service doesn't know its hostname yet; wait till it does:
    4.63 +        LogTo(DNS,@"MYAddressLookup requesting hostname of %@ ...", _service);
    4.64 +        Assert(_service);
    4.65 +        return [_service start];
    4.66 +    }
    4.67 +}
    4.68 +
    4.69 +
    4.70 +// Called by my _service's gotResponse method:
    4.71 +- (void) _serviceGotResponse {
    4.72 +    Assert(_service);
    4.73 +    DNSServiceErrorType err = _service.error;
    4.74 +    if (err) {
    4.75 +        [self cancel];
    4.76 +        self.error = err;
    4.77 +    } else {
    4.78 +        NSString *hostname = _service.hostname;
    4.79 +        UInt16 port = _service.port;
    4.80 +        if (port!=_port || !$equal(hostname,_hostname)) {
    4.81 +            self.hostname = hostname;
    4.82 +            self.port = port;
    4.83 +            if (_hostname)
    4.84 +                [self start];
    4.85 +        }
    4.86 +    }
    4.87 +}
    4.88 +
    4.89 +
    4.90  - (void) priv_resolvedAddress: (const struct sockaddr*)sockaddr
    4.91                            ttl: (uint32_t)ttl
    4.92                          flags: (DNSServiceFlags)flags
    4.93 @@ -85,7 +134,7 @@
    4.94  {
    4.95      MYAddressLookup *lookup = context;
    4.96      @try{
    4.97 -        //LogTo(Bonjour, @"lookupCallback for %s (err=%i)", hostname,errorCode);
    4.98 +        LogTo(DNS, @"lookupCallback for %s (err=%i)", hostname,errorCode);
    4.99          if (errorCode)
   4.100              [lookup setError: errorCode];
   4.101          else
   4.102 @@ -96,10 +145,12 @@
   4.103  
   4.104  
   4.105  - (DNSServiceErrorType) createServiceRef: (DNSServiceRef*)sdRefPtr {
   4.106 +    Assert(_hostname);
   4.107      kvSetSet(self, @"addresses", _addresses, nil);
   4.108      return DNSServiceGetAddrInfo(sdRefPtr,
   4.109                                   kDNSServiceFlagsShareConnection,
   4.110 -                                 _interfaceIndex, 0,
   4.111 +                                 _interfaceIndex, 
   4.112 +                                 kDNSServiceProtocol_IPv4,
   4.113                                   _hostname.UTF8String,
   4.114                                   &lookupCallback, self);
   4.115  }
     5.1 --- a/Bonjour/MYBonjourBrowser.h	Sun May 24 15:03:39 2009 -0700
     5.2 +++ b/Bonjour/MYBonjourBrowser.h	Wed Jul 01 14:14:32 2009 -0700
     5.3 @@ -22,7 +22,7 @@
     5.4      MYBonjourRegistration *_myRegistration;
     5.5  }
     5.6  
     5.7 -/** Initializes a new BonjourBrowser.
     5.8 +/** Initializes a new MYBonjourBrowser.
     5.9      Call -start to begin browsing.
    5.10      @param serviceType  The name of the service type to look for, e.g. "_http._tcp". */
    5.11  - (id) initWithServiceType: (NSString*)serviceType;
    5.12 @@ -31,12 +31,12 @@
    5.13  @property (readonly) BOOL browsing;
    5.14  
    5.15  /** The set of currently found services. These are instances of the serviceClass,
    5.16 -    which is BonjourService by default.
    5.17 +    which is MYBonjourService by default.
    5.18      This is KV-observable. */
    5.19  @property (readonly) NSSet *services;
    5.20  
    5.21  /** The class of objects to create to represent services.
    5.22 -    The default value is [BonjourService class]; you can change this, but only
    5.23 +    The default value is [MYBonjourService class]; you can change this, but only
    5.24      to a subclass of that. */
    5.25  @property Class serviceClass;
    5.26  
     6.1 --- a/Bonjour/MYBonjourBrowser.m	Sun May 24 15:03:39 2009 -0700
     6.2 +++ b/Bonjour/MYBonjourBrowser.m	Wed Jul 01 14:14:32 2009 -0700
     6.3 @@ -51,7 +51,7 @@
     6.4  
     6.5  - (void) dealloc
     6.6  {
     6.7 -    LogTo(Bonjour,@"DEALLOC BonjourBrowser");
     6.8 +    LogTo(Bonjour,@"DEALLOC MYBonjourBrowser");
     6.9      [_myRegistration cancel];
    6.10      [_myRegistration release];
    6.11      [_serviceType release];
    6.12 @@ -160,18 +160,18 @@
    6.13  }
    6.14  
    6.15  
    6.16 -static void browseCallback (DNSServiceRef                       sdRef,
    6.17 -                            DNSServiceFlags                     flags,
    6.18 -                            uint32_t                            interfaceIndex,
    6.19 -                            DNSServiceErrorType                 errorCode,
    6.20 -                            const char                          *serviceName,
    6.21 -                            const char                          *regtype,
    6.22 -                            const char                          *replyDomain,
    6.23 -                            void                                *context)
    6.24 +static void browseCallback (DNSServiceRef        sdRef,
    6.25 +                            DNSServiceFlags      flags,
    6.26 +                            uint32_t             interfaceIndex,
    6.27 +                            DNSServiceErrorType  errorCode,
    6.28 +                            const char           *serviceName,
    6.29 +                            const char           *regtype,
    6.30 +                            const char           *replyDomain,
    6.31 +                            void                 *context)
    6.32  {
    6.33      MYBonjourBrowser *browser = context;
    6.34      @try{
    6.35 -        //LogTo(Bonjour,@"browseCallback (error=%i, name='%s')", errorCode,serviceName);
    6.36 +        LogTo(Bonjour,@"browseCallback (error=%i, name='%s', intf=%u)", errorCode,serviceName,interfaceIndex);
    6.37          if (!errorCode)
    6.38              [browser priv_gotServiceName: [NSString stringWithUTF8String: serviceName]
    6.39                                      type: [NSString stringWithUTF8String: regtype]
    6.40 @@ -253,9 +253,8 @@
    6.41          if( [[change objectForKey: NSKeyValueChangeKindKey] intValue]==NSKeyValueChangeInsertion ) {
    6.42              NSSet *newServices = [change objectForKey: NSKeyValueChangeNewKey];
    6.43              for( MYBonjourService *service in newServices ) {
    6.44 -                NSString *hostname = service.hostname;  // block till it's resolved
    6.45                  Log(@"##### %@ : at %@:%hu, TXT=%@", 
    6.46 -                      service, hostname, service.port, service.txtRecord);
    6.47 +                      service, service.hostname, service.port, service.txtRecord);
    6.48                  service.addressLookup.continuous = YES;
    6.49                  [service.addressLookup addObserver: self
    6.50                                          forKeyPath: @"addresses"
     7.1 --- a/Bonjour/MYBonjourRegistration.m	Sun May 24 15:03:39 2009 -0700
     7.2 +++ b/Bonjour/MYBonjourRegistration.m	Wed Jul 01 14:14:32 2009 -0700
     7.3 @@ -60,6 +60,7 @@
     7.4      [_name release];
     7.5      [_type release];
     7.6      [_domain release];
     7.7 +    [_txtRecord release];
     7.8      [super dealloc];
     7.9  }
    7.10  
    7.11 @@ -149,6 +150,8 @@
    7.12  
    7.13  
    7.14  + (NSData*) dataFromTXTRecordDictionary: (NSDictionary*)txtDict {
    7.15 +    if (!txtDict)
    7.16 +        return nil;
    7.17      // First translate any non-NSData values into UTF-8 formatted description data:
    7.18      NSMutableDictionary *encodedDict = $mdict();
    7.19      for (NSString *key in txtDict) {
    7.20 @@ -166,7 +169,7 @@
    7.21      [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(updateTxtRecord) object: nil];
    7.22      if (self.serviceRef) {
    7.23          NSData *data = [[self class] dataFromTXTRecordDictionary: _txtRecord];
    7.24 -        Assert(data!=nil, @"Can't convert dictionary to TXT record");
    7.25 +        Assert(data!=nil || _txtRecord==nil, @"Can't convert dictionary to TXT record: %@", _txtRecord);
    7.26          DNSServiceErrorType err = DNSServiceUpdateRecord(self.serviceRef,
    7.27                                                           NULL,
    7.28                                                           0,
    7.29 @@ -187,10 +190,7 @@
    7.30  
    7.31  - (void) setTxtRecord: (NSDictionary*)txtDict {
    7.32      if (!$equal(_txtRecord,txtDict)) {
    7.33 -        if (txtDict)
    7.34 -            [_txtRecord setDictionary: txtDict];
    7.35 -        else
    7.36 -            setObj(&_txtRecord,nil);
    7.37 +        setObjCopy(&_txtRecord, txtDict);
    7.38          [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(updateTxtRecord) object: nil];
    7.39          [self performSelector: @selector(updateTxtRecord) withObject: nil afterDelay: 0.1];
    7.40      }
    7.41 @@ -235,6 +235,7 @@
    7.42  - (void) updateTXT {
    7.43      NSDictionary *txt = $dict({@"time", $sprintf(@"%.3lf", CFAbsoluteTimeGetCurrent())});
    7.44      _reg.txtRecord = txt;
    7.45 +    CAssertEqual(_reg.txtRecord, txt);
    7.46      [self performSelector: @selector(updateTXT) withObject: nil afterDelay: 3.0];
    7.47  }
    7.48  
     8.1 --- a/Bonjour/MYBonjourService.h	Sun May 24 15:03:39 2009 -0700
     8.2 +++ b/Bonjour/MYBonjourService.h	Wed Jul 01 14:14:32 2009 -0700
     8.3 @@ -10,7 +10,7 @@
     8.4  @class MYBonjourQuery, MYAddressLookup;
     8.5  
     8.6  
     8.7 -/** Represents a Bonjour service discovered by a BonjourBrowser. */
     8.8 +/** Represents a Bonjour service discovered by a MYBonjourBrowser. */
     8.9  @interface MYBonjourService : MYDNSService 
    8.10  {
    8.11      @private
     9.1 --- a/Bonjour/MYBonjourService.m	Sun May 24 15:03:39 2009 -0700
     9.2 +++ b/Bonjour/MYBonjourService.m	Wed Jul 01 14:14:32 2009 -0700
     9.3 @@ -21,6 +21,8 @@
     9.4  
     9.5  
     9.6  @interface MYBonjourService ()
     9.7 +@property (copy) NSString *hostname;
     9.8 +@property UInt16 port;
     9.9  @end
    9.10  
    9.11  
    9.12 @@ -60,7 +62,8 @@
    9.13  }
    9.14  
    9.15  
    9.16 -@synthesize name=_name, type=_type, domain=_domain, fullName=_fullName, interfaceIndex=_interfaceIndex;
    9.17 +@synthesize name=_name, type=_type, domain=_domain, fullName=_fullName,
    9.18 +            hostname=_hostname, port=_port, interfaceIndex=_interfaceIndex;
    9.19  
    9.20  
    9.21  - (NSString*) description {
    9.22 @@ -105,21 +108,15 @@
    9.23  }
    9.24  
    9.25  
    9.26 -- (void) priv_finishResolve {
    9.27 -    // If I haven't finished my resolve yet, run it *synchronously* now so I can return a valid value:
    9.28 +- (NSString*) hostname {
    9.29      if (!_startedResolve )
    9.30          [self start];
    9.31 -    if (self.serviceRef)
    9.32 -        [self waitForReply];
    9.33 -}    
    9.34 -
    9.35 -- (NSString*) hostname {
    9.36 -    if (!_hostname) [self priv_finishResolve];
    9.37      return _hostname;
    9.38  }
    9.39  
    9.40  - (UInt16) port {
    9.41 -    if (!_port) [self priv_finishResolve];
    9.42 +    if (!_startedResolve )
    9.43 +        [self start];
    9.44      return _port;
    9.45  }
    9.46  
    9.47 @@ -185,16 +182,19 @@
    9.48      LogTo(Bonjour, @"%@: hostname=%@, port=%u, txt=%u bytes", 
    9.49            self, hostname, port, txtData.length);
    9.50  
    9.51 -    // Don't call a setter method to set these properties: the getters are synchronous, so
    9.52 -    // I might already be blocked in a call to one of them, in which case creating a KV
    9.53 -    // notification could cause trouble...
    9.54 -    _hostname = hostname.copy;
    9.55 -    _port = port;
    9.56 +    if (port!=_port || !$equal(hostname,_hostname)) {
    9.57 +        self.hostname = hostname;
    9.58 +        self.port = port;
    9.59 +    }
    9.60      
    9.61 -    // TXT getter is async, though, so I can use a setter to announce the data's availability:
    9.62      [self setTxtData: txtData];
    9.63  }
    9.64  
    9.65 +- (void) gotResponse: (DNSServiceErrorType)errorCode {
    9.66 +    [super gotResponse: errorCode];
    9.67 +    [_addressLookup _serviceGotResponse];
    9.68 +}
    9.69 +
    9.70  
    9.71  static void resolveCallback(DNSServiceRef                       sdRef,
    9.72                              DNSServiceFlags                     flags,
    9.73 @@ -236,8 +236,7 @@
    9.74  - (MYAddressLookup*) addressLookup {
    9.75      if (!_addressLookup) {
    9.76          // Create the lookup the first time this is called:
    9.77 -        _addressLookup = [[MYAddressLookup alloc] initWithHostname: self.hostname];
    9.78 -        _addressLookup.port = _port;
    9.79 +        _addressLookup = [[MYAddressLookup alloc] _initWithBonjourService: self];
    9.80          _addressLookup.interfaceIndex = _interfaceIndex;
    9.81      }
    9.82      // (Re)start the lookup if it's expired:
    10.1 --- a/PortMapper/MYDNSService.h	Sun May 24 15:03:39 2009 -0700
    10.2 +++ b/PortMapper/MYDNSService.h	Wed Jul 01 14:14:32 2009 -0700
    10.3 @@ -38,6 +38,9 @@
    10.4  /** Stops the service. */
    10.5  - (void) stop;
    10.6  
    10.7 +/** Has the service started up? */
    10.8 +@property (readonly) BOOL isRunning;
    10.9 +
   10.10  
   10.11  /** The error status, a DNSServiceErrorType enum; nonzero if something went wrong. 
   10.12      This property is KV observable. */
    11.1 --- a/PortMapper/MYDNSService.m	Sun May 24 15:03:39 2009 -0700
    11.2 +++ b/PortMapper/MYDNSService.m	Wed Jul 01 14:14:32 2009 -0700
    11.3 @@ -143,6 +143,11 @@
    11.4  }
    11.5  
    11.6  
    11.7 +- (BOOL) isRunning {
    11.8 +    return _serviceRef != NULL;
    11.9 +}
   11.10 +
   11.11 +
   11.12  + (NSString*) fullNameOfService: (NSString*)serviceName
   11.13                           ofType: (NSString*)type
   11.14                         inDomain: (NSString*)domain
   11.15 @@ -291,8 +296,12 @@
   11.16      NSAutoreleasePool *pool = [NSAutoreleasePool new];
   11.17      LogTo(DNS,@"---serviceCallback----");
   11.18      DNSServiceErrorType err = DNSServiceProcessResult(_connectionRef);
   11.19 -    if (err)
   11.20 +    if (err) {
   11.21          Warn(@"%@: DNSServiceProcessResult failed, err=%i !!!", self,err);
   11.22 +        //FIX: Are errors here fatal, meaning I should close the connection?
   11.23 +        // I've run into infinite loops constantly getting kDNSServiceErr_ServiceNotRunning
   11.24 +        // or kDNSServiceErr_BadReference ...
   11.25 +    }
   11.26      [pool drain];
   11.27      return !err;
   11.28  }