First checkin after breaking out of Cloudy
authorJens Alfke <jens@mooseyard.com>
Fri May 23 17:37:36 2008 -0700 (2008-05-23)
changeset 09d67172bb323
child 1 8267d5c429c4
First checkin after breaking out of Cloudy
.hgignore
BLIP/BLIP Overview.txt
BLIP/BLIPConnection.h
BLIP/BLIPConnection.m
BLIP/BLIPDispatcher.h
BLIP/BLIPDispatcher.m
BLIP/BLIPMessage.h
BLIP/BLIPMessage.m
BLIP/BLIPProperties.h
BLIP/BLIPProperties.m
BLIP/BLIPReader.h
BLIP/BLIPReader.m
BLIP/BLIPRequest.h
BLIP/BLIPRequest.m
BLIP/BLIPTest.m
BLIP/BLIPWriter.h
BLIP/BLIPWriter.m
BLIP/BLIP_Internal.h
BLIP/runBLIPClient
BLIP/runBLIPListener
IPAddress.h
IPAddress.m
MYNetwork.xcodeproj/TemplateIcon.icns
MYNetwork.xcodeproj/project.pbxproj
TCP/TCPConnection.h
TCP/TCPConnection.m
TCP/TCPEndpoint.h
TCP/TCPEndpoint.m
TCP/TCPListener.h
TCP/TCPListener.m
TCP/TCPStream.h
TCP/TCPStream.m
TCP/TCPWriter.h
TCP/TCPWriter.m
TCP/TCP_Internal.h
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/.hgignore	Fri May 23 17:37:36 2008 -0700
     1.3 @@ -0,0 +1,9 @@
     1.4 +syntax: glob
     1.5 +.DS_Store
     1.6 +build
     1.7 +.svn
     1.8 +(*)
     1.9 +*.pbxuser
    1.10 +*.perspectivev3
    1.11 +*.mpkg
    1.12 +*.framework
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/BLIP/BLIP Overview.txt	Fri May 23 17:37:36 2008 -0700
     2.3 @@ -0,0 +1,66 @@
     2.4 +BLIP OVERVIEW
     2.5 +Jens Alfke <jens@mooseyard.com>
     2.6 +Preliminary Draft 1 -- 21 May 2008
     2.7 +
     2.8 +
     2.9 +BLIP is a generic application-layer network protocol that runs atop TCP. It was inspired by BEEP (in fact BLIP stands for "BEEP-LIke Protocol") but is deliberately simpler and somewhat more limited.
    2.10 +
    2.11 +DATA MODEL
    2.12 +
    2.13 +BLIP lets the two peers on either end of a TCP socket send requests and responses to each other.
    2.14 +Each message and response is very much like a MIME body, as in email or HTTP: it consists of a blob of data of arbitrary length, plus a set of key/value pairs called "properties". The properties are mostly ignored by BLIP itself, but clients can use them for metadata about the body, and for delivery information (i.e. something like BEEP's "profiles".)
    2.15 +
    2.16 +Either peer can send a message at any time; there's no notion of "client" and "server" roles. Multiple messages can be transmitted simultaneously over the same connection, so a very long message does not block any other messages from being delivered. This means that message ordering is a bit looser than in BEEP or HTTP 1.1: the receiver will see the beginnings of messages in the same order in which the sender posted them, but they might not end in that same order. (For example, a long message will take longer to be delivered, so it may finish after messages that were begun after it.)
    2.17 +
    2.18 +The sender can indicate whether a message needs to be replied to; the response is tagged with the identity of the original message, to make it easy for the sender to recognize. This makes it straighforward to implement RPC-style (or REST-style) request/response interactions. (Replies cannot be replied to again, however.)
    2.19 +
    2.20 +A message can be flagged as "urgent". Urgent messages are pushed ahead in the outgoing queue and get a higher fraction of the available bandwidth.
    2.21 +
    2.22 +A message can be flagged as "compressed". This runs its body through the gzip algorithm, ideally making it faster to transmit. (Common markup-based data formats like XML and JSON compress extremely well, at ratios up to 10::1.) The message is decompressed on the receiving end, invisibly to client code.
    2.23 +
    2.24 +WIRE FORMAT
    2.25 +
    2.26 +All multi-byte numbers are encoded in network byte-order (big-endian).
    2.27 +
    2.28 +Each message is packed into a series of bytes consisting of the properties followed by the body. The properties are encoded as a 16-bit byte-count followed by a series of NUL-terminated C strings alternating keys and values.
    2.29 +
    2.30 +The message is then broken up into "frames", usually 4k to 12k bytes. Each frame is prefixed with a 12-byte header containing its length in bytes, message number, and some flags.
    2.31 +
    2.32 +Each of the two unidirectional TCP streams carries a sequence of these frames, and nothing else. If multiple messages are queued up at the sender, their frames will be interleaved, so that one message doesn't block the rest. The ordering is primarily round-robin, except that urgent messages are scheduled more often than regular ones; the scheduler tries to alternate urgent and regular frames, and lets the urgent frames be larger. It's rather like a thread scheduler, really.
    2.33 +
    2.34 +When one peer wants to close the connection, it finishes sending all pending frames and then closes its outgoing (write) stream. The other peer detects this and goes into closing mode as well, sending its pending frames and then closing the other stream, which closes the socket. On the other hand, if a peer's writing stream is closed unexpectedly, or its reading stream closes in mid-frame, this indicates a broken connection.
    2.35 +
    2.36 +  Frame header:
    2.37 +    UInt32           magic;         // magic number (kBLIPFrameHeaderMagicNumber)
    2.38 +    UInt32           number;        // serial number of MSG (starts at 1)
    2.39 +    BLIPMessageFlags flags;         // encodes frame type, "more" flag, and other delivery options
    2.40 +    UInt16           size;          // total size of frame, _including_ this header
    2.41 +
    2.42 +  Flags:
    2.43 +    kBLIP_TypeMask  = 0x000F,       // bits reserved for storing message type
    2.44 +    kBLIP_Compressed= 0x0010,       // data is gzipped
    2.45 +    kBLIP_Urgent    = 0x0020,       // please send sooner/faster
    2.46 +    kBLIP_NoReply   = 0x0040,       // no RPY needed
    2.47 +    kBLIP_MoreComing= 0x0080,       // More frames of this message coming
    2.48 +    // all other bits reserved
    2.49 +
    2.50 +  Message types:
    2.51 +    kBLIP_MSG = 0,                  // initiating message
    2.52 +    kBLIP_RPY = 1,                  // response to a MSG
    2.53 +    kBLIP_ERR = 2                   // error response to a MSG
    2.54 +    // values 3-15 reserved
    2.55 +
    2.56 +
    2.57 +LIMITATIONS
    2.58 +
    2.59 +Compared to BEEP, the BLIP protocol has:
    2.60 +* No channels. (It's as though every message/response were sent on a separate channel.)
    2.61 +* No ANS style responses (multiple answers)
    2.62 +* No proocol for "tuning" the session by negotiating encryption (SSL) or authentication (SASL, etc.)
    2.63 +* No negotiation of closing the connection (a peer can't veto a close)
    2.64 +
    2.65 +Some currently missing features that will likely be added are:
    2.66 +* Flow control (i.e. "windows" to throttle the sender so the listener can keep up)
    2.67 +* Ability to enforce one-at-a-time ordering of a set of requests and responses, so a message isn't sent until the previous message is complete (as in a BEEP channel)
    2.68 +* A more stream-like API for requests and responses, so their bodies can be sent and received incrementally. (The protocol and implementation already support this.)
    2.69 +* Ability to stop an incoming message partway through (e.g. to interrupt a file transfer)
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/BLIP/BLIPConnection.h	Fri May 23 17:37:36 2008 -0700
     3.3 @@ -0,0 +1,83 @@
     3.4 +//
     3.5 +//  BLIPConnection.h
     3.6 +//  MYNetwork
     3.7 +//
     3.8 +//  Created by Jens Alfke on 5/10/08.
     3.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
    3.10 +//
    3.11 +
    3.12 +#import "TCPConnection.h"
    3.13 +#import "TCPListener.h"
    3.14 +@class BLIPRequest, BLIPResponse, BLIPDispatcher;
    3.15 +@protocol BLIPConnectionDelegate;
    3.16 +
    3.17 +
    3.18 +/** Represents a connection to a peer, using the BLIP protocol over a TCP socket.
    3.19 +    Outgoing connections are made simply by instantiating a BLIPConnection via -initToAddress:.
    3.20 +    Incoming connections are usually set up by a BLIPListener and passed to the listener's
    3.21 +    delegate.
    3.22 +    Most of the API is inherited from TCPConnection. */
    3.23 +@interface BLIPConnection : TCPConnection
    3.24 +{
    3.25 +    BLIPDispatcher *_dispatcher;
    3.26 +}
    3.27 +
    3.28 +/** The delegate object that will be called when the connection opens, closes or receives messages. */
    3.29 +@property (assign) id<BLIPConnectionDelegate> delegate;
    3.30 +
    3.31 +@property (readonly) BLIPDispatcher *dispatcher;
    3.32 +
    3.33 +/** Creates an outgoing request, with no properties.
    3.34 +    The body may be nil.
    3.35 +    To send it, call -send. */
    3.36 +- (BLIPRequest*) requestWithBody: (NSData*)body;
    3.37 +
    3.38 +/** Creates an outgoing request.
    3.39 +    The body or properties may be nil.
    3.40 +    To send it, call -send. */
    3.41 +- (BLIPRequest*) requestWithBody: (NSData*)body
    3.42 +                      properties: (NSDictionary*)properies;
    3.43 +
    3.44 +/** Sends a request over this connection.
    3.45 +    (Actually, it queues it to be sent; this method always returns immediately.)
    3.46 +    Call this instead of calling -send on the request itself, if the request was created with
    3.47 +    +[BLIPRequest requestWithBody:] and hasn't yet been assigned to any connection.
    3.48 +    This method will assign it to this connection before sending it.
    3.49 +    The request's matching response object will be returned, or nil if the request couldn't be sent. */
    3.50 +- (BLIPResponse*) sendRequest: (BLIPRequest*)request;
    3.51 +@end
    3.52 +
    3.53 +
    3.54 +
    3.55 +/** The delegate messages that BLIPConnection will send,
    3.56 +    in addition to the ones inherited from TCPConnectionDelegate. */
    3.57 +@protocol BLIPConnectionDelegate <TCPConnectionDelegate>
    3.58 +
    3.59 +/** Called when a BLIPRequest is received from the peer, if there is no BLIPDispatcher
    3.60 +    rule to handle it.
    3.61 +    The delegate should get the request's response object, fill in its data and properties
    3.62 +    or error property, and send it.
    3.63 +    If it doesn't explicitly send a response, a default empty one will be sent;
    3.64 +    to prevent this, call -deferResponse on the request if you want to send a response later. */
    3.65 +- (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request;
    3.66 +
    3.67 +@optional
    3.68 +/** Called when a BLIPResponse (to one of your requests) is received from the peer.
    3.69 +    This is called <i>after</i> the response object's onComplete target, if any, is invoked. */
    3.70 +- (void) connection: (BLIPConnection*)connection receivedResponse: (BLIPResponse*)response;
    3.71 +@end
    3.72 +
    3.73 +
    3.74 +
    3.75 +
    3.76 +/** A "server" that listens on a TCP socket for incoming BLIP connections and creates
    3.77 +    BLIPConnection instances to handle them.
    3.78 +    Most of the API is inherited from TCPListener. */
    3.79 +@interface BLIPListener : TCPListener
    3.80 +{
    3.81 +    BLIPDispatcher *_dispatcher;
    3.82 +}
    3.83 +
    3.84 +@property (readonly) BLIPDispatcher *dispatcher;
    3.85 +
    3.86 +@end
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/BLIP/BLIPConnection.m	Fri May 23 17:37:36 2008 -0700
     4.3 @@ -0,0 +1,158 @@
     4.4 +//
     4.5 +//  BLIPConnection.m
     4.6 +//  MYNetwork
     4.7 +//
     4.8 +//  Created by Jens Alfke on 5/10/08.
     4.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
    4.10 +//
    4.11 +
    4.12 +#import "BLIPConnection.h"
    4.13 +#import "BLIP_Internal.h"
    4.14 +#import "BLIPReader.h"
    4.15 +#import "BLIPWriter.h"
    4.16 +#import "BLIPDispatcher.h"
    4.17 +
    4.18 +#import "Logging.h"
    4.19 +#import "Test.h"
    4.20 +#import "ExceptionUtils.h"
    4.21 +
    4.22 +
    4.23 +NSString* const BLIPErrorDomain = @"BLIP";
    4.24 +
    4.25 +NSError *BLIPMakeError( int errorCode, NSString *message, ... )
    4.26 +{
    4.27 +    va_list args;
    4.28 +    va_start(args,message);
    4.29 +    message = [[NSString alloc] initWithFormat: message arguments: args];
    4.30 +    va_end(args);
    4.31 +    LogTo(BLIP,@"BLIPError #%i: %@",errorCode,message);
    4.32 +    NSDictionary *userInfo = [NSDictionary dictionaryWithObject: message
    4.33 +                                                         forKey: NSLocalizedDescriptionKey];
    4.34 +    [message release];
    4.35 +    return [NSError errorWithDomain: BLIPErrorDomain code: errorCode userInfo: userInfo];
    4.36 +}
    4.37 +
    4.38 +
    4.39 +
    4.40 +
    4.41 +@implementation BLIPConnection
    4.42 +
    4.43 +- (void) dealloc
    4.44 +{
    4.45 +    [_dispatcher release];
    4.46 +    [super dealloc];
    4.47 +}
    4.48 +
    4.49 +- (Class) readerClass                                       {return [BLIPReader class];}
    4.50 +- (Class) writerClass                                       {return [BLIPWriter class];}
    4.51 +- (id<BLIPConnectionDelegate>) delegate                     {return (id)_delegate;}
    4.52 +- (void) setDelegate: (id<BLIPConnectionDelegate>)delegate  {_delegate = delegate;}
    4.53 +
    4.54 +- (BLIPDispatcher*) dispatcher
    4.55 +{
    4.56 +    if( ! _dispatcher ) {
    4.57 +        _dispatcher = [[BLIPDispatcher alloc] init];
    4.58 +        _dispatcher.parent = ((BLIPListener*)self.server).dispatcher;
    4.59 +    }
    4.60 +    return _dispatcher;
    4.61 +}
    4.62 +
    4.63 +
    4.64 +- (void) _dispatchRequest: (BLIPRequest*)request
    4.65 +{
    4.66 +    LogTo(BLIP,@"Received all of %@",request.descriptionWithProperties);
    4.67 +    @try{
    4.68 +        if( ! [self.dispatcher dispatchMessage: request] )
    4.69 +            [self tellDelegate: @selector(connection:receivedRequest:) withObject: request];
    4.70 +        if( ! request.noReply && ! request.repliedTo ) {
    4.71 +            LogTo(BLIP,@"Returning default empty response to %@",request);
    4.72 +            [request respondWithData: nil];
    4.73 +        }
    4.74 +    }@catch( NSException *x ) {
    4.75 +        MYReportException(x,@"Dispatching BLIP request");
    4.76 +        [request respondWithException: x];
    4.77 +    }
    4.78 +}
    4.79 +
    4.80 +- (void) _dispatchResponse: (BLIPResponse*)response
    4.81 +{
    4.82 +    LogTo(BLIP,@"Received all of %@",response);
    4.83 +    [self tellDelegate: @selector(connection:receivedResponse:) withObject: response];
    4.84 +}
    4.85 +
    4.86 +
    4.87 +- (BLIPRequest*) requestWithBody: (NSData*)body
    4.88 +{
    4.89 +    return [[[BLIPRequest alloc] _initWithConnection: self body: body properties: nil] autorelease];
    4.90 +}
    4.91 +
    4.92 +- (BLIPRequest*) requestWithBody: (NSData*)body
    4.93 +                      properties: (NSDictionary*)properties
    4.94 +{
    4.95 +    return [[[BLIPRequest alloc] _initWithConnection: self body: body properties: properties] autorelease];
    4.96 +}
    4.97 +
    4.98 +- (BLIPResponse*) sendRequest: (BLIPRequest*)request
    4.99 +{
   4.100 +    BLIPConnection *itsConnection = request.connection;
   4.101 +    if( itsConnection==nil )
   4.102 +        request.connection = self;
   4.103 +    else
   4.104 +        Assert(itsConnection==self,@"%@ is already assigned to a different BLIPConnection",request);
   4.105 +    return [request send];
   4.106 +}
   4.107 +
   4.108 +
   4.109 +@end
   4.110 +
   4.111 +
   4.112 +
   4.113 +
   4.114 +@implementation BLIPListener
   4.115 +
   4.116 +- (id) initWithPort: (UInt16)port
   4.117 +{
   4.118 +    self = [super initWithPort: port];
   4.119 +    if (self != nil) {
   4.120 +        self.connectionClass = [BLIPConnection class];
   4.121 +    }
   4.122 +    return self;
   4.123 +}
   4.124 +
   4.125 +- (void) dealloc
   4.126 +{
   4.127 +    [_dispatcher release];
   4.128 +    [super dealloc];
   4.129 +}
   4.130 +
   4.131 +- (BLIPDispatcher*) dispatcher
   4.132 +{
   4.133 +    if( ! _dispatcher )
   4.134 +        _dispatcher = [[BLIPDispatcher alloc] init];
   4.135 +    return _dispatcher;
   4.136 +}
   4.137 +
   4.138 +@end
   4.139 +
   4.140 +
   4.141 +/*
   4.142 + Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   4.143 + 
   4.144 + Redistribution and use in source and binary forms, with or without modification, are permitted
   4.145 + provided that the following conditions are met:
   4.146 + 
   4.147 + * Redistributions of source code must retain the above copyright notice, this list of conditions
   4.148 + and the following disclaimer.
   4.149 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   4.150 + and the following disclaimer in the documentation and/or other materials provided with the
   4.151 + distribution.
   4.152 + 
   4.153 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   4.154 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   4.155 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   4.156 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   4.157 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   4.158 +  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   4.159 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   4.160 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   4.161 + */
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/BLIP/BLIPDispatcher.h	Fri May 23 17:37:36 2008 -0700
     5.3 @@ -0,0 +1,28 @@
     5.4 +//
     5.5 +//  BLIPDispatcher.h
     5.6 +//  MYNetwork
     5.7 +//
     5.8 +//  Created by Jens Alfke on 5/15/08.
     5.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
    5.10 +//
    5.11 +
    5.12 +#import <Foundation/Foundation.h>
    5.13 +@class MYTarget, BLIPMessage;
    5.14 +
    5.15 +
    5.16 +@interface BLIPDispatcher : NSObject 
    5.17 +{
    5.18 +    NSMutableArray *_predicates, *_targets;
    5.19 +    BLIPDispatcher *_parent;
    5.20 +}
    5.21 +
    5.22 +@property (retain) BLIPDispatcher *parent;
    5.23 +
    5.24 +- (void) addTarget: (MYTarget*)target forPredicate: (NSPredicate*)predicate;
    5.25 +- (void) removeTarget: (MYTarget*)target;
    5.26 +
    5.27 +- (void) addTarget: (MYTarget*)target forValueOfProperty: (NSString*)value forKey: (NSString*)key;
    5.28 +
    5.29 +- (BOOL) dispatchMessage: (BLIPMessage*)message;
    5.30 +
    5.31 +@end
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/BLIP/BLIPDispatcher.m	Fri May 23 17:37:36 2008 -0700
     6.3 @@ -0,0 +1,108 @@
     6.4 +//
     6.5 +//  BLIPDispatcher.m
     6.6 +//  MYNetwork
     6.7 +//
     6.8 +//  Created by Jens Alfke on 5/15/08.
     6.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
    6.10 +//
    6.11 +
    6.12 +#import "BLIPDispatcher.h"
    6.13 +#import "Target.h"
    6.14 +#import "BLIPRequest.h"
    6.15 +#import "BLIPProperties.h"
    6.16 +
    6.17 +
    6.18 +@implementation BLIPDispatcher
    6.19 +
    6.20 +
    6.21 +- (id) init
    6.22 +{
    6.23 +    self = [super init];
    6.24 +    if (self != nil) {
    6.25 +        _targets = [[NSMutableArray alloc] init];
    6.26 +        _predicates = [[NSMutableArray alloc] init];
    6.27 +    }
    6.28 +    return self;
    6.29 +}
    6.30 +
    6.31 +- (void) dealloc
    6.32 +{
    6.33 +    [_targets release];
    6.34 +    [_predicates release];
    6.35 +    [_parent release];
    6.36 +    [super dealloc];
    6.37 +}
    6.38 +
    6.39 +
    6.40 +@synthesize parent=_parent;
    6.41 +
    6.42 +
    6.43 +- (void) addTarget: (MYTarget*)target forPredicate: (NSPredicate*)predicate
    6.44 +{
    6.45 +    [_targets addObject: target];
    6.46 +    [_predicates addObject: predicate];
    6.47 +}
    6.48 +
    6.49 +
    6.50 +- (void) removeTarget: (MYTarget*)target
    6.51 +{
    6.52 +    NSUInteger i = [_targets indexOfObject: target];
    6.53 +    if( i != NSNotFound ) {
    6.54 +        [_targets removeObjectAtIndex: i];
    6.55 +        [_predicates removeObjectAtIndex: i];
    6.56 +    }
    6.57 +}
    6.58 +
    6.59 +
    6.60 +- (void) addTarget: (MYTarget*)target forValueOfProperty: (NSString*)value forKey: (NSString*)key
    6.61 +{
    6.62 +    [self addTarget: target 
    6.63 +       forPredicate: [NSComparisonPredicate predicateWithLeftExpression: [NSExpression expressionForKeyPath: key]
    6.64 +                                                        rightExpression: [NSExpression expressionForConstantValue: value]
    6.65 +                                                               modifier: NSDirectPredicateModifier
    6.66 +                                                                   type: NSEqualToPredicateOperatorType
    6.67 +                                                                options: 0]];
    6.68 +}
    6.69 +
    6.70 +
    6.71 +- (BOOL) dispatchMessage: (BLIPMessage*)message
    6.72 +{
    6.73 +    NSDictionary *properties = message.properties.allProperties;
    6.74 +    NSUInteger n = _predicates.count;
    6.75 +    for( NSUInteger i=0; i<n; i++ ) {
    6.76 +        NSPredicate *p = [_predicates objectAtIndex: i];
    6.77 +        if( [p evaluateWithObject: properties] ) {
    6.78 +            MYTarget *target = [_targets objectAtIndex: i];
    6.79 +            LogTo(BLIP,@"Dispatcher matched %@ -- calling %@",p,target);
    6.80 +            [target invokeWithSender: message];
    6.81 +            return YES;
    6.82 +        }
    6.83 +    }
    6.84 +    return [_parent dispatchMessage: message];
    6.85 +}
    6.86 +
    6.87 +
    6.88 +@end
    6.89 +
    6.90 +
    6.91 +/*
    6.92 + Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
    6.93 + 
    6.94 + Redistribution and use in source and binary forms, with or without modification, are permitted
    6.95 + provided that the following conditions are met:
    6.96 + 
    6.97 + * Redistributions of source code must retain the above copyright notice, this list of conditions
    6.98 + and the following disclaimer.
    6.99 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   6.100 + and the following disclaimer in the documentation and/or other materials provided with the
   6.101 + distribution.
   6.102 + 
   6.103 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   6.104 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   6.105 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   6.106 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   6.107 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   6.108 +  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   6.109 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   6.110 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   6.111 + */
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/BLIP/BLIPMessage.h	Fri May 23 17:37:36 2008 -0700
     7.3 @@ -0,0 +1,109 @@
     7.4 +//
     7.5 +//  BLIPMessage.h
     7.6 +//  MYNetwork
     7.7 +//
     7.8 +//  Created by Jens Alfke on 5/10/08.
     7.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
    7.10 +//
    7.11 +
    7.12 +#import <Foundation/Foundation.h>
    7.13 +@class BLIPConnection, BLIPProperties, BLIPMutableProperties;
    7.14 +
    7.15 +
    7.16 +/** NSError domain and codes for BLIP */
    7.17 +extern NSString* const BLIPErrorDomain;
    7.18 +enum {
    7.19 +    kBLIPError_BadData = 1,
    7.20 +    kBLIPError_BadFrame,
    7.21 +    kBLIPError_Disconnected,
    7.22 +    kBLIPError_PeerNotAllowed,
    7.23 +    
    7.24 +    kBLIPError_Misc = 99,
    7.25 +    
    7.26 +    // errors returned in responses:
    7.27 +    kBLIPError_BadRequest = 400,
    7.28 +    kBLIPError_Forbidden = 403,
    7.29 +    kBLIPError_NotFound = 404,
    7.30 +    kBLIPError_BadRange = 416,
    7.31 +    
    7.32 +    kBLIPError_HandlerFailed = 501,
    7.33 +    kBLIPError_Unspecified = 599            // peer didn't send any detailed error info
    7.34 +};
    7.35 +
    7.36 +
    7.37 +/** Abstract superclass for both requests and responses. */
    7.38 +@interface BLIPMessage : NSObject 
    7.39 +{
    7.40 +    BLIPConnection *_connection;
    7.41 +    UInt16 _flags;
    7.42 +    UInt32 _number;
    7.43 +    BLIPProperties *_properties;
    7.44 +    NSData *_body;
    7.45 +    NSMutableData *_encodedBody;
    7.46 +    NSMutableData *_mutableBody;
    7.47 +    BOOL _isMine, _isMutable, _sent, _propertiesAvailable, _complete;
    7.48 +    SInt32 _bytesWritten;
    7.49 +};
    7.50 +
    7.51 +/** The BLIPConnection associated with this message. */
    7.52 +@property (readonly,retain) BLIPConnection *connection;
    7.53 +
    7.54 +/** This message's serial number in its connection.
    7.55 +    A BLIPRequest's number is initially zero, then assigned when it's sent.
    7.56 +    A BLIPResponse is automatically assigned the same number as the request it replies to. */
    7.57 +@property (readonly) UInt32 number;
    7.58 +
    7.59 +/** Is this a message sent by me (as opposed to the peer)? */
    7.60 +@property (readonly) BOOL isMine;
    7.61 +
    7.62 +/** Has this message been sent yet? (Only makes sense when isMe is set.) */
    7.63 +@property (readonly) BOOL sent;
    7.64 +
    7.65 +/** Has enough of the message arrived to read its properies? */
    7.66 +@property (readonly) BOOL propertiesAvailable;
    7.67 +
    7.68 +/** Has the entire message, including the body, arrived? */
    7.69 +@property (readonly) BOOL complete;
    7.70 +
    7.71 +/** Should the message body be compressed using gzip?
    7.72 +    This property can only be set before sending the message. */
    7.73 +@property BOOL compressed;
    7.74 +
    7.75 +/** Should the message be sent ahead of normal-priority messages?
    7.76 +    This property can only be set before sending the message. */
    7.77 +@property BOOL urgent;
    7.78 +
    7.79 +/** Can this message be changed? (Only true for outgoing messages, before you send them.) */
    7.80 +@property (readonly) BOOL isMutable;
    7.81 +
    7.82 +/** The message body, a blob of arbitrary data. */
    7.83 +@property (copy) NSData *body;
    7.84 +
    7.85 +/** Appends data to the body. */
    7.86 +- (void) addToBody: (NSData*)data;
    7.87 +
    7.88 +#pragma mark PROPERTIES:
    7.89 +
    7.90 +/** The message's properties, a dictionary-like object. */
    7.91 +@property (readonly) BLIPProperties* properties;
    7.92 +
    7.93 +/** Mutable version of the message's properties; only available if this mesage is mutable. */
    7.94 +@property (readonly) BLIPMutableProperties* mutableProperties;
    7.95 +
    7.96 +/** The value of the "Content-Type" property, which is by convention the MIME type of the body. */
    7.97 +@property (copy) NSString *contentType;
    7.98 +
    7.99 +/** The value of the "Profile" property, which by convention identifies the purpose of the message. */
   7.100 +@property (copy) NSString *profile;
   7.101 +
   7.102 +/** A shortcut to get the value of a property. */
   7.103 +- (NSString*) valueOfProperty: (NSString*)property;
   7.104 +
   7.105 +/** A shortcut to set the value of a property. A nil value deletes that property. */
   7.106 +- (void) setValue: (NSString*)value ofProperty: (NSString*)property;
   7.107 +
   7.108 +/** Similar to -description, but also shows the properties and their values. */
   7.109 +@property (readonly) NSString* descriptionWithProperties;
   7.110 +
   7.111 +
   7.112 +@end
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/BLIP/BLIPMessage.m	Fri May 23 17:37:36 2008 -0700
     8.3 @@ -0,0 +1,335 @@
     8.4 +//
     8.5 +//  BLIPMessage.m
     8.6 +//  MYNetwork
     8.7 +//
     8.8 +//  Created by Jens Alfke on 5/10/08.
     8.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
    8.10 +//
    8.11 +
    8.12 +#import "BLIPMessage.h"
    8.13 +#import "BLIP_Internal.h"
    8.14 +#import "BLIPReader.h"
    8.15 +#import "BLIPWriter.h"
    8.16 +
    8.17 +#import "ExceptionUtils.h"
    8.18 +#import "Target.h"
    8.19 +
    8.20 +// From Google Toolbox For Mac <http://code.google.com/p/google-toolbox-for-mac/>
    8.21 +#import "GTMNSData+zlib.h"
    8.22 +
    8.23 +
    8.24 +@implementation BLIPMessage
    8.25 +
    8.26 +
    8.27 +- (id) _initWithConnection: (BLIPConnection*)connection
    8.28 +                    isMine: (BOOL)isMine
    8.29 +                     flags: (BLIPMessageFlags)flags
    8.30 +                    number: (UInt32)msgNo
    8.31 +                      body: (NSData*)body
    8.32 +{
    8.33 +    self = [super init];
    8.34 +    if (self != nil) {
    8.35 +        _connection = [connection retain];
    8.36 +        _isMine = isMine;
    8.37 +        _flags = flags;
    8.38 +        _number = msgNo;
    8.39 +        if( isMine ) {
    8.40 +            _body = body.copy;
    8.41 +            _properties = [[BLIPMutableProperties alloc] init];
    8.42 +            _propertiesAvailable = YES;
    8.43 +        } else {
    8.44 +            _encodedBody = body.mutableCopy;
    8.45 +        }
    8.46 +        LogTo(BLIPVerbose,@"INIT %@",self);
    8.47 +    }
    8.48 +    return self;
    8.49 +}
    8.50 +
    8.51 +- (void) dealloc
    8.52 +{
    8.53 +    LogTo(BLIPVerbose,@"DEALLOC %@",self);
    8.54 +    [_properties release];
    8.55 +    [_encodedBody release];
    8.56 +    [_mutableBody release];
    8.57 +    [_body release];
    8.58 +    [_connection release];
    8.59 +    [super dealloc];
    8.60 +}
    8.61 +
    8.62 +
    8.63 +- (NSString*) description
    8.64 +{
    8.65 +    NSUInteger length = (_body.length ?: _mutableBody.length) ?: _encodedBody.length;
    8.66 +    NSMutableString *desc = [NSMutableString stringWithFormat: @"%@[#%u, %u bytes",
    8.67 +                             self.class,_number, length];
    8.68 +    if( _flags & kBLIP_Compressed ) {
    8.69 +        if( _encodedBody && _encodedBody.length != length )
    8.70 +            [desc appendFormat: @" (%u gzipped)", _encodedBody.length];
    8.71 +        else
    8.72 +            [desc appendString: @", gzipped"];
    8.73 +    }
    8.74 +    if( _flags & kBLIP_Urgent )
    8.75 +        [desc appendString: @", urgent"];
    8.76 +    if( _flags & kBLIP_NoReply )
    8.77 +        [desc appendString: @", noreply"];
    8.78 +    [desc appendString: @"]"];
    8.79 +    return desc;
    8.80 +}
    8.81 +
    8.82 +- (NSString*) descriptionWithProperties
    8.83 +{
    8.84 +    NSMutableString *desc = (NSMutableString*)self.description;
    8.85 +    [desc appendFormat: @" %@", self.properties.allProperties];
    8.86 +    return desc;
    8.87 +}
    8.88 +
    8.89 +
    8.90 +#pragma mark -
    8.91 +#pragma mark PROPERTIES & METADATA:
    8.92 +
    8.93 +
    8.94 +@synthesize connection=_connection, number=_number, isMine=_isMine, isMutable=_isMutable,
    8.95 +            _bytesWritten, sent=_sent, propertiesAvailable=_propertiesAvailable, complete=_complete;
    8.96 +
    8.97 +
    8.98 +- (void) _setFlag: (BLIPMessageFlags)flag value: (BOOL)value
    8.99 +{
   8.100 +    Assert(_isMine && _isMutable);
   8.101 +    if( value )
   8.102 +        _flags |= flag;
   8.103 +    else
   8.104 +        _flags &= ~flag;
   8.105 +}
   8.106 +
   8.107 +- (BOOL) compressed                         {return (_flags & kBLIP_Compressed) != 0;}
   8.108 +- (BOOL) urgent                             {return (_flags & kBLIP_Urgent) != 0;}
   8.109 +- (void) setCompressed: (BOOL)compressed    {[self _setFlag: kBLIP_Compressed value: compressed];}
   8.110 +- (void) setUrgent: (BOOL)high              {[self _setFlag: kBLIP_Urgent value: high];}
   8.111 +
   8.112 +
   8.113 +- (NSData*) body
   8.114 +{
   8.115 +    if( ! _body && _isMine )
   8.116 +        return [[_mutableBody copy] autorelease];
   8.117 +    else
   8.118 +        return _body;
   8.119 +}
   8.120 +
   8.121 +- (void) setBody: (NSData*)body
   8.122 +{
   8.123 +    Assert(_isMine && _isMutable);
   8.124 +    if( _mutableBody )
   8.125 +        [_mutableBody setData: body];
   8.126 +    else
   8.127 +        _mutableBody = [body mutableCopy];
   8.128 +}
   8.129 +
   8.130 +- (void) _addToBody: (NSData*)data
   8.131 +{
   8.132 +    if( data.length ) {
   8.133 +        if( _mutableBody )
   8.134 +            [_mutableBody appendData: data];
   8.135 +        else
   8.136 +            _mutableBody = [data mutableCopy];
   8.137 +        setObj(&_body,nil);
   8.138 +    }
   8.139 +}
   8.140 +
   8.141 +- (void) addToBody: (NSData*)data
   8.142 +{
   8.143 +    Assert(_isMine && _isMutable);
   8.144 +    [self _addToBody: data];
   8.145 +}
   8.146 +
   8.147 +
   8.148 +- (BLIPProperties*) properties
   8.149 +{
   8.150 +    return _properties;
   8.151 +}
   8.152 +
   8.153 +- (BLIPMutableProperties*) mutableProperties
   8.154 +{
   8.155 +    Assert(_isMine && _isMutable);
   8.156 +    return (BLIPMutableProperties*)_properties;
   8.157 +}
   8.158 +
   8.159 +- (NSString*) valueOfProperty: (NSString*)property
   8.160 +{
   8.161 +    return [_properties valueOfProperty: property];
   8.162 +}
   8.163 +
   8.164 +- (void) setValue: (NSString*)value ofProperty: (NSString*)property
   8.165 +{
   8.166 +    [self.mutableProperties setValue: value ofProperty: property];
   8.167 +}
   8.168 +
   8.169 +- (NSString*) contentType               {return [_properties valueOfProperty: @"Content-Type"];}
   8.170 +- (void) setContentType: (NSString*)t   {[self setValue: t ofProperty: @"Content-Type"];}
   8.171 +- (NSString*) profile                   {return [_properties valueOfProperty: @"Profile"];}
   8.172 +- (void) setProfile: (NSString*)p       {[self setValue: p ofProperty: @"Profile"];}
   8.173 +
   8.174 +
   8.175 +#pragma mark -
   8.176 +#pragma mark I/O:
   8.177 +
   8.178 +
   8.179 +- (void) _encode
   8.180 +{
   8.181 +    Assert(_isMine && _isMutable);
   8.182 +    _isMutable = NO;
   8.183 +
   8.184 +    BLIPProperties *oldProps = _properties;
   8.185 +    _properties = [oldProps copy];
   8.186 +    [oldProps release];
   8.187 +    
   8.188 +    _encodedBody = [_properties.encodedData mutableCopy];
   8.189 +    Assert(_encodedBody.length>=2);
   8.190 +
   8.191 +    NSData *body = _body ?: _mutableBody;
   8.192 +    NSUInteger length = body.length;
   8.193 +    if( length > 0 ) {
   8.194 +        if( self.compressed ) {
   8.195 +            body = [NSData gtm_dataByGzippingData: body compressionLevel: 5];
   8.196 +            LogTo(BLIPVerbose,@"Compressed %@ to %u bytes (%.0f%%)", self,body.length,
   8.197 +                  body.length*100.0/length);
   8.198 +        }
   8.199 +        [_encodedBody appendData: body];
   8.200 +    }
   8.201 +}
   8.202 +
   8.203 +
   8.204 +- (void) _assignedNumber: (UInt32)number
   8.205 +{
   8.206 +    Assert(_number==0,@"%@ has already been sent",self);
   8.207 +    _number = number;
   8.208 +    _isMutable = NO;
   8.209 +}
   8.210 +
   8.211 +
   8.212 +- (BOOL) _writeFrameTo: (BLIPWriter*)writer maxSize: (UInt16)maxSize
   8.213 +{
   8.214 +    Assert(_number!=0);
   8.215 +    Assert(_isMine);
   8.216 +    Assert(_encodedBody);
   8.217 +    if( _bytesWritten==0 )
   8.218 +        LogTo(BLIP,@"Now sending %@",self);
   8.219 +    ssize_t lengthToWrite = _encodedBody.length - _bytesWritten;
   8.220 +    if( lengthToWrite <= 0 && _bytesWritten > 0 )
   8.221 +        return NO; // done
   8.222 +    Assert(maxSize > sizeof(BLIPFrameHeader));
   8.223 +    maxSize -= sizeof(BLIPFrameHeader);
   8.224 +    UInt16 flags = _flags;
   8.225 +    if( lengthToWrite > maxSize ) {
   8.226 +        lengthToWrite = maxSize;
   8.227 +        flags |= kBLIP_MoreComing;
   8.228 +        LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u", self, _bytesWritten, _bytesWritten+lengthToWrite);
   8.229 +    } else {
   8.230 +        flags &= ~kBLIP_MoreComing;
   8.231 +        LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u (finished)", self, _bytesWritten, _bytesWritten+lengthToWrite);
   8.232 +    }
   8.233 +        
   8.234 +    // First write the frame header:
   8.235 +    BLIPFrameHeader header = {  EndianU32_NtoB(kBLIPFrameHeaderMagicNumber),
   8.236 +                                EndianU32_NtoB(_number),
   8.237 +                                EndianU16_NtoB(flags),
   8.238 +                                EndianU16_NtoB(sizeof(BLIPFrameHeader) + lengthToWrite) };
   8.239 +    
   8.240 +    [writer writeData: [NSData dataWithBytes: &header length: sizeof(header)]];
   8.241 +    
   8.242 +    // Then write the body:
   8.243 +    if( lengthToWrite > 0 ) {
   8.244 +        [writer writeData: [NSData dataWithBytesNoCopy: (UInt8*)_encodedBody.bytes + _bytesWritten
   8.245 +                                              length: lengthToWrite
   8.246 +                                        freeWhenDone: NO]];
   8.247 +        _bytesWritten += lengthToWrite;
   8.248 +    }
   8.249 +    return (flags & kBLIP_MoreComing) != 0;
   8.250 +}
   8.251 +
   8.252 +
   8.253 +- (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body
   8.254 +{
   8.255 +    Assert(!_isMine);
   8.256 +    AssertEq(header->number,_number);
   8.257 +    Assert(_flags & kBLIP_MoreComing);
   8.258 +    
   8.259 +    BLIPMessageType frameType = (header->flags & kBLIP_TypeMask), curType = (_flags & kBLIP_TypeMask);
   8.260 +    if( frameType != curType ) {
   8.261 +        Assert(curType==kBLIP_RPY && frameType==kBLIP_ERR && _mutableBody.length==0,
   8.262 +               @"Incoming frame's type %i doesn't match %@",frameType,self);
   8.263 +        _flags = (_flags & ~kBLIP_TypeMask) | frameType;
   8.264 +    }
   8.265 +
   8.266 +    if( _encodedBody )
   8.267 +        [_encodedBody appendData: body];
   8.268 +    else
   8.269 +        _encodedBody = [body mutableCopy];
   8.270 +    LogTo(BLIPVerbose,@"%@ rcvd bytes %u-%u", self, _encodedBody.length-body.length, _encodedBody.length);
   8.271 +    
   8.272 +    if( ! _properties ) {
   8.273 +        // Try to extract the properties:
   8.274 +        ssize_t usedLength;
   8.275 +        setObj(&_properties, [BLIPProperties propertiesWithEncodedData: _encodedBody usedLength: &usedLength]);
   8.276 +        if( _properties ) {
   8.277 +            [_encodedBody replaceBytesInRange: NSMakeRange(0,usedLength)
   8.278 +                                    withBytes: NULL length: 0];
   8.279 +        } else if( usedLength < 0 )
   8.280 +            return NO;
   8.281 +        self.propertiesAvailable = YES;
   8.282 +    }
   8.283 +    
   8.284 +    if( ! (header->flags & kBLIP_MoreComing) ) {
   8.285 +        // After last frame, decode the data:
   8.286 +        _flags &= ~kBLIP_MoreComing;
   8.287 +        if( ! _properties )
   8.288 +            return NO;
   8.289 +        unsigned encodedLength = _encodedBody.length;
   8.290 +        if( self.compressed && encodedLength>0 ) {
   8.291 +            _body = [[NSData gtm_dataByInflatingData: _encodedBody] copy];
   8.292 +            if( ! _body )
   8.293 +                return NO;
   8.294 +            LogTo(BLIPVerbose,@"Uncompressed %@ from %u bytes (%.1fx)", self, encodedLength,
   8.295 +                  _body.length/(float)encodedLength);
   8.296 +        } else {
   8.297 +            _body = [_encodedBody copy];
   8.298 +        }
   8.299 +        setObj(&_encodedBody,nil);
   8.300 +        self.propertiesAvailable = self.complete = YES;
   8.301 +    }
   8.302 +    return YES;
   8.303 +}
   8.304 +
   8.305 +
   8.306 +- (void) _connectionClosed
   8.307 +{
   8.308 +    if( _isMine ) {
   8.309 +        _bytesWritten = 0;
   8.310 +        _flags |= kBLIP_MoreComing;
   8.311 +    }
   8.312 +}
   8.313 +
   8.314 +
   8.315 +@end
   8.316 +
   8.317 +
   8.318 +/*
   8.319 + Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   8.320 + 
   8.321 + Redistribution and use in source and binary forms, with or without modification, are permitted
   8.322 + provided that the following conditions are met:
   8.323 + 
   8.324 + * Redistributions of source code must retain the above copyright notice, this list of conditions
   8.325 + and the following disclaimer.
   8.326 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   8.327 + and the following disclaimer in the documentation and/or other materials provided with the
   8.328 + distribution.
   8.329 + 
   8.330 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   8.331 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   8.332 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   8.333 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   8.334 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   8.335 +  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   8.336 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   8.337 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   8.338 + */
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/BLIP/BLIPProperties.h	Fri May 23 17:37:36 2008 -0700
     9.3 @@ -0,0 +1,61 @@
     9.4 +//
     9.5 +//  BLIPProperties.h
     9.6 +//  MYNetwork
     9.7 +//
     9.8 +//  Created by Jens Alfke on 5/13/08.
     9.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
    9.10 +//
    9.11 +
    9.12 +#import <Foundation/Foundation.h>
    9.13 +
    9.14 +
    9.15 +/** A key/value property store, like a set of MIME or RFC822 headers (but without the weird details).
    9.16 +    It can be written to or read from a block of data; the data is binary, not the textual
    9.17 +    format that MIME uses. */
    9.18 +@interface BLIPProperties : NSObject <NSCopying, NSMutableCopying>
    9.19 +
    9.20 +/** Parse properties from a block of data.
    9.21 +    On success, returns a Properties object and sets *usedLength to the number of bytes of
    9.22 +    data consumed.
    9.23 +    If the data doesn't contain the complete properties, returns nil and sets *usedLength to zero.
    9.24 +    If the properties are syntactically invalid, returns nil and sets *usedLength to a negative number.
    9.25 +*/
    9.26 ++ (BLIPProperties*) propertiesWithEncodedData: (NSData*)data
    9.27 +                                   usedLength: (ssize_t*)usedLength;
    9.28 +
    9.29 +/** Returns an empty autoreleased instance. */
    9.30 ++ (BLIPProperties*) properties;
    9.31 +
    9.32 +/** Property value lookup. (Case-sensitive, like NSDictionary, but unlike RFC822.) */
    9.33 +- (NSString*) valueOfProperty: (NSString*)prop;
    9.34 +
    9.35 +/** Returns all the properties/values as a dictionary. */
    9.36 +@property (readonly) NSDictionary* allProperties;
    9.37 +
    9.38 +/** The number of properties. */
    9.39 +@property (readonly) NSUInteger count;
    9.40 +
    9.41 +/** The raw data representation of the properties. */
    9.42 +@property (readonly) NSData *encodedData;
    9.43 +
    9.44 +@end
    9.45 +
    9.46 +
    9.47 +
    9.48 +/** Mutable subclass of BLIPProperties, used for creating new instances. */
    9.49 +@interface BLIPMutableProperties : BLIPProperties
    9.50 +{
    9.51 +    NSMutableDictionary *_properties;
    9.52 +}
    9.53 +
    9.54 +/** Initializes a new instance, adding all the key/value pairs from the given NSDictionary. */
    9.55 +- (id) initWithDictionary: (NSDictionary*)dict;
    9.56 +
    9.57 +/** Sets the value of a property. A nil value is allowed, and removes the property. */
    9.58 +- (void) setValue: (NSString*)value ofProperty: (NSString*)prop;
    9.59 +
    9.60 +/** Sets the receiver's key/value pairs from the given NSDictionary.
    9.61 +    All previously existing properties are removed first. */
    9.62 +- (void) setAllProperties: (NSDictionary*)properties;
    9.63 +
    9.64 +@end
    9.65 \ No newline at end of file
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/BLIP/BLIPProperties.m	Fri May 23 17:37:36 2008 -0700
    10.3 @@ -0,0 +1,409 @@
    10.4 +//
    10.5 +//  BLIPProperties.m
    10.6 +//  MYNetwork
    10.7 +//
    10.8 +//  Created by Jens Alfke on 5/13/08.
    10.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   10.10 +//
   10.11 +
   10.12 +#import "BLIPProperties.h"
   10.13 +
   10.14 +
   10.15 +/** Common strings are abbreviated as single-byte strings in the packed form.
   10.16 +    The ascii value of the single character minus one is the index into this table. */
   10.17 +static const char* kAbbreviations[] = {
   10.18 +    "Content-Type",
   10.19 +    "Profile",
   10.20 +    "Channel"
   10.21 +    "Error-Code"
   10.22 +    "Error-Domain",
   10.23 +    "application/octet-stream",
   10.24 +    "text/plain",
   10.25 +    "text/xml",
   10.26 +    "text/yaml",
   10.27 +    "application/x-cloudy-signed+yaml",
   10.28 +};
   10.29 +#define kNAbbreviations ((sizeof(kAbbreviations)/sizeof(const char*)))  // cannot exceed 31!
   10.30 +
   10.31 +
   10.32 +
   10.33 +@interface BLIPPackedProperties : BLIPProperties
   10.34 +{
   10.35 +    NSData *_data;
   10.36 +    int _count;
   10.37 +    const char **_strings;
   10.38 +    int _nStrings;
   10.39 +}
   10.40 +
   10.41 +@end
   10.42 +
   10.43 +
   10.44 +
   10.45 +// The base class just represents an immutable empty collection.
   10.46 +@implementation BLIPProperties
   10.47 +
   10.48 +
   10.49 ++ (BLIPProperties*) propertiesWithEncodedData: (NSData*)data usedLength: (ssize_t*)usedLength
   10.50 +{
   10.51 +    size_t available = data.length;
   10.52 +    if( available < sizeof(UInt16) ) {
   10.53 +        // Not enough available to read length:
   10.54 +        *usedLength = 0;
   10.55 +        return nil;
   10.56 +    }
   10.57 +    
   10.58 +    // Read the length:
   10.59 +    const char *bytes = data.bytes;
   10.60 +    size_t length = EndianU16_BtoN( *(UInt16*)bytes ) + sizeof(UInt16);
   10.61 +    if( length > available ) {
   10.62 +        // Properties not complete yet.
   10.63 +        *usedLength = 0;
   10.64 +        return nil;
   10.65 +    }
   10.66 +    
   10.67 +    // Complete -- try to create an object:
   10.68 +    BLIPProperties *props;
   10.69 +    if( length > sizeof(UInt16) )
   10.70 +        props = [[[BLIPPackedProperties alloc] initWithBytes: bytes length: length] autorelease];
   10.71 +    else
   10.72 +        props = [BLIPProperties properties];
   10.73 +    
   10.74 +    *usedLength = props ?length :-1;
   10.75 +    return props;
   10.76 +}
   10.77 +
   10.78 +
   10.79 +- (id) copyWithZone: (NSZone*)zone
   10.80 +{
   10.81 +    return [self retain];
   10.82 +}
   10.83 +
   10.84 +- (id) mutableCopyWithZone: (NSZone*)zone
   10.85 +{
   10.86 +    return [[BLIPMutableProperties allocWithZone: zone] initWithDictionary: self.allProperties];
   10.87 +}
   10.88 +
   10.89 +- (BOOL) isEqual: (id)other
   10.90 +{
   10.91 +    return [other isKindOfClass: [BLIPProperties class]]
   10.92 +        && [self.allProperties isEqual: [other allProperties]];
   10.93 +}
   10.94 +
   10.95 +- (NSString*) valueOfProperty: (NSString*)prop  {return nil;}
   10.96 +- (NSDictionary*) allProperties                 {return [NSDictionary dictionary];}
   10.97 +- (NSUInteger) count                            {return 0;}
   10.98 +- (NSUInteger) dataLength                       {return sizeof(UInt16);}
   10.99 +
  10.100 +- (NSData*) encodedData
  10.101 +{
  10.102 +    UInt16 len = 0;
  10.103 +    return [NSData dataWithBytes: &len length: sizeof(len)];
  10.104 +}
  10.105 +
  10.106 +
  10.107 ++ (BLIPProperties*) properties
  10.108 +{
  10.109 +    static BLIPProperties *sEmptyInstance;
  10.110 +    if( ! sEmptyInstance )
  10.111 +        sEmptyInstance = [[self alloc] init];
  10.112 +    return sEmptyInstance;
  10.113 +}
  10.114 +
  10.115 +
  10.116 +@end
  10.117 +
  10.118 +
  10.119 +
  10.120 +/** Internal immutable subclass that keeps its contents in the packed data representation. */
  10.121 +@implementation BLIPPackedProperties
  10.122 +
  10.123 +
  10.124 +- (id) initWithBytes: (const char*)bytes length: (size_t)length
  10.125 +{
  10.126 +    self = [super init];
  10.127 +    if (self != nil) {
  10.128 +        // Copy data, then skip the length field:
  10.129 +        _data = [[NSData alloc] initWithBytes: bytes length: length];
  10.130 +        bytes = (const char*)_data.bytes + sizeof(UInt16);
  10.131 +        length -= sizeof(UInt16);
  10.132 +        
  10.133 +        if( bytes[length-1]!='\0' )
  10.134 +            goto fail;
  10.135 +
  10.136 +        // The data consists of consecutive NUL-terminated strings, alternating key/value:
  10.137 +        unsigned capacity = 0;
  10.138 +        const char *end = bytes+length;
  10.139 +        for( const char *str=bytes; str < end; str += strlen(str)+1, _nStrings++ ) {
  10.140 +            if( _nStrings >= capacity ) {
  10.141 +                capacity = capacity ?(2*capacity) :4;
  10.142 +                _strings = realloc(_strings, capacity*sizeof(const char**));
  10.143 +            }
  10.144 +            UInt8 first = (UInt8)str[0];
  10.145 +            if( first>'\0' && first<' ' && str[1]=='\0' ) {
  10.146 +                // Single-control-character property string is an abbreviation:
  10.147 +                if( first > kNAbbreviations )
  10.148 +                    goto fail;
  10.149 +                _strings[_nStrings] = kAbbreviations[first-1];
  10.150 +            } else
  10.151 +                _strings[_nStrings] = str;
  10.152 +        }
  10.153 +        
  10.154 +        // It's illegal for the data to end with a non-NUL or for there to be an odd number of strings:
  10.155 +        if( (_nStrings & 1) )
  10.156 +            goto fail;
  10.157 +        
  10.158 +        return self;
  10.159 +            
  10.160 +    fail:
  10.161 +        Warn(@"BLIPProperties: invalid data");
  10.162 +        [self release];
  10.163 +        return nil;
  10.164 +    }
  10.165 +    return self;
  10.166 +}
  10.167 +
  10.168 +
  10.169 +- (void) dealloc
  10.170 +{
  10.171 +    if( _strings ) free(_strings);
  10.172 +    [_data release];
  10.173 +    [super dealloc];
  10.174 +}
  10.175 +
  10.176 +- (id) copyWithZone: (NSZone*)zone
  10.177 +{
  10.178 +    return [self retain];
  10.179 +}
  10.180 +
  10.181 +- (id) mutableCopyWithZone: (NSZone*)zone
  10.182 +{
  10.183 +    return [[BLIPMutableProperties allocWithZone: zone] initWithDictionary: self.allProperties];
  10.184 +}
  10.185 +
  10.186 +
  10.187 +- (NSString*) valueOfProperty: (NSString*)prop
  10.188 +{
  10.189 +    const char *propStr = [prop UTF8String];
  10.190 +    Assert(propStr);
  10.191 +    // Search in reverse order so that later values will take precedence over earlier ones.
  10.192 +    for( int i=_nStrings-2; i>=0; i-=2 ) {
  10.193 +        if( strcmp(propStr, _strings[i]) == 0 )
  10.194 +            return [NSString stringWithUTF8String: _strings[i+1]];
  10.195 +    }
  10.196 +    return nil;
  10.197 +}
  10.198 +
  10.199 +
  10.200 +- (NSDictionary*) allProperties
  10.201 +{
  10.202 +    NSMutableDictionary *props = [NSMutableDictionary dictionaryWithCapacity: _nStrings/2];
  10.203 +    // Add values in forward order so that later ones will overwrite (take precedence over)
  10.204 +    // earlier ones, which matches the behavior of -valueOfProperty.
  10.205 +    // (However, note that unlike -valueOfProperty, this dictionary is case-sensitive!)
  10.206 +    for( int i=0; i<_nStrings; i+=2 ) {
  10.207 +        NSString *key = [[NSString alloc] initWithUTF8String: _strings[i]];
  10.208 +        NSString *value = [[NSString alloc] initWithUTF8String: _strings[i+1]];
  10.209 +        if( key && value )
  10.210 +            [props setObject: value forKey: key];
  10.211 +        [key release];
  10.212 +        [value release];
  10.213 +    }
  10.214 +    return props;
  10.215 +}
  10.216 +
  10.217 +
  10.218 +- (NSUInteger) count        {return _nStrings/2;}
  10.219 +- (NSData*) encodedData            {return _data;}
  10.220 +- (NSUInteger) dataLength   {return _data.length;}
  10.221 +
  10.222 +
  10.223 +@end
  10.224 +
  10.225 +
  10.226 +
  10.227 +/** Mutable subclass that stores its properties in an NSMutableDictionary. */
  10.228 +@implementation BLIPMutableProperties
  10.229 +
  10.230 +
  10.231 ++ (BLIPProperties*) properties
  10.232 +{
  10.233 +    return [[self alloc] initWithDictionary: nil];
  10.234 +}
  10.235 +
  10.236 +- (id) init
  10.237 +{
  10.238 +    self = [super init];
  10.239 +    if (self != nil) {
  10.240 +        _properties = [[NSMutableDictionary alloc] init];
  10.241 +    }
  10.242 +    return self;
  10.243 +}
  10.244 +
  10.245 +- (id) initWithDictionary: (NSDictionary*)dict
  10.246 +{
  10.247 +    self = [super init];
  10.248 +    if (self != nil) {
  10.249 +        _properties = dict ?[dict mutableCopy] :[[NSMutableDictionary alloc] init];
  10.250 +    }
  10.251 +    return self;
  10.252 +}
  10.253 +
  10.254 +- (id) initWithProperties: (BLIPProperties*)properties
  10.255 +{
  10.256 +    return [self initWithDictionary: [properties allProperties]];
  10.257 +}
  10.258 +
  10.259 +- (void) dealloc
  10.260 +{
  10.261 +    [_properties release];
  10.262 +    [super dealloc];
  10.263 +}
  10.264 +
  10.265 +- (id) copyWithZone: (NSZone*)zone
  10.266 +{
  10.267 +    ssize_t usedLength;
  10.268 +    BLIPProperties *copy = [BLIPProperties propertiesWithEncodedData: self.encodedData usedLength: &usedLength];
  10.269 +    Assert(copy);
  10.270 +    return [copy retain];
  10.271 +}
  10.272 +
  10.273 +
  10.274 +- (NSString*) valueOfProperty: (NSString*)prop
  10.275 +{
  10.276 +    return [_properties objectForKey: prop];
  10.277 +}
  10.278 +
  10.279 +- (NSDictionary*) allProperties
  10.280 +{
  10.281 +    return _properties;
  10.282 +}
  10.283 +
  10.284 +- (NSUInteger) count        {return _properties.count;}
  10.285 +
  10.286 +
  10.287 +static void appendStr( NSMutableData *data, NSString *str ) {
  10.288 +    const char *utf8 = [str UTF8String];
  10.289 +    size_t size = strlen(utf8)+1;
  10.290 +    for( int i=0; i<kNAbbreviations; i++ )
  10.291 +        if( memcmp(utf8,kAbbreviations[i],size)==0 ) {
  10.292 +            const UInt8 abbrev[2] = {i+1,0};
  10.293 +            [data appendBytes: &abbrev length: 2];
  10.294 +            return;
  10.295 +        }
  10.296 +    [data appendBytes: utf8 length: size];
  10.297 +}
  10.298 +
  10.299 +- (NSData*) encodedData
  10.300 +{
  10.301 +    NSMutableData *data = [NSMutableData dataWithCapacity: 16*_properties.count];
  10.302 +    [data setLength: sizeof(UInt16)]; // leave room for length
  10.303 +    for( NSString *name in _properties ) {
  10.304 +        appendStr(data,name);
  10.305 +        appendStr(data,[_properties objectForKey: name]);
  10.306 +    }
  10.307 +    
  10.308 +    NSUInteger length = data.length - sizeof(UInt16);
  10.309 +    if( length > 0xFFFF )
  10.310 +        return nil;
  10.311 +    *(UInt16*)[data mutableBytes] = EndianU16_NtoB((UInt16)length);
  10.312 +    return data;
  10.313 +}
  10.314 +
  10.315 +    
  10.316 +- (void) setValue: (NSString*)value ofProperty: (NSString*)prop
  10.317 +{
  10.318 +    Assert(prop.length>0);
  10.319 +    if( value )
  10.320 +        [_properties setObject: value forKey: prop];
  10.321 +    else
  10.322 +        [_properties removeObjectForKey: prop];
  10.323 +}
  10.324 +
  10.325 +
  10.326 +- (void) setAllProperties: (NSDictionary*)properties
  10.327 +{
  10.328 +    if( properties.count ) {
  10.329 +        for( id key in properties ) {
  10.330 +            Assert([key isKindOfClass: [NSString class]]);
  10.331 +            Assert([key length] > 0);
  10.332 +            Assert([[properties objectForKey: key] isKindOfClass: [NSString class]]);
  10.333 +        }
  10.334 +        [_properties setDictionary: properties];
  10.335 +    } else
  10.336 +        [_properties removeAllObjects];
  10.337 +}
  10.338 +
  10.339 +
  10.340 +@end
  10.341 +
  10.342 +
  10.343 +
  10.344 +
  10.345 +TestCase(BLIPProperties) {
  10.346 +    BLIPProperties *props;
  10.347 +    
  10.348 +    props = [BLIPProperties properties];
  10.349 +    CAssert(props);
  10.350 +    CAssertEq(props.count,0);
  10.351 +    Log(@"Empty properties:\n%@", props.allProperties);
  10.352 +    NSData *data = props.encodedData;
  10.353 +    Log(@"As data: %@", data);
  10.354 +    CAssertEqual(data,[NSMutableData dataWithLength: 2]);
  10.355 +    
  10.356 +    BLIPMutableProperties *mprops = [props mutableCopy];
  10.357 +    Log(@"Mutable copy:\n%@", mprops.allProperties);
  10.358 +    data = mprops.encodedData;
  10.359 +    Log(@"As data: %@", data);
  10.360 +    CAssertEqual(data,[NSMutableData dataWithLength: 2]);
  10.361 +    
  10.362 +    ssize_t used;
  10.363 +    props = [BLIPProperties propertiesWithEncodedData: data usedLength: &used];
  10.364 +    CAssertEq(used,data.length);
  10.365 +    CAssertEqual(props,mprops);
  10.366 +    
  10.367 +    [mprops setValue: @"Jens" ofProperty: @"First-Name"];
  10.368 +    [mprops setValue: @"Alfke" ofProperty: @"Last-Name"];
  10.369 +    [mprops setValue: @"" ofProperty: @"Empty-String"];
  10.370 +    [mprops setValue: @"Z" ofProperty: @"A"];
  10.371 +    Log(@"With properties:\n%@", mprops.allProperties);
  10.372 +    data = mprops.encodedData;
  10.373 +    Log(@"As data: %@", data);
  10.374 +    
  10.375 +    for( unsigned len=0; len<data.length; len++ ) {
  10.376 +        props = [BLIPProperties propertiesWithEncodedData: [data subdataWithRange: NSMakeRange(0,len)]
  10.377 +                                                                usedLength: &used];
  10.378 +        CAssertEq(props,nil);
  10.379 +        CAssertEq(used,0);
  10.380 +    }
  10.381 +    props = [BLIPProperties propertiesWithEncodedData: data usedLength: &used];
  10.382 +    CAssertEq(used,data.length);
  10.383 +    Log(@"Read back in:\n%@",props.allProperties);
  10.384 +    CAssertEqual(props,mprops);
  10.385 +    
  10.386 +    NSDictionary *all = mprops.allProperties;
  10.387 +    for( NSString *prop in all )
  10.388 +        CAssertEqual([props valueOfProperty: prop],[all objectForKey: prop]);
  10.389 +}
  10.390 +
  10.391 +
  10.392 +/*
  10.393 + Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
  10.394 + 
  10.395 + Redistribution and use in source and binary forms, with or without modification, are permitted
  10.396 + provided that the following conditions are met:
  10.397 + 
  10.398 + * Redistributions of source code must retain the above copyright notice, this list of conditions
  10.399 + and the following disclaimer.
  10.400 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
  10.401 + and the following disclaimer in the documentation and/or other materials provided with the
  10.402 + distribution.
  10.403 + 
  10.404 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  10.405 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
  10.406 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
  10.407 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  10.408 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  10.409 +  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  10.410 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
  10.411 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  10.412 + */
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/BLIP/BLIPReader.h	Fri May 23 17:37:36 2008 -0700
    11.3 @@ -0,0 +1,27 @@
    11.4 +//
    11.5 +//  BLIPReader.h
    11.6 +//  MYNetwork
    11.7 +//
    11.8 +//  Created by Jens Alfke on 5/10/08.
    11.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   11.10 +//
   11.11 +
   11.12 +#import "TCPStream.h"
   11.13 +#import "BLIP_Internal.h"
   11.14 +@class BLIPResponse;
   11.15 +
   11.16 +
   11.17 +/** INTERNAL class that reads BLIP frames from the socket. */
   11.18 +@interface BLIPReader : TCPReader
   11.19 +{
   11.20 +    BLIPFrameHeader _curHeader;
   11.21 +    UInt32 _curBytesRead;
   11.22 +    NSMutableData *_curBody;
   11.23 +
   11.24 +    UInt32 _numQueriesReceived;
   11.25 +    NSMutableDictionary *_pendingQueries, *_pendingReplies;
   11.26 +}
   11.27 +
   11.28 +- (void) _addPendingResponse: (BLIPResponse*)response;
   11.29 +
   11.30 +@end
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/BLIP/BLIPReader.m	Fri May 23 17:37:36 2008 -0700
    12.3 @@ -0,0 +1,263 @@
    12.4 +//
    12.5 +//  BLIPReader.m
    12.6 +//  MYNetwork
    12.7 +//
    12.8 +//  Created by Jens Alfke on 5/10/08.
    12.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   12.10 +//
   12.11 +
   12.12 +#import "BLIPReader.h"
   12.13 +#import "BLIP_Internal.h"
   12.14 +#import "BLIPWriter.h"
   12.15 +#import "BLIPDispatcher.h"
   12.16 +
   12.17 +
   12.18 +@interface BLIPReader ()
   12.19 +- (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body;
   12.20 +@end
   12.21 +
   12.22 +
   12.23 +@implementation BLIPReader
   12.24 +
   12.25 +
   12.26 +#define _blipConn ((BLIPConnection*)_conn)
   12.27 +
   12.28 +
   12.29 +- (id) initWithConnection: (BLIPConnection*)conn stream: (NSStream*)stream
   12.30 +{
   12.31 +    self = [super initWithConnection: conn stream: stream];
   12.32 +    if (self != nil) {
   12.33 +        _pendingQueries = [[NSMutableDictionary alloc] init];
   12.34 +        _pendingReplies = [[NSMutableDictionary alloc] init];
   12.35 +    }
   12.36 +    return self;
   12.37 +}
   12.38 +
   12.39 +- (void) dealloc
   12.40 +{
   12.41 +    [_pendingQueries release];
   12.42 +    [_pendingReplies release];
   12.43 +    [_curBody release];
   12.44 +    [super dealloc];
   12.45 +}
   12.46 +
   12.47 +- (void) disconnect
   12.48 +{
   12.49 +    for( BLIPResponse *response in [_pendingReplies allValues] ) {
   12.50 +        [response _connectionClosed];
   12.51 +        [_conn tellDelegate: @selector(connection:receivedResponse:) withObject: response];
   12.52 +    }
   12.53 +    setObj(&_pendingReplies,nil);
   12.54 +    [super disconnect];
   12.55 +}
   12.56 +
   12.57 +
   12.58 +#pragma mark -
   12.59 +#pragma mark READING FRAMES:
   12.60 +
   12.61 +
   12.62 +- (NSString*) _validateHeader
   12.63 +{
   12.64 +    // Convert header to native byte order:
   12.65 +    _curHeader.magic = EndianU32_BtoN(_curHeader.magic);
   12.66 +    _curHeader.number= EndianU32_BtoN(_curHeader.number);
   12.67 +    _curHeader.flags = EndianU16_BtoN(_curHeader.flags);
   12.68 +    _curHeader.size  = EndianU16_BtoN(_curHeader.size);
   12.69 +    
   12.70 +    if( _curHeader.magic != kBLIPFrameHeaderMagicNumber )
   12.71 +        return $sprintf(@"Incorrect magic number (%08X not %08X)",
   12.72 +                        _curHeader.magic,kBLIPFrameHeaderMagicNumber);
   12.73 +    size_t bodyLength = _curHeader.size;
   12.74 +    if( bodyLength < sizeof(BLIPFrameHeader) )
   12.75 +        return @"Length is impossibly short";
   12.76 +    bodyLength -= sizeof(BLIPFrameHeader);
   12.77 +    _curBody = [[NSMutableData alloc] initWithLength: bodyLength];
   12.78 +    return nil;
   12.79 +}
   12.80 +    
   12.81 +
   12.82 +- (void) _endCurFrame
   12.83 +{
   12.84 +    [self retain];
   12.85 +    [self _receivedFrameWithHeader: &_curHeader body: _curBody];
   12.86 +    memset(&_curHeader,0,sizeof(_curHeader));
   12.87 +    setObj(&_curBody,nil);
   12.88 +    _curBytesRead = 0;
   12.89 +    [self release];
   12.90 +}
   12.91 +
   12.92 +
   12.93 +- (BOOL) isBusy
   12.94 +{
   12.95 +    return _curBytesRead > 0;
   12.96 +}
   12.97 +
   12.98 +
   12.99 +- (void) _canRead
  12.100 +{
  12.101 +    SInt32 headerLeft = sizeof(BLIPFrameHeader) - _curBytesRead;
  12.102 +    if( headerLeft > 0 ) {
  12.103 +        // Read (more of) the header:
  12.104 +        NSInteger bytesRead = [(NSInputStream*)_stream read: (uint8_t*)&_curHeader +_curBytesRead
  12.105 +                                                  maxLength: headerLeft];
  12.106 +        if( bytesRead < 0 ) {
  12.107 +            [self _gotError];
  12.108 +        } else {
  12.109 +            _curBytesRead += bytesRead;
  12.110 +            if( _curBytesRead < sizeof(BLIPFrameHeader) ) {
  12.111 +                // Incomplete header:
  12.112 +                LogTo(BLIPVerbose,@"%@ read %u bytes of header (%u left)",
  12.113 +                      self,bytesRead,sizeof(BLIPFrameHeader)-_curBytesRead);
  12.114 +            } else {
  12.115 +                // Finished reading the header!
  12.116 +                headerLeft = 0;
  12.117 +                NSString *err = [self _validateHeader];
  12.118 +                if( err ) {
  12.119 +                    Warn(@"%@ read bogus frame header: %@",self,err);
  12.120 +                    return (void)[self _gotError: BLIPMakeError(kBLIPError_BadData, @"%@", err)];
  12.121 +                }
  12.122 +                LogTo(BLIPVerbose,@"%@: Read header; next is %u-byte body",self,_curBody.length);
  12.123 +                
  12.124 +                if( _curBody.length == 0 ) {
  12.125 +                    // Zero-byte body, so no need to wait for another read
  12.126 +                    [self _endCurFrame];
  12.127 +                }
  12.128 +            }
  12.129 +        }
  12.130 +        
  12.131 +    } else {
  12.132 +        // Read (more of) the current frame's body:
  12.133 +        SInt32 bodyRemaining = (SInt32)_curBody.length + headerLeft;
  12.134 +        if( bodyRemaining > 0 ) {
  12.135 +            uint8_t *dst = _curBody.mutableBytes;
  12.136 +            dst += _curBody.length - bodyRemaining;
  12.137 +            NSInteger bytesRead = [(NSInputStream*)_stream read: dst maxLength: bodyRemaining];
  12.138 +            if( bytesRead < 0 )
  12.139 +                return (void)[self _gotError];
  12.140 +            else if( bytesRead > 0 ) {
  12.141 +                _curBytesRead += bytesRead;
  12.142 +                bodyRemaining -= bytesRead;
  12.143 +                LogTo(BLIPVerbose,@"%@: Read %u bytes of frame body (%u left)",self,bytesRead,bodyRemaining);
  12.144 +            }
  12.145 +        }
  12.146 +        if( bodyRemaining==0 ) {
  12.147 +            // Done reading this frame: give it to the Connection and reset my state
  12.148 +            [self _endCurFrame];
  12.149 +        }
  12.150 +    }
  12.151 +}
  12.152 +
  12.153 +
  12.154 +#pragma mark -
  12.155 +#pragma mark PROCESSING FRAMES:
  12.156 +
  12.157 +
  12.158 +- (void) _addPendingResponse: (BLIPResponse*)response
  12.159 +{
  12.160 +    [_pendingReplies setObject: response forKey: $object(response.number)];
  12.161 +}
  12.162 +
  12.163 +
  12.164 +- (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body
  12.165 +{
  12.166 +    static const char* kTypeStrs[16] = {"MSG","RPY","ERR","3??","4??","5??","6??","7??"};
  12.167 +    BLIPMessageType type = header->flags & kBLIP_TypeMask;
  12.168 +    LogTo(BLIPVerbose,@"%@ rcvd frame of %s #%u, length %u",self,kTypeStrs[type],header->number,body.length);
  12.169 +
  12.170 +    id key = $object(header->number);
  12.171 +    BOOL complete = ! (header->flags & kBLIP_MoreComing);
  12.172 +    switch(type) {
  12.173 +        case kBLIP_MSG: {
  12.174 +            // Incoming request:
  12.175 +            BLIPRequest *request = [_pendingQueries objectForKey: key];
  12.176 +            if( request ) {
  12.177 +                // Continuation frame of a request:
  12.178 +                if( complete ) {
  12.179 +                    [[request retain] autorelease];
  12.180 +                    [_pendingQueries removeObjectForKey: key];
  12.181 +                }
  12.182 +            } else if( header->number == _numQueriesReceived+1 ) {
  12.183 +                // Next new request:
  12.184 +                request = [[[BLIPRequest alloc] _initWithConnection: _blipConn
  12.185 +                                                         isMine: NO
  12.186 +                                                          flags: header->flags | kBLIP_MoreComing
  12.187 +                                                         number: header->number
  12.188 +                                                           body: nil]
  12.189 +                                autorelease];
  12.190 +                if( ! complete )
  12.191 +                    [_pendingQueries setObject: request forKey: key];
  12.192 +                _numQueriesReceived++;
  12.193 +            } else
  12.194 +                return [self _gotError: BLIPMakeError(kBLIPError_BadFrame, 
  12.195 +                                               @"Received bad request frame #%u (next is #%u)",
  12.196 +                                               header->number,_numQueriesReceived+1)];
  12.197 +            
  12.198 +            if( ! [request _receivedFrameWithHeader: header body: body] )
  12.199 +                return [self _gotError: BLIPMakeError(kBLIPError_BadFrame, 
  12.200 +                                               @"Couldn't parse message frame")];
  12.201 +            
  12.202 +            if( complete )
  12.203 +                [_blipConn _dispatchRequest: request];
  12.204 +            break;
  12.205 +        }
  12.206 +            
  12.207 +        case kBLIP_RPY:
  12.208 +        case kBLIP_ERR: {
  12.209 +            BLIPResponse *response = [_pendingReplies objectForKey: key];
  12.210 +            if( response ) {
  12.211 +                if( complete ) {
  12.212 +                    [[response retain] autorelease];
  12.213 +                    [_pendingReplies removeObjectForKey: key];
  12.214 +                }
  12.215 +                
  12.216 +                if( ! [response _receivedFrameWithHeader: header body: body] ) {
  12.217 +                    return [self _gotError: BLIPMakeError(kBLIPError_BadFrame, 
  12.218 +                                                          @"Couldn't parse response frame")];
  12.219 +                } else if( complete ) 
  12.220 +                    [_blipConn _dispatchResponse: response];
  12.221 +                
  12.222 +            } else {
  12.223 +                if( header->number <= ((BLIPWriter*)self.writer).numQueriesSent )
  12.224 +                    LogTo(BLIP,@"??? %@ got unexpected response frame to my msg #%u",
  12.225 +                          self,header->number); //benign
  12.226 +                else
  12.227 +                    return [self _gotError: BLIPMakeError(kBLIPError_BadFrame, 
  12.228 +                                                          @"Bogus message number %u in response",
  12.229 +                                                          header->number)];
  12.230 +            }
  12.231 +            break;
  12.232 +        }
  12.233 +            
  12.234 +        default:
  12.235 +            // To leave room for future expansion, undefined message types are just ignored.
  12.236 +            Log(@"??? %@ received header with unknown message type %i", self,type);
  12.237 +            break;
  12.238 +    }
  12.239 +    return YES;
  12.240 +}
  12.241 +
  12.242 +
  12.243 +@end
  12.244 +
  12.245 +
  12.246 +/*
  12.247 + Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
  12.248 + 
  12.249 + Redistribution and use in source and binary forms, with or without modification, are permitted
  12.250 + provided that the following conditions are met:
  12.251 + 
  12.252 + * Redistributions of source code must retain the above copyright notice, this list of conditions
  12.253 + and the following disclaimer.
  12.254 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
  12.255 + and the following disclaimer in the documentation and/or other materials provided with the
  12.256 + distribution.
  12.257 + 
  12.258 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  12.259 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
  12.260 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
  12.261 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  12.262 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  12.263 +  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  12.264 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
  12.265 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  12.266 + */
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/BLIP/BLIPRequest.h	Fri May 23 17:37:36 2008 -0700
    13.3 @@ -0,0 +1,101 @@
    13.4 +//
    13.5 +//  BLIPRequest.h
    13.6 +//  MYNetwork
    13.7 +//
    13.8 +//  Created by Jens Alfke on 5/22/08.
    13.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   13.10 +//
   13.11 +
   13.12 +#import "BLIPMessage.h"
   13.13 +@class BLIPResponse, MYTarget;
   13.14 +
   13.15 +
   13.16 +/** A Request, or initiating message. */
   13.17 +@interface BLIPRequest : BLIPMessage
   13.18 +{
   13.19 +    @private
   13.20 +    BLIPResponse *_response;
   13.21 +}
   13.22 +
   13.23 +/** Creates an outgoing request.
   13.24 +    The body may be nil.
   13.25 +    The request is not associated with any BLIPConnection yet, so you must either set its
   13.26 +    connection property before calling -send, or pass the request as a parameter to
   13.27 +    -[BLIPConnection sendRequest:]. */
   13.28 ++ (BLIPRequest*) requestWithBody: (NSData*)body;
   13.29 +
   13.30 +/** Creates an outgoing request.
   13.31 +    The body or properties may be nil.
   13.32 +    The request is not associated with any BLIPConnection yet, so you must either set its
   13.33 +    connection property before calling -send, or pass the request as a parameter to
   13.34 +    -[BLIPConnection sendRequest:]. */
   13.35 ++ (BLIPRequest*) requestWithBody: (NSData*)body
   13.36 +                      properties: (NSDictionary*)properties;
   13.37 +
   13.38 +/** BLIPRequest extends the -connection property to be settable.
   13.39 +    This allows a request to be created without a connection (i.e. before the connection is created).
   13.40 +    It can later be sent by setting the connection property and calling -send. */
   13.41 +@property (retain) BLIPConnection *connection;
   13.42 +
   13.43 +/** Sends this request over its connection.
   13.44 +    (Actually, the connection queues it to be sent; this method always returns immediately.)
   13.45 +    If this request has not been assigned to a connection, an exception will be raised.
   13.46 +    Its matching response object will be returned, or nil if the request couldn't be sent. */
   13.47 +- (BLIPResponse*) send;
   13.48 +
   13.49 +/** Does this request not need a response?
   13.50 +    This property can only be set before sending the request. */
   13.51 +@property BOOL noReply;
   13.52 +
   13.53 +/** Returns YES if you've replied to this request (by accessing its -response property.) */
   13.54 +@property (readonly) BOOL repliedTo;
   13.55 +
   13.56 +/** The request's response. This can be accessed at any time, even before sending the request,
   13.57 +    but the contents of the response won't be filled in until it arrives, of course. */
   13.58 +@property (readonly) BLIPResponse *response;
   13.59 +
   13.60 +/** Call this when a request arrives, to indicate that you want to respond to it later.
   13.61 +    It will prevent a default empty response from being sent upon return from the request handler. */
   13.62 +- (void) deferResponse;
   13.63 +
   13.64 +/** Shortcut to respond to this request with the given data. */
   13.65 +- (void) respondWithData: (NSData*)data;
   13.66 +
   13.67 +/** Shortcut to respond to this request with the given string (which will be encoded in UTF-8). */
   13.68 +- (void) respondWithString: (NSString*)string;
   13.69 +
   13.70 +/** Shortcut to respond to this request with an error. */
   13.71 +- (void) respondWithError: (NSError*)error;
   13.72 +
   13.73 +/** Shortcut to respond to this request with the given error code and message.
   13.74 +    The BLIPErrorDomain is assumed. */
   13.75 +- (void) respondWithErrorCode: (int)code message: (NSString*)message; //, ... __attribute__ ((format (__NSString__, 2,3)));;
   13.76 +
   13.77 +/** Shortcut to respond to this message with an error indicating that an exception occurred. */
   13.78 +- (void) respondWithException: (NSException*)exception;
   13.79 +
   13.80 +@end
   13.81 +
   13.82 +
   13.83 +
   13.84 +
   13.85 +/** A reply to a BLIPRequest. */
   13.86 +@interface BLIPResponse : BLIPMessage
   13.87 +{
   13.88 +    @private
   13.89 +    NSError *_error;
   13.90 +    MYTarget *_onComplete;
   13.91 +}
   13.92 +
   13.93 +/** Sends this response. */
   13.94 +- (BOOL) send;
   13.95 +
   13.96 +/** The error returned by the peer, or nil if the response is successful. */
   13.97 +@property (retain) NSError* error;
   13.98 +
   13.99 +/** Sets a target/action to be called when an incoming response is complete.
  13.100 +    Use this on the response returned from -[BLIPRequest send], to be notified when the response is available. */
  13.101 +@property (retain) MYTarget *onComplete;
  13.102 +
  13.103 +
  13.104 +@end
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/BLIP/BLIPRequest.m	Fri May 23 17:37:36 2008 -0700
    14.3 @@ -0,0 +1,268 @@
    14.4 +//
    14.5 +//  BLIPRequest.m
    14.6 +//  MYNetwork
    14.7 +//
    14.8 +//  Created by Jens Alfke on 5/22/08.
    14.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   14.10 +//
   14.11 +
   14.12 +#import "BLIPRequest.h"
   14.13 +#import "BLIP_Internal.h"
   14.14 +#import "BLIPWriter.h"
   14.15 +#import "BLIPReader.h"
   14.16 +#import "Target.h"
   14.17 +#import "ExceptionUtils.h"
   14.18 +
   14.19 +
   14.20 +@implementation BLIPRequest
   14.21 +
   14.22 +
   14.23 +- (id) _initWithConnection: (BLIPConnection*)connection
   14.24 +                      body: (NSData*)body 
   14.25 +                properties: (NSDictionary*)properties
   14.26 +{
   14.27 +    self = [self _initWithConnection: connection
   14.28 +                              isMine: YES
   14.29 +                               flags: kBLIP_MSG
   14.30 +                              number: 0
   14.31 +                                body: body];
   14.32 +    if( self ) {
   14.33 +        _isMutable = YES;
   14.34 +        if( body )
   14.35 +            self.body = body;
   14.36 +        if( properties )
   14.37 +            [self.mutableProperties setAllProperties: properties];
   14.38 +    }
   14.39 +    return self;
   14.40 +}
   14.41 +
   14.42 ++ (BLIPRequest*) requestWithBody: (NSData*)body
   14.43 +{
   14.44 +    return [[[self alloc] _initWithConnection: nil body: body properties: nil] autorelease];
   14.45 +}
   14.46 +
   14.47 ++ (BLIPRequest*) requestWithBody: (NSData*)body
   14.48 +                      properties: (NSDictionary*)properties
   14.49 +{
   14.50 +    return [[[self alloc] _initWithConnection: nil body: body properties: properties] autorelease];
   14.51 +}
   14.52 +
   14.53 +
   14.54 +- (void) dealloc
   14.55 +{
   14.56 +    [_response release];
   14.57 +    [super dealloc];
   14.58 +}
   14.59 +
   14.60 +
   14.61 +- (BOOL) noReply                            {return (_flags & kBLIP_NoReply) != 0;}
   14.62 +- (void) setNoReply: (BOOL)noReply          {[self _setFlag: kBLIP_NoReply value: noReply];}
   14.63 +- (BLIPConnection*) connection              {return _connection;}
   14.64 +
   14.65 +- (void) setConnection: (BLIPConnection*)conn
   14.66 +{
   14.67 +    Assert(_isMine && !_sent,@"Connection can only be set before sending");
   14.68 +    setObj(&_connection,conn);
   14.69 +}
   14.70 +
   14.71 +
   14.72 +- (BLIPResponse*) send
   14.73 +{
   14.74 +    Assert(_connection,@"%@ has no connection to send over",self);
   14.75 +    Assert(!_sent,@"%@ was already sent",self);
   14.76 +    [self _encode];
   14.77 +    BLIPResponse *response = self.response;
   14.78 +    if( [(BLIPWriter*)_connection.writer sendRequest: self response: response] )
   14.79 +        self.sent = YES;
   14.80 +    else
   14.81 +        response = nil;
   14.82 +    return response;
   14.83 +}
   14.84 +
   14.85 +
   14.86 +- (BLIPResponse*) response
   14.87 +{
   14.88 +    if( ! _response && ! self.noReply )
   14.89 +        _response = [[BLIPResponse alloc] _initWithRequest: self];
   14.90 +    return _response;
   14.91 +}
   14.92 +
   14.93 +- (void) deferResponse
   14.94 +{
   14.95 +    // This will allocate _response, causing -repliedTo to become YES, so BLIPConnection won't
   14.96 +    // send an automatic empty response after the current request handler returns.
   14.97 +    LogTo(BLIP,@"Deferring response to %@",self);
   14.98 +    [self response];
   14.99 +}
  14.100 +
  14.101 +- (BOOL) repliedTo
  14.102 +{
  14.103 +    return _response != nil;
  14.104 +}
  14.105 +
  14.106 +- (void) respondWithData: (NSData*)data                   {self.response.body = data; [self.response send];}
  14.107 +- (void) respondWithString: (NSString*)string             {[self respondWithData: [string dataUsingEncoding: NSUTF8StringEncoding]];}
  14.108 +- (void) respondWithError: (NSError*)error                {self.response.error = error; [self.response send];}
  14.109 +
  14.110 +- (void) respondWithErrorCode: (int)errorCode message: (NSString*)errorMessage
  14.111 +{
  14.112 +    [self respondWithError: BLIPMakeError(errorCode, @"%@",errorMessage)];
  14.113 +}
  14.114 +
  14.115 +- (void) respondWithException: (NSException*)exception
  14.116 +{
  14.117 +    [self respondWithError: BLIPMakeError(kBLIPError_HandlerFailed, @"%@", exception.reason)];
  14.118 +}
  14.119 +
  14.120 +
  14.121 +@end
  14.122 +
  14.123 +
  14.124 +
  14.125 +
  14.126 +#pragma mark -
  14.127 +@implementation BLIPResponse
  14.128 +
  14.129 +- (id) _initWithRequest: (BLIPRequest*)request
  14.130 +{
  14.131 +    Assert(request);
  14.132 +    self = [super _initWithConnection: request.connection
  14.133 +                               isMine: !request.isMine
  14.134 +                                flags: kBLIP_RPY | kBLIP_MoreComing
  14.135 +                               number: request.number
  14.136 +                                 body: nil];
  14.137 +    if (self != nil) {
  14.138 +        if( _isMine ) {
  14.139 +            _isMutable = YES;
  14.140 +            if( request.urgent )
  14.141 +                _flags |= kBLIP_Urgent;
  14.142 +        } else {
  14.143 +            _flags |= kBLIP_MoreComing;
  14.144 +        }
  14.145 +    }
  14.146 +    return self;
  14.147 +}
  14.148 +
  14.149 +- (void) dealloc
  14.150 +{
  14.151 +    [_error release];
  14.152 +    [_onComplete release];
  14.153 +    [super dealloc];
  14.154 +}
  14.155 +
  14.156 +
  14.157 +- (NSError*) error
  14.158 +{
  14.159 +    if( ! (_flags & kBLIP_ERR) )
  14.160 +        return nil;
  14.161 +    
  14.162 +    NSMutableDictionary *userInfo = [[self.properties allProperties] mutableCopy];
  14.163 +    NSString *domain = [userInfo objectForKey: @"Error-Domain"];
  14.164 +    int code = [[userInfo objectForKey: @"Error-Code"] intValue];
  14.165 +    if( domain==nil || code==0 ) {
  14.166 +        domain = BLIPErrorDomain;
  14.167 +        if( code==0 )
  14.168 +            code = kBLIPError_Unspecified;
  14.169 +    }
  14.170 +    [userInfo removeObjectForKey: @"Error-Domain"];
  14.171 +    [userInfo removeObjectForKey: @"Error-Code"];
  14.172 +    return [NSError errorWithDomain: domain code: code userInfo: userInfo];
  14.173 +}
  14.174 +
  14.175 +- (void) _setError: (NSError*)error
  14.176 +{
  14.177 +    _flags &= ~kBLIP_TypeMask;
  14.178 +    if( error ) {
  14.179 +        // Setting this stuff is a PITA because this object might be technically immutable,
  14.180 +        // in which case the standard setters would barf if I called them.
  14.181 +        _flags |= kBLIP_ERR;
  14.182 +        setObj(&_body,nil);
  14.183 +        setObj(&_mutableBody,nil);
  14.184 +        
  14.185 +        BLIPMutableProperties *errorProps = [self.properties mutableCopy];
  14.186 +        NSDictionary *userInfo = error.userInfo;
  14.187 +        for( NSString *key in userInfo ) {
  14.188 +            id value = $castIf(NSString,[userInfo objectForKey: key]);
  14.189 +            if( value )
  14.190 +                [errorProps setValue: value ofProperty: key];
  14.191 +        }
  14.192 +        [errorProps setValue: error.domain ofProperty: @"Error-Domain"];
  14.193 +        [errorProps setValue: $sprintf(@"%i",error.code) ofProperty: @"Error-Code"];
  14.194 +        setObj(&_properties,errorProps);
  14.195 +        [errorProps release];
  14.196 +        
  14.197 +    } else {
  14.198 +        _flags |= kBLIP_RPY;
  14.199 +        [self.mutableProperties setAllProperties: nil];
  14.200 +    }
  14.201 +}
  14.202 +
  14.203 +- (void) setError: (NSError*)error
  14.204 +{
  14.205 +    Assert(_isMine && _isMutable);
  14.206 +    [self _setError: error];
  14.207 +}
  14.208 +
  14.209 +
  14.210 +- (BOOL) send
  14.211 +{
  14.212 +    Assert(_connection,@"%@ has no connection to send over",self);
  14.213 +    Assert(!_sent,@"%@ was already sent",self);
  14.214 +    [self _encode];
  14.215 +    return (self.sent = [(BLIPWriter*)_connection.writer sendMessage: self]);
  14.216 +}
  14.217 +
  14.218 +
  14.219 +@synthesize onComplete=_onComplete;
  14.220 +
  14.221 +
  14.222 +- (void) setComplete: (BOOL)complete
  14.223 +{
  14.224 +    [super setComplete: complete];
  14.225 +    if( complete && _onComplete ) {
  14.226 +        @try{
  14.227 +            [_onComplete invokeWithSender: self];
  14.228 +        }catchAndReport(@"BLIPResponse onComplete target");
  14.229 +    }
  14.230 +}
  14.231 +
  14.232 +
  14.233 +- (void) _connectionClosed
  14.234 +{
  14.235 +    [super _connectionClosed];
  14.236 +    if( !_isMine ) {
  14.237 +        // Change incoming response to an error:
  14.238 +        _isMutable = YES;
  14.239 +        [_properties autorelease];
  14.240 +        _properties = [_properties mutableCopy];
  14.241 +        [self _setError: BLIPMakeError(kBLIPError_Disconnected,
  14.242 +                                         @"Connection closed before response was received")];
  14.243 +        _isMutable = NO;
  14.244 +    }
  14.245 +}
  14.246 +
  14.247 +
  14.248 +@end
  14.249 +
  14.250 +
  14.251 +/*
  14.252 + Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
  14.253 + 
  14.254 + Redistribution and use in source and binary forms, with or without modification, are permitted
  14.255 + provided that the following conditions are met:
  14.256 + 
  14.257 + * Redistributions of source code must retain the above copyright notice, this list of conditions
  14.258 + and the following disclaimer.
  14.259 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
  14.260 + and the following disclaimer in the documentation and/or other materials provided with the
  14.261 + distribution.
  14.262 + 
  14.263 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  14.264 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
  14.265 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
  14.266 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  14.267 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  14.268 + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  14.269 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
  14.270 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14.271 + */
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/BLIP/BLIPTest.m	Fri May 23 17:37:36 2008 -0700
    15.3 @@ -0,0 +1,350 @@
    15.4 +//
    15.5 +//  BLIPTest.m
    15.6 +//  MYNetwork
    15.7 +//
    15.8 +//  Created by Jens Alfke on 5/13/08.
    15.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   15.10 +//
   15.11 +
   15.12 +#ifndef NDEBUG
   15.13 +
   15.14 +
   15.15 +#import "BLIPRequest.h"
   15.16 +#import "BLIPProperties.h"
   15.17 +#import "BLIPConnection.h"
   15.18 +#import "IPAddress.h"
   15.19 +#import "Target.h"
   15.20 +
   15.21 +#define HAVE_KEYCHAIN_FRAMEWORK 0
   15.22 +#if HAVE_KEYCHAIN_FRAMEWORK
   15.23 +#import <Keychain/Keychain.h>
   15.24 +#endif
   15.25 +
   15.26 +#define kListenerPort               46353
   15.27 +#define kSendInterval               0.5
   15.28 +#define kNBatchedMessages           20
   15.29 +#define kUseCompression             YES
   15.30 +#define kUrgentEvery                4
   15.31 +#define kClientRequiresSSL          NO
   15.32 +#define kClientUsesSSLCert          NO
   15.33 +#define kListenerRequiresSSL        NO
   15.34 +#define kListenerRequiresClientCert NO
   15.35 +
   15.36 +
   15.37 +static SecIdentityRef GetClientIdentity(void) {
   15.38 +    return NULL;    // Make this return a valid identity to test client-side certs
   15.39 +}
   15.40 +
   15.41 +static SecIdentityRef GetListenerIdentity(void) {
   15.42 +    return NULL;    // Make this return a valid identity to test client-side certs
   15.43 +}
   15.44 +
   15.45 +
   15.46 +#pragma mark -
   15.47 +#pragma mark CLIENT TEST:
   15.48 +
   15.49 +
   15.50 +@interface BLIPConnectionTester : NSObject <BLIPConnectionDelegate>
   15.51 +{
   15.52 +    BLIPConnection *_conn;
   15.53 +    NSMutableDictionary *_pending;
   15.54 +}
   15.55 +
   15.56 +@end
   15.57 +
   15.58 +
   15.59 +@implementation BLIPConnectionTester
   15.60 +
   15.61 +- (id) init
   15.62 +{
   15.63 +    self = [super init];
   15.64 +    if (self != nil) {
   15.65 +        Log(@"** INIT %@",self);
   15.66 +        _pending = [[NSMutableDictionary alloc] init];
   15.67 +        IPAddress *addr = [[IPAddress alloc] initWithHostname: @"localhost" port: kListenerPort];
   15.68 +        _conn = [[BLIPConnection alloc] initToAddress: addr];
   15.69 +        if( ! _conn ) {
   15.70 +            [self release];
   15.71 +            return nil;
   15.72 +        }
   15.73 +        if( kClientRequiresSSL ) {
   15.74 +            _conn.SSLProperties = $mdict({kTCPPropertySSLAllowsAnyRoot, $true});
   15.75 +            if( kClientUsesSSLCert ) {
   15.76 +                SecIdentityRef clientIdentity = GetClientIdentity();
   15.77 +                if( clientIdentity ) {
   15.78 +                    [_conn setSSLProperty: $array((id)clientIdentity)
   15.79 +                                   forKey: kTCPPropertySSLCertificates];
   15.80 +                }
   15.81 +            }
   15.82 +        }
   15.83 +        _conn.delegate = self;
   15.84 +        Log(@"** Opening connection...");
   15.85 +        [_conn open];
   15.86 +    }
   15.87 +    return self;
   15.88 +}
   15.89 +
   15.90 +- (void) dealloc
   15.91 +{
   15.92 +    Log(@"** %@ closing",self);
   15.93 +    [_conn close];
   15.94 +    [_conn release];
   15.95 +    [super dealloc];
   15.96 +}
   15.97 +
   15.98 +- (void) sendAMessage
   15.99 +{
  15.100 +    Log(@"** Sending another %i messages...", kNBatchedMessages);
  15.101 +    for( int i=0; i<kNBatchedMessages; i++ ) {
  15.102 +        size_t size = random() % 32768;
  15.103 +        NSMutableData *body = [NSMutableData dataWithLength: size];
  15.104 +        UInt8 *bytes = body.mutableBytes;
  15.105 +        for( size_t i=0; i<size; i++ )
  15.106 +            bytes[i] = i % 256;
  15.107 +        
  15.108 +        BLIPRequest *q = [_conn requestWithBody: body
  15.109 +                                     properties: $dict({@"Content-Type", @"application/octet-stream"},
  15.110 +                                                       {@"User-Agent", @"BLIPConnectionTester"},
  15.111 +                                                       {@"Date", [[NSDate date] description]},
  15.112 +                                                       {@"Size",$sprintf(@"%u",size)})];
  15.113 +        Assert(q);
  15.114 +        if( kUseCompression && (random()%2==1) )
  15.115 +            q.compressed = YES;
  15.116 +        if( random()%16 > 12 )
  15.117 +            q.urgent = YES;
  15.118 +        BLIPResponse *response = [q send];
  15.119 +        Assert(response);
  15.120 +        Assert(q.number>0);
  15.121 +        Assert(response.number==q.number);
  15.122 +        [_pending setObject: $object(size) forKey: $object(q.number)];
  15.123 +        response.onComplete = $target(self,responseArrived:);
  15.124 +    }
  15.125 +    [self performSelector: @selector(sendAMessage) withObject: nil afterDelay: kSendInterval];
  15.126 +}
  15.127 +
  15.128 +- (void) responseArrived: (BLIPResponse*)response
  15.129 +{
  15.130 +    Log(@"********** called responseArrived: %@",response);
  15.131 +}
  15.132 +
  15.133 +- (void) connectionDidOpen: (TCPConnection*)connection
  15.134 +{
  15.135 +    Log(@"** %@ didOpen",connection);
  15.136 +    [self sendAMessage];
  15.137 +}
  15.138 +- (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
  15.139 +{
  15.140 +#if HAVE_KEYCHAIN_FRAMEWORK
  15.141 +    Certificate *cert = peerCert ?[Certificate certificateWithCertificateRef: peerCert] :nil;
  15.142 +    Log(@"** %@ authorizeSSLPeer: %@",self,cert);
  15.143 +#else
  15.144 +    Log(@"** %@ authorizeSSLPeer: %@",self,peerCert);
  15.145 +#endif
  15.146 +    return peerCert != nil;
  15.147 +}
  15.148 +- (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error
  15.149 +{
  15.150 +    Log(@"** %@ failedToOpen: %@",connection,error);
  15.151 +    CFRunLoopStop(CFRunLoopGetCurrent());
  15.152 +}
  15.153 +- (void) connectionDidClose: (TCPConnection*)connection
  15.154 +{
  15.155 +    Log(@"** %@ didClose",connection);
  15.156 +    setObj(&_conn,nil);
  15.157 +    [NSObject cancelPreviousPerformRequestsWithTarget: self];
  15.158 +    CFRunLoopStop(CFRunLoopGetCurrent());
  15.159 +}
  15.160 +- (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request
  15.161 +{
  15.162 +    Log(@"***** %@ received %@",connection,request);
  15.163 +    [request respondWithData: request.body];
  15.164 +}
  15.165 +
  15.166 +- (void) connection: (BLIPConnection*)connection receivedResponse: (BLIPResponse*)response
  15.167 +{
  15.168 +    Log(@"********** %@ received %@",connection,response);
  15.169 +    NSNumber *sizeObj = [_pending objectForKey: $object(response.number)];
  15.170 +
  15.171 +    if( response.error )
  15.172 +        Warn(@"Got error response: %@",response.error);
  15.173 +    else {
  15.174 +        NSData *body = response.body;
  15.175 +        size_t size = body.length;
  15.176 +        Assert(size<32768);
  15.177 +        const UInt8 *bytes = body.bytes;
  15.178 +        for( size_t i=0; i<size; i++ )
  15.179 +            AssertEq(bytes[i],i % 256);
  15.180 +        AssertEq(size,sizeObj.intValue);
  15.181 +    }
  15.182 +    Assert(sizeObj);
  15.183 +    [_pending removeObjectForKey: $object(response.number)];
  15.184 +    Log(@"Now %u replies pending", _pending.count);
  15.185 +    Assert(_pending.count<100);
  15.186 +}
  15.187 +
  15.188 +
  15.189 +@end
  15.190 +
  15.191 +
  15.192 +TestCase(BLIPConnection) {
  15.193 +#if HAVE_KEYCHAIN_FRAMEWORK
  15.194 +    [Keychain setUserInteractionAllowed: YES];
  15.195 +#endif
  15.196 +    BLIPConnectionTester *tester = [[BLIPConnectionTester alloc] init];
  15.197 +    CAssert(tester);
  15.198 +    
  15.199 +    [[NSRunLoop currentRunLoop] run];
  15.200 +    
  15.201 +    Log(@"** Runloop stopped");
  15.202 +    [tester release];
  15.203 +}
  15.204 +
  15.205 +
  15.206 +
  15.207 +
  15.208 +#pragma mark LISTENER TEST:
  15.209 +
  15.210 +
  15.211 +@interface BLIPTestListener : NSObject <TCPListenerDelegate, BLIPConnectionDelegate>
  15.212 +{
  15.213 +    BLIPListener *_listener;
  15.214 +}
  15.215 +
  15.216 +@end
  15.217 +
  15.218 +
  15.219 +@implementation BLIPTestListener
  15.220 +
  15.221 +- (id) init
  15.222 +{
  15.223 +    self = [super init];
  15.224 +    if (self != nil) {
  15.225 +        _listener = [[BLIPListener alloc] initWithPort: kListenerPort];
  15.226 +        _listener.delegate = self;
  15.227 +        _listener.pickAvailablePort = YES;
  15.228 +        _listener.bonjourServiceType = @"_bliptest._tcp";
  15.229 +        if( kListenerRequiresSSL ) {
  15.230 +            SecIdentityRef listenerIdentity = GetListenerIdentity();
  15.231 +            Assert(listenerIdentity);
  15.232 +            _listener.SSLProperties = $mdict({kTCPPropertySSLCertificates, $array((id)listenerIdentity)},
  15.233 +                                             {kTCPPropertySSLAllowsAnyRoot,$true},
  15.234 +            {kTCPPropertySSLClientSideAuthentication, $object(kTryAuthenticate)});
  15.235 +        }
  15.236 +        Assert( [_listener open] );
  15.237 +        Log(@"%@ is listening...",self);
  15.238 +    }
  15.239 +    return self;
  15.240 +}
  15.241 +
  15.242 +- (void) dealloc
  15.243 +{
  15.244 +    Log(@"%@ closing",self);
  15.245 +    [_listener close];
  15.246 +    [_listener release];
  15.247 +    [super dealloc];
  15.248 +}
  15.249 +
  15.250 +- (void) listener: (TCPListener*)listener didAcceptConnection: (TCPConnection*)connection
  15.251 +{
  15.252 +    Log(@"** %@ accepted %@",self,connection);
  15.253 +    connection.delegate = self;
  15.254 +}
  15.255 +
  15.256 +- (void) listener: (TCPListener*)listener failedToOpen: (NSError*)error
  15.257 +{
  15.258 +    Log(@"** BLIPTestListener failed to open: %@",error);
  15.259 +}
  15.260 +
  15.261 +- (void) listenerDidOpen: (TCPListener*)listener   {Log(@"** BLIPTestListener did open");}
  15.262 +- (void) listenerDidClose: (TCPListener*)listener   {Log(@"** BLIPTestListener did close");}
  15.263 +
  15.264 +- (BOOL) listener: (TCPListener*)listener shouldAcceptConnectionFrom: (IPAddress*)address
  15.265 +{
  15.266 +    Log(@"** %@ shouldAcceptConnectionFrom: %@",self,address);
  15.267 +    return YES;
  15.268 +}
  15.269 +
  15.270 +
  15.271 +- (void) connectionDidOpen: (TCPConnection*)connection
  15.272 +{
  15.273 +    Log(@"** %@ didOpen [SSL=%@]",connection,connection.actualSecurityLevel);
  15.274 +}
  15.275 +- (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
  15.276 +{
  15.277 +#if HAVE_KEYCHAIN_FRAMEWORK
  15.278 +    Certificate *cert = peerCert ?[Certificate certificateWithCertificateRef: peerCert] :nil;
  15.279 +    Log(@"** %@ authorizeSSLPeer: %@",connection,cert);
  15.280 +#else
  15.281 +    Log(@"** %@ authorizeSSLPeer: %@",self,peerCert);
  15.282 +#endif
  15.283 +    return peerCert != nil || ! kListenerRequiresClientCert;
  15.284 +}
  15.285 +- (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error
  15.286 +{
  15.287 +    Log(@"** %@ failedToOpen: %@",connection,error);
  15.288 +}
  15.289 +- (void) connectionDidClose: (TCPConnection*)connection
  15.290 +{
  15.291 +    Log(@"** %@ didClose",connection);
  15.292 +    [connection release];
  15.293 +}
  15.294 +- (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request
  15.295 +{
  15.296 +    Log(@"***** %@ received %@",connection,request);
  15.297 +    NSData *body = request.body;
  15.298 +    size_t size = body.length;
  15.299 +    Assert(size<32768);
  15.300 +    const UInt8 *bytes = body.bytes;
  15.301 +    for( size_t i=0; i<size; i++ )
  15.302 +        AssertEq(bytes[i],i % 256);
  15.303 +    
  15.304 +    AssertEqual([request valueOfProperty: @"Content-Type"], @"application/octet-stream");
  15.305 +    AssertEqual([request valueOfProperty: @"User-Agent"], @"BLIPConnectionTester");
  15.306 +    AssertEq([[request valueOfProperty: @"Size"] intValue], size);
  15.307 +
  15.308 +    [request respondWithData: body];
  15.309 +}
  15.310 +
  15.311 +
  15.312 +@end
  15.313 +
  15.314 +
  15.315 +TestCase(BLIPListener) {
  15.316 +    EnableLogTo(BLIP,YES);
  15.317 +    EnableLogTo(PortMapper,YES);
  15.318 +    EnableLogTo(Bonjour,YES);
  15.319 +#if HAVE_KEYCHAIN_FRAMEWORK
  15.320 +    [Keychain setUserInteractionAllowed: YES];
  15.321 +#endif
  15.322 +    BLIPTestListener *listener = [[BLIPTestListener alloc] init];
  15.323 +    
  15.324 +    [[NSRunLoop currentRunLoop] run];
  15.325 +    
  15.326 +    [listener release];
  15.327 +}
  15.328 +
  15.329 +
  15.330 +#endif
  15.331 +
  15.332 +
  15.333 +/*
  15.334 + Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
  15.335 + 
  15.336 + Redistribution and use in source and binary forms, with or without modification, are permitted
  15.337 + provided that the following conditions are met:
  15.338 + 
  15.339 + * Redistributions of source code must retain the above copyright notice, this list of conditions
  15.340 + and the following disclaimer.
  15.341 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
  15.342 + and the following disclaimer in the documentation and/or other materials provided with the
  15.343 + distribution.
  15.344 + 
  15.345 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  15.346 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
  15.347 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
  15.348 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  15.349 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  15.350 +  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  15.351 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
  15.352 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  15.353 + */
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/BLIP/BLIPWriter.h	Fri May 23 17:37:36 2008 -0700
    16.3 @@ -0,0 +1,24 @@
    16.4 +//
    16.5 +//  BLIPFrameWriter.h
    16.6 +//  MYNetwork
    16.7 +//
    16.8 +//  Created by Jens Alfke on 5/18/08.
    16.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   16.10 +//
   16.11 +
   16.12 +#import "TCPWriter.h"
   16.13 +@class BLIPRequest, BLIPResponse, BLIPMessage;
   16.14 +
   16.15 +
   16.16 +@interface BLIPWriter : TCPWriter
   16.17 +{
   16.18 +    NSMutableArray *_outBox;
   16.19 +    UInt32 _numQueriesSent;
   16.20 +}
   16.21 +
   16.22 +- (BOOL) sendRequest: (BLIPRequest*)request response: (BLIPResponse*)response;
   16.23 +- (BOOL) sendMessage: (BLIPMessage*)message;
   16.24 +
   16.25 +@property (readonly) UInt32 numQueriesSent;
   16.26 +
   16.27 +@end
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/BLIP/BLIPWriter.m	Fri May 23 17:37:36 2008 -0700
    17.3 @@ -0,0 +1,150 @@
    17.4 +//
    17.5 +//  BLIPFrameWriter.m
    17.6 +//  MYNetwork
    17.7 +//
    17.8 +//  Created by Jens Alfke on 5/18/08.
    17.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   17.10 +//
   17.11 +
   17.12 +#import "BLIPReader.h"
   17.13 +#import "BLIPWriter.h"
   17.14 +#import "BLIP_Internal.h"
   17.15 +
   17.16 +
   17.17 +#define kDefaultFrameSize 4096
   17.18 +
   17.19 +
   17.20 +@implementation BLIPWriter
   17.21 +
   17.22 +
   17.23 +- (void) dealloc
   17.24 +{
   17.25 +    [_outBox release];
   17.26 +    [super dealloc];
   17.27 +}
   17.28 +
   17.29 +- (void) disconnect
   17.30 +{
   17.31 +    [_outBox makeObjectsPerformSelector: @selector(_connectionClosed) withObject: nil];
   17.32 +    setObj(&_outBox,nil);
   17.33 +    [super disconnect];
   17.34 +}
   17.35 +
   17.36 +@synthesize numQueriesSent=_numQueriesSent;
   17.37 +
   17.38 +
   17.39 +- (BOOL) isBusy
   17.40 +{
   17.41 +    return _outBox.count>0 || [super isBusy];
   17.42 +}
   17.43 +
   17.44 +
   17.45 +- (void) _queueMessage: (BLIPMessage*)msg isNew: (BOOL)isNew
   17.46 +{
   17.47 +    int n = _outBox.count, index;
   17.48 +    if( msg.urgent && n > 1 ) {
   17.49 +        // High-priority gets queued after the last existing high-priority message,
   17.50 +        // leaving one regular-priority message in between if possible.
   17.51 +        for( index=n-1; index>0; index-- ) {
   17.52 +            BLIPMessage *otherMsg = [_outBox objectAtIndex: index];
   17.53 +            if( [otherMsg urgent] ) {
   17.54 +                index = MIN(index+2, n);
   17.55 +                break;
   17.56 +            } else if( isNew && otherMsg._bytesWritten==0 ) {
   17.57 +                // But have to keep message starts in order
   17.58 +                index = index+1;
   17.59 +                break;
   17.60 +            }
   17.61 +        }
   17.62 +        if( index==0 )
   17.63 +            index = 1;
   17.64 +    } else {
   17.65 +        // Regular priority goes at the end of the queue:
   17.66 +        index = n;
   17.67 +    }
   17.68 +    if( ! _outBox )
   17.69 +        _outBox = [[NSMutableArray alloc] init];
   17.70 +    [_outBox insertObject: msg atIndex: index];
   17.71 +    
   17.72 +    if( isNew ) {
   17.73 +        LogTo(BLIP,@"%@ queuing outgoing %@ at index %i",self,msg,index);
   17.74 +        if( n==0 )
   17.75 +            [self queueIsEmpty];
   17.76 +    }
   17.77 +}
   17.78 +
   17.79 +
   17.80 +- (BOOL) sendMessage: (BLIPMessage*)message
   17.81 +{
   17.82 +    if( _shouldClose ) {
   17.83 +        Warn(@"%@: Attempt to send a message after the connection has started closing",self);
   17.84 +        return NO;
   17.85 +    }
   17.86 +    Assert(!message.sent,@"message has already been sent");
   17.87 +    [self _queueMessage: message isNew: YES];
   17.88 +    return YES;
   17.89 +}
   17.90 +
   17.91 +
   17.92 +- (BOOL) sendRequest: (BLIPRequest*)q response: (BLIPResponse*)response
   17.93 +{
   17.94 +    if( !_shouldClose ) {
   17.95 +        [q _assignedNumber: ++_numQueriesSent];
   17.96 +        if( response ) {
   17.97 +            [response _assignedNumber: _numQueriesSent];
   17.98 +            [(BLIPReader*)self.reader _addPendingResponse: response];
   17.99 +        }
  17.100 +    }
  17.101 +    return [self sendMessage: q];
  17.102 +}
  17.103 +
  17.104 +
  17.105 +- (void) queueIsEmpty
  17.106 +{
  17.107 +    if( _outBox.count > 0 ) {
  17.108 +        // Pop first message in queue:
  17.109 +        BLIPMessage *msg = [[_outBox objectAtIndex: 0] retain];
  17.110 +        [_outBox removeObjectAtIndex: 0];
  17.111 +        
  17.112 +        // As an optimization, allow message to send a big frame unless there's a higher-priority
  17.113 +        // message right behind it:
  17.114 +        size_t frameSize = kDefaultFrameSize;
  17.115 +        if( msg.urgent || _outBox.count==0 || ! [[_outBox objectAtIndex: 0] urgent] )
  17.116 +            frameSize *= 4;
  17.117 +        
  17.118 +        if( [msg _writeFrameTo: self maxSize: frameSize] ) {
  17.119 +            // add it back so it can send its next frame later:
  17.120 +            [self _queueMessage: msg isNew: NO];
  17.121 +        }
  17.122 +        [msg release];
  17.123 +    } else {
  17.124 +        LogTo(BLIPVerbose,@"%@: no more work for writer",self);
  17.125 +    }
  17.126 +}
  17.127 +
  17.128 +
  17.129 +
  17.130 +@end
  17.131 +
  17.132 +
  17.133 +/*
  17.134 + Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
  17.135 + 
  17.136 + Redistribution and use in source and binary forms, with or without modification, are permitted
  17.137 + provided that the following conditions are met:
  17.138 + 
  17.139 + * Redistributions of source code must retain the above copyright notice, this list of conditions
  17.140 + and the following disclaimer.
  17.141 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
  17.142 + and the following disclaimer in the documentation and/or other materials provided with the
  17.143 + distribution.
  17.144 + 
  17.145 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  17.146 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
  17.147 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
  17.148 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  17.149 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  17.150 +  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  17.151 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
  17.152 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  17.153 + */
    18.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2 +++ b/BLIP/BLIP_Internal.h	Fri May 23 17:37:36 2008 -0700
    18.3 @@ -0,0 +1,86 @@
    18.4 +//
    18.5 +//  BLIP_Internal.h
    18.6 +//  MYNetwork
    18.7 +//
    18.8 +//  Created by Jens Alfke on 5/10/08.
    18.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   18.10 +//
   18.11 +
   18.12 +#import "BLIPConnection.h"
   18.13 +#import "BLIPRequest.h"
   18.14 +#import "BLIPProperties.h"
   18.15 +@class BLIPWriter;
   18.16 +
   18.17 +
   18.18 +/* Private declarations and APIs for BLIP implementation. Not for use by clients! */
   18.19 +
   18.20 +
   18.21 +/* BLIP message types; encoded in each frame's header. */
   18.22 +typedef enum {
   18.23 +    kBLIP_MSG,                      // initiating message
   18.24 +    kBLIP_RPY,                      // response to a MSG
   18.25 +    kBLIP_ERR                       // error response to a MSG
   18.26 +} BLIPMessageType;
   18.27 +
   18.28 +/* Flag bits in a BLIP frame header */
   18.29 +enum {
   18.30 +    kBLIP_TypeMask  = 0x000F,       // bits reserved for storing message type
   18.31 +    kBLIP_Compressed= 0x0010,       // data is gzipped
   18.32 +    kBLIP_Urgent    = 0x0020,       // please send sooner/faster
   18.33 +    kBLIP_NoReply   = 0x0040,       // no RPY needed
   18.34 +    kBLIP_MoreComing= 0x0080,       // More frames coming (Applies only to individual frame)
   18.35 +};
   18.36 +typedef UInt16 BLIPMessageFlags;
   18.37 +
   18.38 +
   18.39 +/** Header of a BLIP frame as sent across the wire. All fields are big-endian. */
   18.40 +typedef struct {
   18.41 +    UInt32           magic;         // magic number (kBLIPFrameHeaderMagicNumber)
   18.42 +    UInt32           number;        // serial number of MSG
   18.43 +    BLIPMessageFlags flags;         // encodes frame type, "more" flag, and other delivery options
   18.44 +    UInt16           size;          // total size of frame, _including_ this header
   18.45 +} BLIPFrameHeader;
   18.46 +
   18.47 +#define kBLIPFrameHeaderMagicNumber 0x9B34F205
   18.48 +
   18.49 +
   18.50 +NSError *BLIPMakeError( int errorCode, NSString *message, ... ) __attribute__ ((format (__NSString__, 2, 3)));
   18.51 +
   18.52 +
   18.53 +@interface BLIPConnection ()
   18.54 +- (void) _dispatchRequest: (BLIPRequest*)request;
   18.55 +- (void) _dispatchResponse: (BLIPResponse*)response;
   18.56 +@end
   18.57 +
   18.58 +
   18.59 +@interface BLIPMessage ()
   18.60 +@property BOOL sent, propertiesAvailable, complete;
   18.61 +- (void) _setFlag: (BLIPMessageFlags)flag value: (BOOL)value;
   18.62 +- (void) _encode;
   18.63 +@end
   18.64 +
   18.65 +
   18.66 +@interface BLIPMessage ()
   18.67 +- (id) _initWithConnection: (BLIPConnection*)connection
   18.68 +                    isMine: (BOOL)isMine
   18.69 +                     flags: (BLIPMessageFlags)flags
   18.70 +                    number: (UInt32)msgNo
   18.71 +                      body: (NSData*)body;
   18.72 +- (BOOL) _writeFrameTo: (BLIPWriter*)writer maxSize: (UInt16)maxSize;
   18.73 +@property (readonly) SInt32 _bytesWritten;
   18.74 +- (void) _assignedNumber: (UInt32)number;
   18.75 +- (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body;
   18.76 +- (void) _connectionClosed;
   18.77 +@end
   18.78 +
   18.79 +
   18.80 +@interface BLIPRequest ()
   18.81 +- (id) _initWithConnection: (BLIPConnection*)connection
   18.82 +                      body: (NSData*)body 
   18.83 +                properties: (NSDictionary*)properties;
   18.84 +@end
   18.85 +
   18.86 +
   18.87 +@interface BLIPResponse ()
   18.88 +- (id) _initWithRequest: (BLIPRequest*)request;
   18.89 +@end
    19.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.2 +++ b/BLIP/runBLIPClient	Fri May 23 17:37:36 2008 -0700
    19.3 @@ -0,0 +1,3 @@
    19.4 +#!/bin/csh
    19.5 +
    19.6 +build/Debug/MYNetwork Test_BLIPConnection -Log YES -LogBLIP YES $*
    20.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2 +++ b/BLIP/runBLIPListener	Fri May 23 17:37:36 2008 -0700
    20.3 @@ -0,0 +1,3 @@
    20.4 +#!/bin/csh
    20.5 +
    20.6 +build/Debug/MYNetwork Test_BLIPListener -Log YES -LogBLIP YES $*
    21.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2 +++ b/IPAddress.h	Fri May 23 17:37:36 2008 -0700
    21.3 @@ -0,0 +1,112 @@
    21.4 +//
    21.5 +//  IPAddress.h
    21.6 +//  MYNetwork
    21.7 +//
    21.8 +//  Created by Jens Alfke on 1/4/08.
    21.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   21.10 +//
   21.11 +
   21.12 +#import <Foundation/Foundation.h>
   21.13 +
   21.14 +
   21.15 +/** Represents an Internet Protocol address and port number (similar to a sockaddr_in.)
   21.16 +    IPAddress itself only remembers the raw 32-bit IPv4 address; the subclass HostAddress
   21.17 +    also remembers the DNS host-name. */
   21.18 +@interface IPAddress : NSObject <NSCoding, NSCopying>
   21.19 +{
   21.20 +    UInt32 _ipv4;       // In network byte order (big-endian), just like struct in_addr
   21.21 +    UInt16 _port;       // native byte order
   21.22 +}
   21.23 +
   21.24 +/** Initializes an IPAddress from a host name (which may be a DNS name or dotted-quad numeric form)
   21.25 +    and port number.
   21.26 +    If the hostname is not in dotted-quad form, an instance of the subclass HostAddress will
   21.27 +    be returned instead. */
   21.28 +- (id) initWithHostname: (NSString*)hostname port: (UInt16)port;
   21.29 +
   21.30 +/** Initializes an IPAddress from a raw IPv4 address (in network byte order, i.e. big-endian)
   21.31 +    and port number (in native byte order) */
   21.32 +- (id) initWithIPv4: (UInt32)ipv4 port: (UInt16)port;
   21.33 +
   21.34 +/** Initializes an IPAddress from a raw IPv4 address (in network byte order, i.e. big-endian).
   21.35 +    The port number defaults to zero. */
   21.36 +- (id) initWithIPv4: (UInt32)ipv4;
   21.37 +
   21.38 +/** Initializes an IPAddress from a BSD struct sockaddr. */
   21.39 +- (id) initWithSockAddr: (const struct sockaddr*)sockaddr;
   21.40 +
   21.41 +/** Returns the IP address of this host (with a socket number of zero.)
   21.42 +    If multiple network interfaces are active, the main one's address is returned. */
   21.43 ++ (IPAddress*) localAddress;
   21.44 +
   21.45 +/** Returns the address of the peer that an open socket is connected to.
   21.46 +    (This calls getpeername.) */
   21.47 ++ (IPAddress*) addressOfSocket: (CFSocketNativeHandle)socket;
   21.48 +
   21.49 +/** Returns YES if the two objects have the same IP address, ignoring port numbers. */
   21.50 +- (BOOL) isSameHost: (IPAddress*)addr;
   21.51 +
   21.52 +/** The raw IPv4 address, in network (big-endian) byte order. */
   21.53 +@property (readonly) UInt32 ipv4;               // raw address in network byte order
   21.54 +
   21.55 +/** The address as a dotted-quad string, e.g. @"10.0.1.1". */
   21.56 +@property (readonly) NSString* ipv4name;
   21.57 +
   21.58 +/** The address as a DNS hostname or else a dotted-quad string.
   21.59 +    (IPAddress itself always returns dotted-quad; HostAddress returns the hostname it was
   21.60 +    initialized with.) */
   21.61 +@property (readonly) NSString* hostname;        // dotted-quad string, or DNS name if I am a HostAddress
   21.62 +
   21.63 +/** The port number, or zero if none was specified, in native byte order. */
   21.64 +@property (readonly) UInt16 port;
   21.65 +
   21.66 +/** Is this IP address in a designated private/local address range, such as 10.0.1.X?
   21.67 +    If so, the address is not globally meaningful outside of the local subnet. */
   21.68 +@property (readonly) BOOL isPrivate;            // In a private/local addr range like 10.0.1.X?
   21.69 +@end
   21.70 +
   21.71 +
   21.72 +
   21.73 +/** A subclass of IPAddress that remembers the DNS hostname instead of a raw address.
   21.74 +    An instance of HostAddress looks up its ipv4 address on the fly by calling gethostbyname. */
   21.75 +@interface HostAddress : IPAddress
   21.76 +{
   21.77 +    NSString *_hostname;
   21.78 +}
   21.79 +
   21.80 +- (id) initWithHostname: (NSString*)hostname port: (UInt16)port;
   21.81 +
   21.82 +@end
   21.83 +
   21.84 +
   21.85 +
   21.86 +/** An IPAddress that can keep track of statistics on when it was last sucessfully used
   21.87 +    and the number of successful attempts. This is useful when keeping a cache of recent
   21.88 +    addresses for a peer that doesn't have a stable address. */
   21.89 +@interface RecentAddress : IPAddress
   21.90 +{
   21.91 +    CFAbsoluteTime _lastSuccess;
   21.92 +    UInt32 _successes;
   21.93 +}
   21.94 +
   21.95 +/** Initializes a RecentAddress from an IPAddress. (You can also initialize RecentAddress using
   21.96 +    any inherited initializer method.) */
   21.97 +- (id) initWithIPAddress: (IPAddress*)addr;
   21.98 +
   21.99 +/** The absolute time that -noteSuccess or -noteSeen was last called. */
  21.100 +@property (readonly) CFAbsoluteTime lastSuccess;
  21.101 +
  21.102 +/** The number of times that -noteSuccess has been called. */
  21.103 +@property (readonly) UInt32 successes;
  21.104 +
  21.105 +/** Call this to indicate that the address was successfully used to connect to the desired peer.
  21.106 +    Returns YES if the state of the object has changed and it should be re-archived. */
  21.107 +- (BOOL) noteSuccess;
  21.108 +
  21.109 +/** Call this to indicate that you have received evidence that this address is currently being
  21.110 +    used by this peer. Unlike -noteSuccess it doesn't increment -successes, and only returns
  21.111 +    YES (to indicate a persistent change) once every 18 hours (to avoid making the client
  21.112 +    save its cache too often.) */
  21.113 +- (BOOL) noteSeen;
  21.114 +
  21.115 +@end
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/IPAddress.m	Fri May 23 17:37:36 2008 -0700
    22.3 @@ -0,0 +1,376 @@
    22.4 +//
    22.5 +//  IPAddress.m
    22.6 +//  MYNetwork
    22.7 +//
    22.8 +//  Created by Jens Alfke on 1/4/08.
    22.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   22.10 +//
   22.11 +
   22.12 +#import "IPAddress.h"
   22.13 +#import <sys/types.h>
   22.14 +#import <sys/socket.h>
   22.15 +#import <net/if.h>
   22.16 +#import <netinet/in.h>
   22.17 +#import <ifaddrs.h>
   22.18 +#include <netdb.h>
   22.19 +
   22.20 +
   22.21 +@implementation IPAddress
   22.22 +
   22.23 +
   22.24 ++ (UInt32) IPv4FromDottedQuadString: (NSString*)str
   22.25 +{
   22.26 +    Assert(str);
   22.27 +    UInt32 ipv4 = 0;
   22.28 +    NSScanner *scanner = [NSScanner scannerWithString: str];
   22.29 +    for( int i=0; i<4; i++ ) {
   22.30 +        if( i>0 && ! [scanner scanString: @"." intoString: nil] )
   22.31 +            return 0;
   22.32 +        NSInteger octet;
   22.33 +        if( ! [scanner scanInteger: &octet] || octet<0 || octet>255 )
   22.34 +            return 0;
   22.35 +        ipv4 = (ipv4<<8) | octet;
   22.36 +    }
   22.37 +    if( ! [scanner isAtEnd] )
   22.38 +        return 0;
   22.39 +    return htonl(ipv4);
   22.40 +}
   22.41 +         
   22.42 +         
   22.43 +- (id) initWithHostname: (NSString*)hostname port: (UInt16)port
   22.44 +{
   22.45 +    Assert(hostname);
   22.46 +    self = [super init];
   22.47 +    if (self != nil) {
   22.48 +        _ipv4 = [[self class] IPv4FromDottedQuadString: hostname];
   22.49 +        if( ! _ipv4 ) {
   22.50 +            [self release];
   22.51 +            return [[HostAddress alloc] initWithHostname: hostname port: port];
   22.52 +        }
   22.53 +        _port = port;
   22.54 +    }
   22.55 +    return self;
   22.56 +}
   22.57 +
   22.58 +
   22.59 +- (id) initWithIPv4: (UInt32)ipv4 port: (UInt16)port
   22.60 +{
   22.61 +    self = [super init];
   22.62 +    if (self != nil) {
   22.63 +        _ipv4 = ipv4;
   22.64 +        _port = port;
   22.65 +    }
   22.66 +    return self;
   22.67 +}
   22.68 +
   22.69 +- (id) initWithIPv4: (UInt32)ipv4
   22.70 +{
   22.71 +    return [self initWithIPv4: ipv4 port: 0];
   22.72 +}
   22.73 +
   22.74 +- (id) initWithSockAddr: (const struct sockaddr*)sockaddr
   22.75 +{
   22.76 +    if( sockaddr->sa_family == AF_INET ) {
   22.77 +        const struct sockaddr_in *addr_in = (const struct sockaddr_in*)sockaddr;
   22.78 +        return [self initWithIPv4: addr_in->sin_addr.s_addr port: ntohs(addr_in->sin_port)];
   22.79 +    } else {
   22.80 +        [self release];
   22.81 +        return nil;
   22.82 +    }
   22.83 +}
   22.84 +
   22.85 ++ (IPAddress*) addressOfSocket: (CFSocketNativeHandle)socket
   22.86 +{
   22.87 +    uint8_t name[SOCK_MAXADDRLEN];
   22.88 +    socklen_t namelen = sizeof(name);
   22.89 +    struct sockaddr *addr = (struct sockaddr*)name;
   22.90 +    if (0 == getpeername(socket, addr, &namelen))
   22.91 +        return [[[self alloc] initWithSockAddr: addr] autorelease];
   22.92 +    else
   22.93 +        return nil;
   22.94 +}    
   22.95 +
   22.96 +- (id) copyWithZone: (NSZone*)zone
   22.97 +{
   22.98 +    return [self retain];
   22.99 +}
  22.100 +
  22.101 +- (void)encodeWithCoder:(NSCoder *)coder
  22.102 +{
  22.103 +    if( _ipv4 )
  22.104 +        [coder encodeInt32: _ipv4 forKey: @"ipv4"];
  22.105 +    if( _port )
  22.106 +        [coder encodeInt: _port forKey: @"port"];
  22.107 +}
  22.108 +
  22.109 +- (id)initWithCoder:(NSCoder *)decoder
  22.110 +{
  22.111 +    self = [super init];
  22.112 +    if( self ) {
  22.113 +        _ipv4 = [decoder decodeInt32ForKey: @"ipv4"];
  22.114 +        _port = [decoder decodeIntForKey: @"port"];
  22.115 +    }
  22.116 +    return self;
  22.117 +}
  22.118 +
  22.119 +
  22.120 +@synthesize ipv4=_ipv4, port=_port;
  22.121 +
  22.122 +- (BOOL) isEqual: (IPAddress*)addr
  22.123 +{
  22.124 +    return [addr isKindOfClass: [IPAddress class]] && [self isSameHost: addr] && addr->_port==_port;
  22.125 +}
  22.126 +
  22.127 +- (BOOL) isSameHost: (IPAddress*)addr
  22.128 +{
  22.129 +    return addr && _ipv4==addr->_ipv4;
  22.130 +}
  22.131 +
  22.132 +- (NSUInteger) hash
  22.133 +{
  22.134 +    return _ipv4 ^ _port;
  22.135 +}
  22.136 +
  22.137 +- (NSString*) ipv4name
  22.138 +{
  22.139 +    UInt32 ipv4 = self.ipv4;
  22.140 +    if( ipv4 != 0 ) {
  22.141 +        const UInt8* b = (const UInt8*)&ipv4;
  22.142 +        return [NSString stringWithFormat: @"%u.%u.%u.%u",
  22.143 +                (unsigned)b[0],(unsigned)b[1],(unsigned)b[2],(unsigned)b[3]];
  22.144 +    } else
  22.145 +        return nil;
  22.146 +}
  22.147 +
  22.148 +- (NSString*) hostname
  22.149 +{
  22.150 +    return [self ipv4name];
  22.151 +}
  22.152 +
  22.153 +- (NSString*) description
  22.154 +{
  22.155 +    NSString *name = self.hostname ?: @"0.0.0.0";
  22.156 +    if( _port )
  22.157 +        name = [name stringByAppendingFormat: @":%hu",_port];
  22.158 +    return name;
  22.159 +}
  22.160 +
  22.161 +
  22.162 ++ (IPAddress*) localAddress
  22.163 +{
  22.164 +    // getifaddrs returns a linked list of interface entries;
  22.165 +    // find the first active non-loopback interface with IPv4:
  22.166 +    UInt32 address = 0;
  22.167 +    struct ifaddrs *interfaces;
  22.168 +    if( getifaddrs(&interfaces) == 0 ) {
  22.169 +        struct ifaddrs *interface;
  22.170 +        for( interface=interfaces; interface; interface=interface->ifa_next ) {
  22.171 +            if( (interface->ifa_flags & IFF_UP) && ! (interface->ifa_flags & IFF_LOOPBACK) ) {
  22.172 +                const struct sockaddr_in *addr = (const struct sockaddr_in*) interface->ifa_addr;
  22.173 +                if( addr && addr->sin_family==AF_INET ) {
  22.174 +                    address = addr->sin_addr.s_addr;
  22.175 +                    break;
  22.176 +                }
  22.177 +            }
  22.178 +        }
  22.179 +        freeifaddrs(interfaces);
  22.180 +    }
  22.181 +    return [[[self alloc] initWithIPv4: address] autorelease];
  22.182 +}
  22.183 +
  22.184 +
  22.185 +// Private IP address ranges. See RFC 3330.
  22.186 +static const struct {UInt32 mask, value;} const kPrivateRanges[] = {
  22.187 +    {0xFF000000, 0x00000000},       //   0.x.x.x (hosts on "this" network)
  22.188 +    {0xFF000000, 0x0A000000},       //  10.x.x.x (private address range)
  22.189 +    {0xFF000000, 0x7F000000},       // 127.x.x.x (loopback)
  22.190 +    {0xFFFF0000, 0xA9FE0000},       // 169.254.x.x (link-local self-configured addresses)
  22.191 +    {0xFFF00000, 0xAC100000},       // 172.(16-31).x.x (private address range)
  22.192 +    {0xFFFF0000, 0xC0A80000},       // 192.168.x.x (private address range)
  22.193 +    {0,0}
  22.194 +};
  22.195 +
  22.196 +
  22.197 +- (BOOL) isPrivate
  22.198 +{
  22.199 +    UInt32 address = ntohl(self.ipv4);
  22.200 +    int i;
  22.201 +    for( i=0; kPrivateRanges[i].mask; i++ )
  22.202 +        if( (address & kPrivateRanges[i].mask) == kPrivateRanges[i].value )
  22.203 +            return YES;
  22.204 +    return NO;
  22.205 +}
  22.206 +
  22.207 +
  22.208 +@end
  22.209 +
  22.210 +
  22.211 +
  22.212 +
  22.213 +
  22.214 +@implementation HostAddress
  22.215 +
  22.216 +
  22.217 +- (id) initWithHostname: (NSString*)hostname port: (UInt16)port
  22.218 +{
  22.219 +    self = [super initWithIPv4: 0 port: port];
  22.220 +    if( self ) {
  22.221 +        if( [hostname length]==0 ) {
  22.222 +            [self release];
  22.223 +            return nil;
  22.224 +        }
  22.225 +        _hostname = [hostname copy];
  22.226 +    }
  22.227 +    return self;
  22.228 +}
  22.229 +
  22.230 +
  22.231 +- (void)encodeWithCoder:(NSCoder *)coder
  22.232 +{
  22.233 +    [super encodeWithCoder: coder];
  22.234 +    [coder encodeObject: _hostname forKey: @"host"];
  22.235 +}
  22.236 +
  22.237 +- (id)initWithCoder:(NSCoder *)decoder
  22.238 +{
  22.239 +    self = [super initWithCoder: decoder];
  22.240 +    if( self ) {
  22.241 +        _hostname = [[decoder decodeObjectForKey: @"host"] copy];
  22.242 +    }
  22.243 +    return self;
  22.244 +}
  22.245 +
  22.246 +- (NSUInteger) hash
  22.247 +{
  22.248 +    return [_hostname hash] ^ _port;
  22.249 +}
  22.250 +
  22.251 +
  22.252 +- (NSString*) hostname  {return _hostname;}
  22.253 +
  22.254 +
  22.255 +- (UInt32) ipv4
  22.256 +{
  22.257 +    struct hostent *ent = gethostbyname(_hostname.UTF8String);
  22.258 +    if( ! ent ) {
  22.259 +        Log(@"HostAddress: DNS lookup failed for <%@>: %s", _hostname, hstrerror(h_errno));
  22.260 +        return 0;
  22.261 +    }
  22.262 +    return * (const in_addr_t*) ent->h_addr_list[0];
  22.263 +}
  22.264 +
  22.265 +
  22.266 +- (BOOL) isSameHost: (IPAddress*)addr
  22.267 +{
  22.268 +    return [addr isKindOfClass: [HostAddress class]] && [_hostname caseInsensitiveCompare: addr.hostname]==0;
  22.269 +}
  22.270 +
  22.271 +
  22.272 +@end
  22.273 +
  22.274 +
  22.275 +
  22.276 +
  22.277 +@implementation RecentAddress
  22.278 +
  22.279 +
  22.280 +- (id) initWithIPAddress: (IPAddress*)addr
  22.281 +{
  22.282 +    return [super initWithIPv4: addr.ipv4 port: addr.port];
  22.283 +}
  22.284 +
  22.285 +
  22.286 +@synthesize lastSuccess=_lastSuccess, successes=_successes;
  22.287 +
  22.288 +- (BOOL) noteSuccess
  22.289 +{
  22.290 +    if( _successes < 0xFFFF )
  22.291 +        _successes++;
  22.292 +    _lastSuccess = CFAbsoluteTimeGetCurrent();
  22.293 +    return YES;
  22.294 +}
  22.295 +
  22.296 +- (BOOL) noteSeen
  22.297 +{
  22.298 +    CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
  22.299 +    BOOL significant = ( now-_lastSuccess >= 18*60*60 );
  22.300 +    _lastSuccess = now;
  22.301 +    return significant;
  22.302 +}
  22.303 +
  22.304 +
  22.305 +- (void)encodeWithCoder:(NSCoder *)coder
  22.306 +{
  22.307 +    [super encodeWithCoder: coder];
  22.308 +    [coder encodeDouble: _lastSuccess forKey: @"last"];
  22.309 +    [coder encodeInt: _successes forKey: @"succ"];
  22.310 +}
  22.311 +
  22.312 +- (id)initWithCoder:(NSCoder *)decoder
  22.313 +{
  22.314 +    self = [super initWithCoder: decoder];
  22.315 +    if( self ) {
  22.316 +        _lastSuccess = [decoder decodeDoubleForKey: @"last"];
  22.317 +        _successes = [decoder decodeIntForKey: @"succ"];
  22.318 +    }
  22.319 +    return self;
  22.320 +}
  22.321 +
  22.322 +
  22.323 +@end
  22.324 +
  22.325 +
  22.326 +
  22.327 +
  22.328 +
  22.329 +#import "Test.h"
  22.330 +
  22.331 +TestCase(IPAddress) {
  22.332 +    RequireTestCase(CollectionUtils);
  22.333 +    IPAddress *addr = [[IPAddress alloc] initWithIPv4: htonl(0x0A0001FE) port: 8080];
  22.334 +    CAssertEq(addr.ipv4,htonl(0x0A0001FE));
  22.335 +    CAssertEq(addr.port,8080);
  22.336 +    CAssertEqual(addr.hostname,@"10.0.1.254");
  22.337 +    CAssertEqual(addr.description,@"10.0.1.254:8080");
  22.338 +    CAssert(addr.isPrivate);
  22.339 +    
  22.340 +    addr = [[IPAddress alloc] initWithHostname: @"66.66.0.255" port: 123];
  22.341 +    CAssertEq(addr.class,[IPAddress class]);
  22.342 +    CAssertEq(addr.ipv4,htonl(0x424200FF));
  22.343 +    CAssertEq(addr.port,123);
  22.344 +    CAssertEqual(addr.hostname,@"66.66.0.255");
  22.345 +    CAssertEqual(addr.description,@"66.66.0.255:123");
  22.346 +    CAssert(!addr.isPrivate);
  22.347 +    
  22.348 +    addr = [[IPAddress alloc] initWithHostname: @"www.apple.com" port: 80];
  22.349 +    CAssertEq(addr.class,[HostAddress class]);
  22.350 +    Log(@"www.apple.com = 0x%08X", addr.ipv4);
  22.351 +    CAssertEq(addr.ipv4,htonl(0x1195A00A));
  22.352 +    CAssertEq(addr.port,80);
  22.353 +    CAssertEqual(addr.hostname,@"www.apple.com");
  22.354 +    CAssertEqual(addr.description,@"www.apple.com:80");
  22.355 +    CAssert(!addr.isPrivate);
  22.356 +}
  22.357 +
  22.358 +
  22.359 +/*
  22.360 + Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
  22.361 + 
  22.362 + Redistribution and use in source and binary forms, with or without modification, are permitted
  22.363 + provided that the following conditions are met:
  22.364 + 
  22.365 + * Redistributions of source code must retain the above copyright notice, this list of conditions
  22.366 + and the following disclaimer.
  22.367 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
  22.368 + and the following disclaimer in the documentation and/or other materials provided with the
  22.369 + distribution.
  22.370 + 
  22.371 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  22.372 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
  22.373 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
  22.374 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  22.375 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  22.376 +  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  22.377 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
  22.378 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  22.379 + */
    23.1 Binary file MYNetwork.xcodeproj/TemplateIcon.icns has changed
    24.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.2 +++ b/MYNetwork.xcodeproj/project.pbxproj	Fri May 23 17:37:36 2008 -0700
    24.3 @@ -0,0 +1,379 @@
    24.4 +// !$*UTF8*$!
    24.5 +{
    24.6 +	archiveVersion = 1;
    24.7 +	classes = {
    24.8 +	};
    24.9 +	objectVersion = 45;
   24.10 +	objects = {
   24.11 +
   24.12 +/* Begin PBXBuildFile section */
   24.13 +		270461130DE49030003D9D3F /* BLIPConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 270460F40DE49030003D9D3F /* BLIPConnection.m */; };
   24.14 +		270461140DE49030003D9D3F /* BLIPDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 270460F60DE49030003D9D3F /* BLIPDispatcher.m */; };
   24.15 +		270461150DE49030003D9D3F /* BLIPMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 270460F90DE49030003D9D3F /* BLIPMessage.m */; };
   24.16 +		270461160DE49030003D9D3F /* BLIPProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = 270460FB0DE49030003D9D3F /* BLIPProperties.m */; };
   24.17 +		270461170DE49030003D9D3F /* BLIPReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 270460FD0DE49030003D9D3F /* BLIPReader.m */; };
   24.18 +		270461180DE49030003D9D3F /* BLIPTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 270460FE0DE49030003D9D3F /* BLIPTest.m */; };
   24.19 +		270461190DE49030003D9D3F /* BLIPWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 270461000DE49030003D9D3F /* BLIPWriter.m */; };
   24.20 +		2704611A0DE49030003D9D3F /* IPAddress.m in Sources */ = {isa = PBXBuildFile; fileRef = 270461020DE49030003D9D3F /* IPAddress.m */; };
   24.21 +		2704611B0DE49030003D9D3F /* TCPConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 2704610A0DE49030003D9D3F /* TCPConnection.m */; };
   24.22 +		2704611C0DE49030003D9D3F /* TCPEndpoint.m in Sources */ = {isa = PBXBuildFile; fileRef = 2704610C0DE49030003D9D3F /* TCPEndpoint.m */; };
   24.23 +		2704611D0DE49030003D9D3F /* TCPListener.m in Sources */ = {isa = PBXBuildFile; fileRef = 2704610E0DE49030003D9D3F /* TCPListener.m */; };
   24.24 +		2704611E0DE49030003D9D3F /* TCPStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 270461100DE49030003D9D3F /* TCPStream.m */; };
   24.25 +		2704611F0DE49030003D9D3F /* TCPWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 270461120DE49030003D9D3F /* TCPWriter.m */; };
   24.26 +		2704612C0DE49088003D9D3F /* Test.m in Sources */ = {isa = PBXBuildFile; fileRef = 270461280DE49088003D9D3F /* Test.m */; };
   24.27 +		2704612D0DE49088003D9D3F /* Logging.m in Sources */ = {isa = PBXBuildFile; fileRef = 2704612A0DE49088003D9D3F /* Logging.m */; };
   24.28 +		270461370DE4918D003D9D3F /* ExceptionUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 270461350DE4918D003D9D3F /* ExceptionUtils.m */; };
   24.29 +		270461470DE491A6003D9D3F /* Target.m in Sources */ = {isa = PBXBuildFile; fileRef = 270461460DE491A6003D9D3F /* Target.m */; };
   24.30 +		270461700DE492F3003D9D3F /* GTMNSData+zlib.m in Sources */ = {isa = PBXBuildFile; fileRef = 2704616F0DE492F3003D9D3F /* GTMNSData+zlib.m */; };
   24.31 +		270461890DE49634003D9D3F /* CollectionUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 270461870DE49634003D9D3F /* CollectionUtils.m */; };
   24.32 +		2704618C0DE49652003D9D3F /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2704618B0DE49652003D9D3F /* libz.dylib */; };
   24.33 +		270461920DE4975D003D9D3F /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 270461910DE4975C003D9D3F /* CoreServices.framework */; };
   24.34 +		270462C20DE4A64B003D9D3F /* MYUtilitiesTest_main.m in Sources */ = {isa = PBXBuildFile; fileRef = 270462C10DE4A64B003D9D3F /* MYUtilitiesTest_main.m */; };
   24.35 +		27D5EC070DE5FEDE00CD84FA /* BLIPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D5EC060DE5FEDE00CD84FA /* BLIPRequest.m */; };
   24.36 +		8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; };
   24.37 +/* End PBXBuildFile section */
   24.38 +
   24.39 +/* Begin PBXCopyFilesBuildPhase section */
   24.40 +		8DD76F9E0486AA7600D96B5E /* CopyFiles */ = {
   24.41 +			isa = PBXCopyFilesBuildPhase;
   24.42 +			buildActionMask = 8;
   24.43 +			dstPath = /usr/share/man/man1/;
   24.44 +			dstSubfolderSpec = 0;
   24.45 +			files = (
   24.46 +			);
   24.47 +			runOnlyForDeploymentPostprocessing = 1;
   24.48 +		};
   24.49 +/* End PBXCopyFilesBuildPhase section */
   24.50 +
   24.51 +/* Begin PBXFileReference section */
   24.52 +		08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
   24.53 +		270460F30DE49030003D9D3F /* BLIPConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BLIPConnection.h; sourceTree = "<group>"; };
   24.54 +		270460F40DE49030003D9D3F /* BLIPConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BLIPConnection.m; sourceTree = "<group>"; };
   24.55 +		270460F50DE49030003D9D3F /* BLIPDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BLIPDispatcher.h; sourceTree = "<group>"; };
   24.56 +		270460F60DE49030003D9D3F /* BLIPDispatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BLIPDispatcher.m; sourceTree = "<group>"; };
   24.57 +		270460F70DE49030003D9D3F /* BLIP_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BLIP_Internal.h; sourceTree = "<group>"; };
   24.58 +		270460F80DE49030003D9D3F /* BLIPMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BLIPMessage.h; sourceTree = "<group>"; };
   24.59 +		270460F90DE49030003D9D3F /* BLIPMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BLIPMessage.m; sourceTree = "<group>"; };
   24.60 +		270460FA0DE49030003D9D3F /* BLIPProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BLIPProperties.h; sourceTree = "<group>"; };
   24.61 +		270460FB0DE49030003D9D3F /* BLIPProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BLIPProperties.m; sourceTree = "<group>"; };
   24.62 +		270460FC0DE49030003D9D3F /* BLIPReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BLIPReader.h; sourceTree = "<group>"; };
   24.63 +		270460FD0DE49030003D9D3F /* BLIPReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BLIPReader.m; sourceTree = "<group>"; };
   24.64 +		270460FE0DE49030003D9D3F /* BLIPTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BLIPTest.m; sourceTree = "<group>"; };
   24.65 +		270460FF0DE49030003D9D3F /* BLIPWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BLIPWriter.h; sourceTree = "<group>"; };
   24.66 +		270461000DE49030003D9D3F /* BLIPWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BLIPWriter.m; sourceTree = "<group>"; };
   24.67 +		270461010DE49030003D9D3F /* IPAddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IPAddress.h; sourceTree = "<group>"; };
   24.68 +		270461020DE49030003D9D3F /* IPAddress.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IPAddress.m; sourceTree = "<group>"; };
   24.69 +		270461080DE49030003D9D3F /* TCP_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TCP_Internal.h; sourceTree = "<group>"; };
   24.70 +		270461090DE49030003D9D3F /* TCPConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TCPConnection.h; sourceTree = "<group>"; };
   24.71 +		2704610A0DE49030003D9D3F /* TCPConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TCPConnection.m; sourceTree = "<group>"; };
   24.72 +		2704610B0DE49030003D9D3F /* TCPEndpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TCPEndpoint.h; sourceTree = "<group>"; };
   24.73 +		2704610C0DE49030003D9D3F /* TCPEndpoint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TCPEndpoint.m; sourceTree = "<group>"; };
   24.74 +		2704610D0DE49030003D9D3F /* TCPListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TCPListener.h; sourceTree = "<group>"; };
   24.75 +		2704610E0DE49030003D9D3F /* TCPListener.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TCPListener.m; sourceTree = "<group>"; };
   24.76 +		2704610F0DE49030003D9D3F /* TCPStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TCPStream.h; sourceTree = "<group>"; };
   24.77 +		270461100DE49030003D9D3F /* TCPStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TCPStream.m; sourceTree = "<group>"; };
   24.78 +		270461110DE49030003D9D3F /* TCPWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TCPWriter.h; sourceTree = "<group>"; };
   24.79 +		270461120DE49030003D9D3F /* TCPWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TCPWriter.m; sourceTree = "<group>"; };
   24.80 +		270461280DE49088003D9D3F /* Test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Test.m; sourceTree = "<group>"; };
   24.81 +		270461290DE49088003D9D3F /* Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Test.h; sourceTree = "<group>"; };
   24.82 +		2704612A0DE49088003D9D3F /* Logging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Logging.m; sourceTree = "<group>"; };
   24.83 +		2704612B0DE49088003D9D3F /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = "<group>"; };
   24.84 +		270461350DE4918D003D9D3F /* ExceptionUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExceptionUtils.m; sourceTree = "<group>"; };
   24.85 +		270461360DE4918D003D9D3F /* ExceptionUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExceptionUtils.h; sourceTree = "<group>"; };
   24.86 +		270461450DE491A6003D9D3F /* Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Target.h; sourceTree = "<group>"; };
   24.87 +		270461460DE491A6003D9D3F /* Target.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Target.m; sourceTree = "<group>"; };
   24.88 +		2704616E0DE492F3003D9D3F /* GTMNSData+zlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "GTMNSData+zlib.h"; path = "Foundation/GTMNSData+zlib.h"; sourceTree = "<group>"; };
   24.89 +		2704616F0DE492F3003D9D3F /* GTMNSData+zlib.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "GTMNSData+zlib.m"; path = "Foundation/GTMNSData+zlib.m"; sourceTree = "<group>"; };
   24.90 +		270461720DE49340003D9D3F /* MYNetwork */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = MYNetwork; sourceTree = BUILT_PRODUCTS_DIR; };
   24.91 +		270461870DE49634003D9D3F /* CollectionUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CollectionUtils.m; sourceTree = "<group>"; };
   24.92 +		270461880DE49634003D9D3F /* CollectionUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CollectionUtils.h; sourceTree = "<group>"; };
   24.93 +		2704618B0DE49652003D9D3F /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = /usr/lib/libz.dylib; sourceTree = "<absolute>"; };
   24.94 +		270461910DE4975C003D9D3F /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = /System/Library/Frameworks/CoreServices.framework; sourceTree = "<absolute>"; };
   24.95 +		2704620E0DE4A221003D9D3F /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMDefines.h; sourceTree = "<group>"; };
   24.96 +		270462C00DE4A639003D9D3F /* MYUtilities_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYUtilities_Prefix.pch; sourceTree = "<group>"; };
   24.97 +		270462C10DE4A64B003D9D3F /* MYUtilitiesTest_main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYUtilitiesTest_main.m; sourceTree = "<group>"; };
   24.98 +		270462C30DE4A65B003D9D3F /* BLIP Overview.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "BLIP Overview.txt"; path = "BLIP/BLIP Overview.txt"; sourceTree = "<group>"; wrapsLines = 1; };
   24.99 +		27D5EC050DE5FEDE00CD84FA /* BLIPRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BLIPRequest.h; sourceTree = "<group>"; };
  24.100 +		27D5EC060DE5FEDE00CD84FA /* BLIPRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BLIPRequest.m; sourceTree = "<group>"; };
  24.101 +/* End PBXFileReference section */
  24.102 +
  24.103 +/* Begin PBXFrameworksBuildPhase section */
  24.104 +		8DD76F9B0486AA7600D96B5E /* Frameworks */ = {
  24.105 +			isa = PBXFrameworksBuildPhase;
  24.106 +			buildActionMask = 2147483647;
  24.107 +			files = (
  24.108 +				8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */,
  24.109 +				2704618C0DE49652003D9D3F /* libz.dylib in Frameworks */,
  24.110 +				270461920DE4975D003D9D3F /* CoreServices.framework in Frameworks */,
  24.111 +			);
  24.112 +			runOnlyForDeploymentPostprocessing = 0;
  24.113 +		};
  24.114 +/* End PBXFrameworksBuildPhase section */
  24.115 +
  24.116 +/* Begin PBXGroup section */
  24.117 +		08FB7794FE84155DC02AAC07 /* MYNetwork */ = {
  24.118 +			isa = PBXGroup;
  24.119 +			children = (
  24.120 +				270462C30DE4A65B003D9D3F /* BLIP Overview.txt */,
  24.121 +				270460F00DE49030003D9D3F /* MYNetwork */,
  24.122 +				270461220DE49055003D9D3F /* MYUtilities */,
  24.123 +				2704616D0DE492C9003D9D3F /* google-toolbox */,
  24.124 +				08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */,
  24.125 +				1AB674ADFE9D54B511CA2CBB /* Products */,
  24.126 +			);
  24.127 +			name = MYNetwork;
  24.128 +			sourceTree = "<group>";
  24.129 +		};
  24.130 +		08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = {
  24.131 +			isa = PBXGroup;
  24.132 +			children = (
  24.133 +				270461910DE4975C003D9D3F /* CoreServices.framework */,
  24.134 +				08FB779EFE84155DC02AAC07 /* Foundation.framework */,
  24.135 +				2704618B0DE49652003D9D3F /* libz.dylib */,
  24.136 +			);
  24.137 +			name = "External Frameworks and Libraries";
  24.138 +			sourceTree = "<group>";
  24.139 +		};
  24.140 +		1AB674ADFE9D54B511CA2CBB /* Products */ = {
  24.141 +			isa = PBXGroup;
  24.142 +			children = (
  24.143 +				270461720DE49340003D9D3F /* MYNetwork */,
  24.144 +			);
  24.145 +			name = Products;
  24.146 +			sourceTree = "<group>";
  24.147 +		};
  24.148 +		270460F00DE49030003D9D3F /* MYNetwork */ = {
  24.149 +			isa = PBXGroup;
  24.150 +			children = (
  24.151 +				270461010DE49030003D9D3F /* IPAddress.h */,
  24.152 +				270461020DE49030003D9D3F /* IPAddress.m */,
  24.153 +				270461070DE49030003D9D3F /* TCP */,
  24.154 +				270460F10DE49030003D9D3F /* BLIP */,
  24.155 +			);
  24.156 +			name = MYNetwork;
  24.157 +			sourceTree = "<group>";
  24.158 +		};
  24.159 +		270460F10DE49030003D9D3F /* BLIP */ = {
  24.160 +			isa = PBXGroup;
  24.161 +			children = (
  24.162 +				270460F30DE49030003D9D3F /* BLIPConnection.h */,
  24.163 +				270460F40DE49030003D9D3F /* BLIPConnection.m */,
  24.164 +				270460F50DE49030003D9D3F /* BLIPDispatcher.h */,
  24.165 +				270460F60DE49030003D9D3F /* BLIPDispatcher.m */,
  24.166 +				270460F80DE49030003D9D3F /* BLIPMessage.h */,
  24.167 +				270460F90DE49030003D9D3F /* BLIPMessage.m */,
  24.168 +				27D5EC050DE5FEDE00CD84FA /* BLIPRequest.h */,
  24.169 +				27D5EC060DE5FEDE00CD84FA /* BLIPRequest.m */,
  24.170 +				270460FA0DE49030003D9D3F /* BLIPProperties.h */,
  24.171 +				270460FB0DE49030003D9D3F /* BLIPProperties.m */,
  24.172 +				270460FC0DE49030003D9D3F /* BLIPReader.h */,
  24.173 +				270460FD0DE49030003D9D3F /* BLIPReader.m */,
  24.174 +				270460FF0DE49030003D9D3F /* BLIPWriter.h */,
  24.175 +				270461000DE49030003D9D3F /* BLIPWriter.m */,
  24.176 +				270460FE0DE49030003D9D3F /* BLIPTest.m */,
  24.177 +				270460F70DE49030003D9D3F /* BLIP_Internal.h */,
  24.178 +			);
  24.179 +			path = BLIP;
  24.180 +			sourceTree = "<group>";
  24.181 +		};
  24.182 +		270461070DE49030003D9D3F /* TCP */ = {
  24.183 +			isa = PBXGroup;
  24.184 +			children = (
  24.185 +				270461090DE49030003D9D3F /* TCPConnection.h */,
  24.186 +				2704610A0DE49030003D9D3F /* TCPConnection.m */,
  24.187 +				2704610B0DE49030003D9D3F /* TCPEndpoint.h */,
  24.188 +				2704610C0DE49030003D9D3F /* TCPEndpoint.m */,
  24.189 +				2704610D0DE49030003D9D3F /* TCPListener.h */,
  24.190 +				2704610E0DE49030003D9D3F /* TCPListener.m */,
  24.191 +				2704610F0DE49030003D9D3F /* TCPStream.h */,
  24.192 +				270461100DE49030003D9D3F /* TCPStream.m */,
  24.193 +				270461110DE49030003D9D3F /* TCPWriter.h */,
  24.194 +				270461120DE49030003D9D3F /* TCPWriter.m */,
  24.195 +				270461080DE49030003D9D3F /* TCP_Internal.h */,
  24.196 +			);
  24.197 +			path = TCP;
  24.198 +			sourceTree = "<group>";
  24.199 +		};
  24.200 +		270461220DE49055003D9D3F /* MYUtilities */ = {
  24.201 +			isa = PBXGroup;
  24.202 +			children = (
  24.203 +				270462C10DE4A64B003D9D3F /* MYUtilitiesTest_main.m */,
  24.204 +				270462C00DE4A639003D9D3F /* MYUtilities_Prefix.pch */,
  24.205 +				270461880DE49634003D9D3F /* CollectionUtils.h */,
  24.206 +				270461870DE49634003D9D3F /* CollectionUtils.m */,
  24.207 +				270461360DE4918D003D9D3F /* ExceptionUtils.h */,
  24.208 +				270461350DE4918D003D9D3F /* ExceptionUtils.m */,
  24.209 +				2704612B0DE49088003D9D3F /* Logging.h */,
  24.210 +				2704612A0DE49088003D9D3F /* Logging.m */,
  24.211 +				270461450DE491A6003D9D3F /* Target.h */,
  24.212 +				270461460DE491A6003D9D3F /* Target.m */,
  24.213 +				270461290DE49088003D9D3F /* Test.h */,
  24.214 +				270461280DE49088003D9D3F /* Test.m */,
  24.215 +			);
  24.216 +			name = MYUtilities;
  24.217 +			path = ../MYUtilities;
  24.218 +			sourceTree = "<group>";
  24.219 +		};
  24.220 +		2704616D0DE492C9003D9D3F /* google-toolbox */ = {
  24.221 +			isa = PBXGroup;
  24.222 +			children = (
  24.223 +				2704620E0DE4A221003D9D3F /* GTMDefines.h */,
  24.224 +				2704616E0DE492F3003D9D3F /* GTMNSData+zlib.h */,
  24.225 +				2704616F0DE492F3003D9D3F /* GTMNSData+zlib.m */,
  24.226 +			);
  24.227 +			name = "google-toolbox";
  24.228 +			sourceTree = "google-toolbox";
  24.229 +		};
  24.230 +/* End PBXGroup section */
  24.231 +
  24.232 +/* Begin PBXNativeTarget section */
  24.233 +		8DD76F960486AA7600D96B5E /* MYNetwork */ = {
  24.234 +			isa = PBXNativeTarget;
  24.235 +			buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "MYNetwork" */;
  24.236 +			buildPhases = (
  24.237 +				8DD76F990486AA7600D96B5E /* Sources */,
  24.238 +				8DD76F9B0486AA7600D96B5E /* Frameworks */,
  24.239 +				8DD76F9E0486AA7600D96B5E /* CopyFiles */,
  24.240 +			);
  24.241 +			buildRules = (
  24.242 +			);
  24.243 +			dependencies = (
  24.244 +			);
  24.245 +			name = MYNetwork;
  24.246 +			productInstallPath = "$(HOME)/bin";
  24.247 +			productName = MYNetwork;
  24.248 +			productReference = 270461720DE49340003D9D3F /* MYNetwork */;
  24.249 +			productType = "com.apple.product-type.tool";
  24.250 +		};
  24.251 +/* End PBXNativeTarget section */
  24.252 +
  24.253 +/* Begin PBXProject section */
  24.254 +		08FB7793FE84155DC02AAC07 /* Project object */ = {
  24.255 +			isa = PBXProject;
  24.256 +			buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "MYNetwork" */;
  24.257 +			compatibilityVersion = "Xcode 3.1";
  24.258 +			hasScannedForEncodings = 1;
  24.259 +			mainGroup = 08FB7794FE84155DC02AAC07 /* MYNetwork */;
  24.260 +			projectDirPath = "";
  24.261 +			projectRoot = "";
  24.262 +			targets = (
  24.263 +				8DD76F960486AA7600D96B5E /* MYNetwork */,
  24.264 +			);
  24.265 +		};
  24.266 +/* End PBXProject section */
  24.267 +
  24.268 +/* Begin PBXSourcesBuildPhase section */
  24.269 +		8DD76F990486AA7600D96B5E /* Sources */ = {
  24.270 +			isa = PBXSourcesBuildPhase;
  24.271 +			buildActionMask = 2147483647;
  24.272 +			files = (
  24.273 +				270461130DE49030003D9D3F /* BLIPConnection.m in Sources */,
  24.274 +				270461140DE49030003D9D3F /* BLIPDispatcher.m in Sources */,
  24.275 +				270461150DE49030003D9D3F /* BLIPMessage.m in Sources */,
  24.276 +				270461160DE49030003D9D3F /* BLIPProperties.m in Sources */,
  24.277 +				270461170DE49030003D9D3F /* BLIPReader.m in Sources */,
  24.278 +				270461180DE49030003D9D3F /* BLIPTest.m in Sources */,
  24.279 +				270461190DE49030003D9D3F /* BLIPWriter.m in Sources */,
  24.280 +				2704611A0DE49030003D9D3F /* IPAddress.m in Sources */,
  24.281 +				2704611B0DE49030003D9D3F /* TCPConnection.m in Sources */,
  24.282 +				2704611C0DE49030003D9D3F /* TCPEndpoint.m in Sources */,
  24.283 +				2704611D0DE49030003D9D3F /* TCPListener.m in Sources */,
  24.284 +				2704611E0DE49030003D9D3F /* TCPStream.m in Sources */,
  24.285 +				2704611F0DE49030003D9D3F /* TCPWriter.m in Sources */,
  24.286 +				2704612C0DE49088003D9D3F /* Test.m in Sources */,
  24.287 +				2704612D0DE49088003D9D3F /* Logging.m in Sources */,
  24.288 +				270461370DE4918D003D9D3F /* ExceptionUtils.m in Sources */,
  24.289 +				270461470DE491A6003D9D3F /* Target.m in Sources */,
  24.290 +				270461700DE492F3003D9D3F /* GTMNSData+zlib.m in Sources */,
  24.291 +				270461890DE49634003D9D3F /* CollectionUtils.m in Sources */,
  24.292 +				270462C20DE4A64B003D9D3F /* MYUtilitiesTest_main.m in Sources */,
  24.293 +				27D5EC070DE5FEDE00CD84FA /* BLIPRequest.m in Sources */,
  24.294 +			);
  24.295 +			runOnlyForDeploymentPostprocessing = 0;
  24.296 +		};
  24.297 +/* End PBXSourcesBuildPhase section */
  24.298 +
  24.299 +/* Begin XCBuildConfiguration section */
  24.300 +		1DEB927508733DD40010E9CD /* Debug */ = {
  24.301 +			isa = XCBuildConfiguration;
  24.302 +			buildSettings = {
  24.303 +				ALWAYS_SEARCH_USER_PATHS = NO;
  24.304 +				COPY_PHASE_STRIP = NO;
  24.305 +				GCC_C_LANGUAGE_STANDARD = gnu99;
  24.306 +				GCC_DYNAMIC_NO_PIC = NO;
  24.307 +				GCC_ENABLE_FIX_AND_CONTINUE = YES;
  24.308 +				GCC_MODEL_TUNING = G5;
  24.309 +				GCC_OPTIMIZATION_LEVEL = 0;
  24.310 +				GCC_PRECOMPILE_PREFIX_HEADER = YES;
  24.311 +				GCC_PREFIX_HEADER = ../MYUtilities/MYUtilities_Prefix.pch;
  24.312 +				GCC_PREPROCESSOR_DEFINITIONS = DEBUG;
  24.313 +				INSTALL_PATH = /usr/local/bin;
  24.314 +				PRODUCT_NAME = MYNetwork;
  24.315 +			};
  24.316 +			name = Debug;
  24.317 +		};
  24.318 +		1DEB927608733DD40010E9CD /* Release */ = {
  24.319 +			isa = XCBuildConfiguration;
  24.320 +			buildSettings = {
  24.321 +				ALWAYS_SEARCH_USER_PATHS = NO;
  24.322 +				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
  24.323 +				GCC_C_LANGUAGE_STANDARD = gnu99;
  24.324 +				GCC_MODEL_TUNING = G5;
  24.325 +				GCC_PRECOMPILE_PREFIX_HEADER = YES;
  24.326 +				GCC_PREFIX_HEADER = MYNetwork_Prefix.pch;
  24.327 +				INSTALL_PATH = /usr/local/bin;
  24.328 +				PRODUCT_NAME = MYNetwork;
  24.329 +			};
  24.330 +			name = Release;
  24.331 +		};
  24.332 +		1DEB927908733DD40010E9CD /* Debug */ = {
  24.333 +			isa = XCBuildConfiguration;
  24.334 +			buildSettings = {
  24.335 +				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
  24.336 +				GCC_C_LANGUAGE_STANDARD = c99;
  24.337 +				GCC_OPTIMIZATION_LEVEL = 0;
  24.338 +				GCC_WARN_ABOUT_RETURN_TYPE = YES;
  24.339 +				GCC_WARN_UNUSED_VARIABLE = YES;
  24.340 +				ONLY_ACTIVE_ARCH = YES;
  24.341 +				PREBINDING = NO;
  24.342 +				SDKROOT = macosx10.5;
  24.343 +			};
  24.344 +			name = Debug;
  24.345 +		};
  24.346 +		1DEB927A08733DD40010E9CD /* Release */ = {
  24.347 +			isa = XCBuildConfiguration;
  24.348 +			buildSettings = {
  24.349 +				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
  24.350 +				GCC_C_LANGUAGE_STANDARD = c99;
  24.351 +				GCC_WARN_ABOUT_RETURN_TYPE = YES;
  24.352 +				GCC_WARN_UNUSED_VARIABLE = YES;
  24.353 +				PREBINDING = NO;
  24.354 +				SDKROOT = macosx10.5;
  24.355 +			};
  24.356 +			name = Release;
  24.357 +		};
  24.358 +/* End XCBuildConfiguration section */
  24.359 +
  24.360 +/* Begin XCConfigurationList section */
  24.361 +		1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "MYNetwork" */ = {
  24.362 +			isa = XCConfigurationList;
  24.363 +			buildConfigurations = (
  24.364 +				1DEB927508733DD40010E9CD /* Debug */,
  24.365 +				1DEB927608733DD40010E9CD /* Release */,
  24.366 +			);
  24.367 +			defaultConfigurationIsVisible = 0;
  24.368 +			defaultConfigurationName = Release;
  24.369 +		};
  24.370 +		1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "MYNetwork" */ = {
  24.371 +			isa = XCConfigurationList;
  24.372 +			buildConfigurations = (
  24.373 +				1DEB927908733DD40010E9CD /* Debug */,
  24.374 +				1DEB927A08733DD40010E9CD /* Release */,
  24.375 +			);
  24.376 +			defaultConfigurationIsVisible = 0;
  24.377 +			defaultConfigurationName = Release;
  24.378 +		};
  24.379 +/* End XCConfigurationList section */
  24.380 +	};
  24.381 +	rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
  24.382 +}
    25.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.2 +++ b/TCP/TCPConnection.h	Fri May 23 17:37:36 2008 -0700
    25.3 @@ -0,0 +1,124 @@
    25.4 +//
    25.5 +//  TCPConnection.h
    25.6 +//  MYNetwork
    25.7 +//
    25.8 +//  Created by Jens Alfke on 5/18/08.
    25.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   25.10 +//
   25.11 +
   25.12 +#import "TCPEndpoint.h"
   25.13 +@class IPAddress;
   25.14 +@class TCPReader, TCPWriter, TCPListener;
   25.15 +@protocol TCPConnectionDelegate;
   25.16 +
   25.17 +
   25.18 +typedef enum {
   25.19 +    kTCP_Disconnected = -1,
   25.20 +    kTCP_Closed,
   25.21 +    kTCP_Opening,
   25.22 +    kTCP_Open,
   25.23 +    kTCP_Closing
   25.24 +} TCPConnectionStatus;
   25.25 +
   25.26 +
   25.27 +/** A generic class that manages a TCP socket connection.
   25.28 +    It creates a TCPReader and a TCPWriter to handle I/O.
   25.29 +    TCPConnection itself mostly deals with SSL setup and opening/closing the socket. */
   25.30 +@interface TCPConnection : TCPEndpoint
   25.31 +{
   25.32 +    @private
   25.33 +    TCPListener *_server;
   25.34 +    IPAddress *_address;
   25.35 +    BOOL _isIncoming, _checkedPeerCert;
   25.36 +    TCPConnectionStatus _status;
   25.37 +    TCPReader *_reader;
   25.38 +    TCPWriter *_writer;
   25.39 +    NSError *_error;
   25.40 +}
   25.41 +
   25.42 +/** Initializes a TCPConnection to the given IP address.
   25.43 +    Afer configuring settings, you should call -open to begin the connection. */
   25.44 +- (id) initToAddress: (IPAddress*)address;
   25.45 +
   25.46 +/** Initializes a TCPConnection to the given IP address, binding to a specific outgoing port
   25.47 +    number. (This is usually only necessary when attempting to tunnel through a NAT.) */
   25.48 +- (id) initToAddress: (IPAddress*)address
   25.49 +           localPort: (UInt16)localPort;
   25.50 +
   25.51 +/** Initializes a TCPConnection from an incoming TCP socket.
   25.52 +    You don't usually need to call this; TCPListener does it automatically. */
   25.53 +- (id) initIncomingFromSocket: (CFSocketNativeHandle)socket listener: (TCPListener*)listener;
   25.54 +
   25.55 +/** The delegate object that will be called when the connection opens, closes or receives messages. */
   25.56 +@property (assign) id<TCPConnectionDelegate> delegate;
   25.57 +
   25.58 +/** The certificate(s) of the connected peer, if this connection uses SSL.
   25.59 +    The items in the array are SecCertificateRefs; use the Keychain API to work with them. */
   25.60 +@property (readonly) NSArray *peerSSLCerts;
   25.61 +
   25.62 +/** Connection's current status */
   25.63 +@property (readonly) TCPConnectionStatus status;
   25.64 +
   25.65 +/** Opens the connection. This happens asynchronously; wait for a delegate method to be called.
   25.66 +    You don't need to open incoming connections received via a TCPListener. */
   25.67 +- (void) open;
   25.68 +
   25.69 +/** Closes the connection, after waiting for all in-progress messages to be sent or received.
   25.70 +    This happens asynchronously; wait for a delegate method to be called.*/
   25.71 +- (void) close;
   25.72 +
   25.73 +/** Closes the connection, like -close, but if it hasn't closed by the time the timeout
   25.74 +    expires, it will disconnect the socket. */
   25.75 +- (void) closeWithTimeout: (NSTimeInterval)timeout;
   25.76 +
   25.77 +/** Closes all open TCPConnections. */
   25.78 ++ (void) closeAllWithTimeout: (NSTimeInterval)timeout;
   25.79 +
   25.80 +/** Blocks until all open TCPConnections close. You should call +closeAllWithTimeout: first. */
   25.81 ++ (void) waitTillAllClosed;
   25.82 +
   25.83 +/** The IP address of the other peer. */
   25.84 +@property (readonly) IPAddress *address;
   25.85 +
   25.86 +/** The TCPListener that created this incoming connection, or nil */
   25.87 +@property (readonly) TCPListener *server;
   25.88 +
   25.89 +/** Is this an incoming connection, received via a TCPListener? */
   25.90 +@property (readonly) BOOL isIncoming;
   25.91 +
   25.92 +/** The fatal error, if any, 
   25.93 +    that caused the connection to fail to open or to disconnect unexpectedly. */
   25.94 +@property (readonly) NSError *error;
   25.95 +
   25.96 +/** The actual security level of this connection. 
   25.97 +    Value is nil or one of the security level constants from NSStream.h,
   25.98 +    such as NSStreamSocketSecurityLevelTLSv1. */
   25.99 +@property (readonly) NSString* actualSecurityLevel;
  25.100 +
  25.101 +
  25.102 +@property (readonly) TCPReader *reader;
  25.103 +@property (readonly) TCPWriter *writer;
  25.104 +
  25.105 +
  25.106 +// protected:
  25.107 +- (Class) readerClass;
  25.108 +- (Class) writerClass;
  25.109 +
  25.110 +@end
  25.111 +
  25.112 +
  25.113 +
  25.114 +/** The delegate messages sent by TCPConnection. */
  25.115 +@protocol TCPConnectionDelegate <NSObject>
  25.116 +@optional
  25.117 +/** Called after the connection successfully opens. */
  25.118 +- (void) connectionDidOpen: (TCPConnection*)connection;
  25.119 +/** Called after the connection fails to open due to an error. */
  25.120 +- (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error;
  25.121 +/** Called when the identity of the peer is known, if using an SSL connection and the SSL
  25.122 +    settings say to check the peer's certificate.
  25.123 +    This happens, if at all, after the -connectionDidOpen: call. */
  25.124 +- (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert;
  25.125 +/** Called after the connection closes. */
  25.126 +- (void) connectionDidClose: (TCPConnection*)connection;
  25.127 +@end
    26.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    26.2 +++ b/TCP/TCPConnection.m	Fri May 23 17:37:36 2008 -0700
    26.3 @@ -0,0 +1,365 @@
    26.4 +//
    26.5 +//  TCPConnection.m
    26.6 +//  MYNetwork
    26.7 +//
    26.8 +//  Created by Jens Alfke on 5/18/08.
    26.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   26.10 +//
   26.11 +
   26.12 +#import "TCP_Internal.h"
   26.13 +#import "IPAddress.h"
   26.14 +
   26.15 +#import "ExceptionUtils.h"
   26.16 +
   26.17 +
   26.18 +NSString* const TCPErrorDomain = @"TCP";
   26.19 +
   26.20 +
   26.21 +@interface TCPConnection ()
   26.22 +@property TCPConnectionStatus status;
   26.23 +- (BOOL) _checkIfClosed;
   26.24 +- (void) _closed;
   26.25 +@end
   26.26 +
   26.27 +
   26.28 +@implementation TCPConnection
   26.29 +
   26.30 +
   26.31 +static NSMutableArray *sAllConnections;
   26.32 +
   26.33 +
   26.34 +- (Class) readerClass   {return [TCPReader class];}
   26.35 +- (Class) writerClass   {return [TCPWriter class];}
   26.36 +
   26.37 +
   26.38 +- (id) _initWithAddress: (IPAddress*)address
   26.39 +            inputStream: (NSInputStream*)input
   26.40 +           outputStream: (NSOutputStream*)output
   26.41 +{
   26.42 +    self = [super init];
   26.43 +    if (self != nil) {
   26.44 +        if( !address || !input || !output ) {
   26.45 +            LogTo(TCP,@"Failed to create %@: addr=%@, in=%@, out=%@",
   26.46 +                  self.class,address,input,output);
   26.47 +            [self release];
   26.48 +            return nil;
   26.49 +        }
   26.50 +        _address = [address copy];
   26.51 +        _reader = [[[self readerClass] alloc] initWithConnection: self stream: input];
   26.52 +        _writer = [[[self writerClass] alloc] initWithConnection: self stream: output];
   26.53 +        LogTo(TCP,@"%@ initialized",self);
   26.54 +    }
   26.55 +    return self;
   26.56 +}
   26.57 +
   26.58 +
   26.59 +
   26.60 +- (id) initToAddress: (IPAddress*)address
   26.61 +           localPort: (UInt16)localPort
   26.62 +{
   26.63 +    NSInputStream *input = nil;
   26.64 +    NSOutputStream *output = nil;
   26.65 +    [NSStream getStreamsToHost: [NSHost hostWithAddress: address.ipv4name]
   26.66 +                          port: address.port 
   26.67 +                   inputStream: &input 
   26.68 +                  outputStream: &output];
   26.69 +    return [self _initWithAddress: address inputStream: input outputStream: output];
   26.70 +    //FIX: Support localPort!
   26.71 +}
   26.72 +
   26.73 +- (id) initToAddress: (IPAddress*)address
   26.74 +{
   26.75 +    return [self initToAddress: address localPort: 0];
   26.76 +}
   26.77 +
   26.78 +
   26.79 +- (id) initIncomingFromSocket: (CFSocketNativeHandle)socket
   26.80 +                     listener: (TCPListener*)listener
   26.81 +{
   26.82 +    CFReadStreamRef readStream = NULL;
   26.83 +    CFWriteStreamRef writeStream = NULL;
   26.84 +    CFStreamCreatePairWithSocket(kCFAllocatorDefault, socket, &readStream, &writeStream);
   26.85 +    self = [self _initWithAddress: [IPAddress addressOfSocket: socket] 
   26.86 +                      inputStream: (NSInputStream*)readStream
   26.87 +                     outputStream: (NSOutputStream*)writeStream];
   26.88 +    if( self ) {
   26.89 +        _isIncoming = YES;
   26.90 +        _server = [listener retain];
   26.91 +        CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
   26.92 +        CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
   26.93 +    }
   26.94 +    return self;
   26.95 +}    
   26.96 +
   26.97 +
   26.98 +- (void) dealloc
   26.99 +{
  26.100 +    LogTo(TCP,@"DEALLOC %@",self);
  26.101 +    [_reader release];
  26.102 +    [_writer release];
  26.103 +    [_address release];
  26.104 +    [super dealloc];
  26.105 +}
  26.106 +
  26.107 +
  26.108 +- (NSString*) description
  26.109 +{
  26.110 +    return $sprintf(@"%@[%@ %@]",self.class,(_isIncoming ?@"from" :@"to"),_address);
  26.111 +}
  26.112 +
  26.113 +
  26.114 +@synthesize address=_address, isIncoming=_isIncoming, status=_status, delegate=_delegate,
  26.115 +            reader=_reader, writer=_writer, server=_server;
  26.116 +
  26.117 +
  26.118 +- (NSError*) error
  26.119 +{
  26.120 +    return _error;
  26.121 +}
  26.122 +
  26.123 +
  26.124 +- (NSString*) actualSecurityLevel
  26.125 +{
  26.126 +    return _reader.securityLevel;
  26.127 +
  26.128 +}
  26.129 +
  26.130 +- (NSArray*) peerSSLCerts
  26.131 +{
  26.132 +    return _reader.peerSSLCerts ?: _writer.peerSSLCerts;
  26.133 +}
  26.134 +
  26.135 +
  26.136 +- (void) _setStreamProperty: (id)value forKey: (NSString*)key
  26.137 +{
  26.138 +    [_reader setProperty: value forKey: (CFStringRef)key];
  26.139 +    [_writer setProperty: value forKey: (CFStringRef)key];
  26.140 +}
  26.141 +
  26.142 +
  26.143 +#pragma mark -
  26.144 +#pragma mark OPENING / CLOSING:
  26.145 +
  26.146 +
  26.147 +- (void) open
  26.148 +{
  26.149 +    if( _status<=kTCP_Closed && _reader ) {
  26.150 +        _reader.SSLProperties = _sslProperties;
  26.151 +        _writer.SSLProperties = _sslProperties;
  26.152 +        [_reader open];
  26.153 +        [_writer open];
  26.154 +        if( ! [sAllConnections my_containsObjectIdenticalTo: self] )
  26.155 +            [sAllConnections addObject: self];
  26.156 +        self.status = kTCP_Opening;
  26.157 +    }
  26.158 +}
  26.159 +
  26.160 +
  26.161 +- (void) disconnect
  26.162 +{
  26.163 +    if( _status > kTCP_Closed ) {
  26.164 +        LogTo(TCP,@"%@ disconnecting",self);
  26.165 +        [_writer disconnect];
  26.166 +        setObj(&_writer,nil);
  26.167 +        [_reader disconnect];
  26.168 +        setObj(&_reader,nil);
  26.169 +        self.status = kTCP_Disconnected;
  26.170 +    }
  26.171 +}
  26.172 +
  26.173 +
  26.174 +- (void) close
  26.175 +{
  26.176 +    [self closeWithTimeout: 60.0];
  26.177 +}
  26.178 +
  26.179 +- (void) closeWithTimeout: (NSTimeInterval)timeout
  26.180 +{
  26.181 +    if( _status == kTCP_Opening ) {
  26.182 +        LogTo(TCP,@"%@ canceling open",self);
  26.183 +        [self _closed];
  26.184 +    } else if( _status == kTCP_Open ) {
  26.185 +        LogTo(TCP,@"%@ closing",self);
  26.186 +        self.status = kTCP_Closing;
  26.187 +        [self retain];
  26.188 +        [_reader close];
  26.189 +        [_writer close];
  26.190 +        if( ! [self _checkIfClosed] ) {
  26.191 +            if( timeout <= 0.0 )
  26.192 +                [self disconnect];
  26.193 +            else if( timeout != INFINITY )
  26.194 +                [self performSelector: @selector(_closeTimeoutExpired)
  26.195 +                           withObject: nil afterDelay: timeout];
  26.196 +        }
  26.197 +        [self release];
  26.198 +    }
  26.199 +}
  26.200 +
  26.201 +- (void) _closeTimeoutExpired
  26.202 +{
  26.203 +    if( _status==kTCP_Closing )
  26.204 +        [self disconnect];
  26.205 +}
  26.206 +
  26.207 +
  26.208 +- (BOOL) _checkIfClosed
  26.209 +{
  26.210 +    if( _status == kTCP_Closing && _writer==nil && _reader==nil ) {
  26.211 +        [self _closed];
  26.212 +        return YES;
  26.213 +    } else
  26.214 +        return NO;
  26.215 +}
  26.216 +
  26.217 +
  26.218 +// called by my streams when they close (after my -close is called)
  26.219 +- (void) _closed
  26.220 +{
  26.221 +    if( _status != kTCP_Closed && _status != kTCP_Disconnected ) {
  26.222 +        LogTo(TCP,@"%@ is now closed",self);
  26.223 +        self.status = (_status==kTCP_Closing ?kTCP_Closed :kTCP_Disconnected);
  26.224 +        [self tellDelegate: @selector(connectionDidClose:) withObject: nil];
  26.225 +    }
  26.226 +    [NSObject cancelPreviousPerformRequestsWithTarget: self
  26.227 +                                             selector: @selector(_closeTimeoutExpired)
  26.228 +                                               object: nil];
  26.229 +    [sAllConnections removeObjectIdenticalTo: self];
  26.230 +}
  26.231 +
  26.232 +
  26.233 ++ (void) closeAllWithTimeout: (NSTimeInterval)timeout
  26.234 +{
  26.235 +    NSArray *connections = [sAllConnections copy];
  26.236 +    for( TCPConnection *conn in connections )
  26.237 +        [conn closeWithTimeout: timeout];
  26.238 +    [connections release];
  26.239 +}
  26.240 +
  26.241 ++ (void) waitTillAllClosed
  26.242 +{
  26.243 +    while( sAllConnections.count ) {
  26.244 +        if( ! [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
  26.245 +                                       beforeDate: [NSDate distantFuture]] )
  26.246 +            break;
  26.247 +    }
  26.248 +}
  26.249 +
  26.250 +
  26.251 +#pragma mark -
  26.252 +#pragma mark STREAM CALLBACKS:
  26.253 +
  26.254 +
  26.255 +- (void) _streamOpened: (TCPStream*)stream
  26.256 +{
  26.257 +    if( _status==kTCP_Opening && _reader.isOpen && _writer.isOpen ) {
  26.258 +        LogTo(TCP,@"%@ opened",self);
  26.259 +        self.status = kTCP_Open;
  26.260 +        [self tellDelegate: @selector(connectionDidOpen:) withObject: nil];
  26.261 +    }
  26.262 +}
  26.263 +
  26.264 +
  26.265 +- (BOOL) _streamPeerCertAvailable: (TCPStream*)stream
  26.266 +{
  26.267 +    BOOL allow = YES;
  26.268 +    if( ! _checkedPeerCert ) {
  26.269 +        @try{
  26.270 +            _checkedPeerCert = YES;
  26.271 +            if( stream.securityLevel != nil ) {
  26.272 +                NSArray *certs = stream.peerSSLCerts;
  26.273 +                if( ! certs && ! _isIncoming )
  26.274 +                    allow = NO; // Server MUST have a cert!
  26.275 +                else {
  26.276 +                    SecCertificateRef cert = certs.count ?(SecCertificateRef)[certs objectAtIndex:0] :NULL;
  26.277 +                    LogTo(TCP,@"%@: Peer cert = %@",self,cert);
  26.278 +                    if( [_delegate respondsToSelector: @selector(connection:authorizeSSLPeer:)] )
  26.279 +                        allow = [_delegate connection: self authorizeSSLPeer: cert];
  26.280 +                }
  26.281 +            }
  26.282 +        }@catch( NSException *x ) {
  26.283 +            MYReportException(x,@"TCPConnection _streamPeerCertAvailable");
  26.284 +            _checkedPeerCert = NO;
  26.285 +            allow = NO;
  26.286 +        }
  26.287 +        if( ! allow )
  26.288 +            [self _stream: stream 
  26.289 +                 gotError: [NSError errorWithDomain: NSStreamSocketSSLErrorDomain
  26.290 +                                               code: errSSLClosedAbort
  26.291 +                                           userInfo: nil]];
  26.292 +    }
  26.293 +    return allow;
  26.294 +}
  26.295 +
  26.296 +
  26.297 +- (void) _stream: (TCPStream*)stream gotError: (NSError*)error
  26.298 +{
  26.299 +    LogTo(TCP,@"%@ got %@ on %@",self,error,stream.class);
  26.300 +    Assert(error);
  26.301 +    setObj(&_error,error);
  26.302 +    [_reader disconnect];
  26.303 +    setObj(&_reader,nil);
  26.304 +    [_writer disconnect];
  26.305 +    setObj(&_writer,nil);
  26.306 +    [self _closed];
  26.307 +}
  26.308 +
  26.309 +- (void) _streamGotEOF: (TCPStream*)stream
  26.310 +{
  26.311 +    LogTo(TCP,@"%@ got EOF on %@",self,stream);
  26.312 +    if( stream == _reader ) {
  26.313 +        setObj(&_reader,nil);
  26.314 +        // This is the expected way for he peer to initiate closing the connection.
  26.315 +        if( _status==kTCP_Open ) {
  26.316 +            [self closeWithTimeout: INFINITY];
  26.317 +            return;
  26.318 +        }
  26.319 +    } else if( stream == _writer ) {
  26.320 +        setObj(&_writer,nil);
  26.321 +    }
  26.322 +    
  26.323 +    if( _status == kTCP_Closing ) {
  26.324 +        [self _checkIfClosed];
  26.325 +    } else {
  26.326 +        [self _stream: stream 
  26.327 +             gotError: [NSError errorWithDomain: NSPOSIXErrorDomain code: ECONNRESET userInfo: nil]];
  26.328 +    }
  26.329 +}
  26.330 +
  26.331 +
  26.332 +// Called after I called -close on a stream and it finished closing:
  26.333 +- (void) _streamClosed: (TCPStream*)stream
  26.334 +{
  26.335 +    LogTo(TCP,@"%@ finished closing %@",self,stream);
  26.336 +    if( stream == _reader )
  26.337 +        setObj(&_reader,nil);
  26.338 +    else if( stream == _writer )
  26.339 +        setObj(&_writer,nil);
  26.340 +    if( !_reader.isOpen && !_writer.isOpen )
  26.341 +        [self _closed];
  26.342 +}
  26.343 +
  26.344 +
  26.345 +@end
  26.346 +
  26.347 +
  26.348 +/*
  26.349 + Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
  26.350 + 
  26.351 + Redistribution and use in source and binary forms, with or without modification, are permitted
  26.352 + provided that the following conditions are met:
  26.353 + 
  26.354 + * Redistributions of source code must retain the above copyright notice, this list of conditions
  26.355 + and the following disclaimer.
  26.356 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
  26.357 + and the following disclaimer in the documentation and/or other materials provided with the
  26.358 + distribution.
  26.359 + 
  26.360 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  26.361 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
  26.362 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
  26.363 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  26.364 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  26.365 +  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  26.366 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
  26.367 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26.368 + */
    27.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    27.2 +++ b/TCP/TCPEndpoint.h	Fri May 23 17:37:36 2008 -0700
    27.3 @@ -0,0 +1,42 @@
    27.4 +//
    27.5 +//  TCPEndpoint.h
    27.6 +//  MYNetwork
    27.7 +//
    27.8 +//  Created by Jens Alfke on 5/14/08.
    27.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   27.10 +//
   27.11 +
   27.12 +#import <Foundation/Foundation.h>
   27.13 +#import <CoreServices/CoreServices.h>
   27.14 +
   27.15 +
   27.16 +// SSL properties:
   27.17 +#define kTCPPropertySSLCertificates  ((NSString*)kCFStreamSSLCertificates)
   27.18 +#define kTCPPropertySSLAllowsAnyRoot ((NSString*)kCFStreamSSLAllowsAnyRoot)
   27.19 +extern NSString* const kTCPPropertySSLClientSideAuthentication;    // value is SSLAuthenticate enum
   27.20 +
   27.21 +
   27.22 +/** Abstract base class of TCPConnection and TCPListener.
   27.23 +    Mostly just manages the SSL properties. */
   27.24 +@interface TCPEndpoint : NSObject
   27.25 +{
   27.26 +    NSMutableDictionary *_sslProperties;
   27.27 +    id _delegate;
   27.28 +}
   27.29 +
   27.30 +/** The desired security level. Use the security level constants from NSStream.h,
   27.31 +    such as NSStreamSocketSecurityLevelNegotiatedSSL. */
   27.32 +@property (copy) NSString *securityLevel;
   27.33 +
   27.34 +/** Detailed SSL settings. This is the same as CFStream's kCFStreamPropertySSLSettings
   27.35 +    property. */
   27.36 +@property (copy) NSMutableDictionary *SSLProperties;
   27.37 +
   27.38 +/** Shortcut to set a single SSL property. */
   27.39 +- (void) setSSLProperty: (id)value 
   27.40 +                 forKey: (NSString*)key;
   27.41 +
   27.42 +//protected:
   27.43 +- (void) tellDelegate: (SEL)selector withObject: (id)param;
   27.44 +
   27.45 +@end
    28.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    28.2 +++ b/TCP/TCPEndpoint.m	Fri May 23 17:37:36 2008 -0700
    28.3 @@ -0,0 +1,84 @@
    28.4 +//
    28.5 +//  BLIPEndpoint.m
    28.6 +//  MYNetwork
    28.7 +//
    28.8 +//  Created by Jens Alfke on 5/14/08.
    28.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   28.10 +//
   28.11 +
   28.12 +#import "TCPEndpoint.h"
   28.13 +
   28.14 +#import "ExceptionUtils.h"
   28.15 +
   28.16 +
   28.17 +NSString* const kTCPPropertySSLClientSideAuthentication = @"kTCPPropertySSLClientSideAuthentication";
   28.18 +
   28.19 +
   28.20 +@implementation TCPEndpoint
   28.21 +
   28.22 +
   28.23 +- (void) dealloc
   28.24 +{
   28.25 +    [_sslProperties release];
   28.26 +    [super dealloc];
   28.27 +}
   28.28 +
   28.29 +
   28.30 +- (NSMutableDictionary*) SSLProperties {return _sslProperties;}
   28.31 +
   28.32 +- (void) setSSLProperties: (NSMutableDictionary*)props
   28.33 +{
   28.34 +    if( props != _sslProperties ) {
   28.35 +        [_sslProperties release];
   28.36 +        _sslProperties = [props mutableCopy];
   28.37 +    }
   28.38 +}
   28.39 +
   28.40 +- (void) setSSLProperty: (id)value forKey: (NSString*)key
   28.41 +{
   28.42 +    if( value ) {
   28.43 +        if( ! _sslProperties )
   28.44 +            _sslProperties = [[NSMutableDictionary alloc] init];
   28.45 +        [_sslProperties setObject: value forKey: key];
   28.46 +    } else
   28.47 +        [_sslProperties removeObjectForKey: key];
   28.48 +}
   28.49 +
   28.50 +- (NSString*) securityLevel                 {return [_sslProperties objectForKey: (id)kCFStreamSSLLevel];}
   28.51 +- (void) setSecurityLevel: (NSString*)level {[self setSSLProperty: level forKey: (id)kCFStreamSSLLevel];}
   28.52 +
   28.53 +
   28.54 +- (void) tellDelegate: (SEL)selector withObject: (id)param
   28.55 +{
   28.56 +    if( [_delegate respondsToSelector: selector] ) {
   28.57 +        @try{
   28.58 +            [_delegate performSelector: selector withObject: self withObject: param];
   28.59 +        }catchAndReport(@"%@ delegate",self.class);
   28.60 +    }
   28.61 +}
   28.62 +
   28.63 +
   28.64 +@end
   28.65 +
   28.66 +
   28.67 +/*
   28.68 + Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   28.69 + 
   28.70 + Redistribution and use in source and binary forms, with or without modification, are permitted
   28.71 + provided that the following conditions are met:
   28.72 + 
   28.73 + * Redistributions of source code must retain the above copyright notice, this list of conditions
   28.74 + and the following disclaimer.
   28.75 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   28.76 + and the following disclaimer in the documentation and/or other materials provided with the
   28.77 + distribution.
   28.78 + 
   28.79 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   28.80 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   28.81 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   28.82 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   28.83 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   28.84 +  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   28.85 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   28.86 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   28.87 + */
    29.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    29.2 +++ b/TCP/TCPListener.h	Fri May 23 17:37:36 2008 -0700
    29.3 @@ -0,0 +1,107 @@
    29.4 +//
    29.5 +//  TCPListener.m
    29.6 +//  MYNetwork
    29.7 +//
    29.8 +//  Created by Jens Alfke on 5/10/08.
    29.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   29.10 +
   29.11 +#import "TCPEndpoint.h"
   29.12 +@class TCPConnection, IPAddress;
   29.13 +@protocol TCPListenerDelegate;
   29.14 +
   29.15 +
   29.16 +/** Generic TCP-based server that listens for incoming connections on a port.
   29.17 +    For each incoming connection, it creates an instance of (a subclass of) the generic TCP
   29.18 +    client class TCPClient. The -connectionClass property lets you customize which subclass
   29.19 +    to use.
   29.20 +    TCPListener supports Bonjour advertisements for the service, and automatic port renumbering
   29.21 +    if there are conflicts. */
   29.22 +@interface TCPListener : TCPEndpoint 
   29.23 +{
   29.24 +    @private
   29.25 +    uint16_t _port;
   29.26 +    BOOL _pickAvailablePort;
   29.27 +    BOOL _useIPv6;
   29.28 +    CFSocketRef _ipv4socket;
   29.29 +    CFSocketRef _ipv6socket;
   29.30 +    
   29.31 +    NSString *_bonjourServiceType, *_bonjourServiceName;
   29.32 +    NSNetService *_netService;
   29.33 +    NSDictionary *_bonjourTXTRecord;
   29.34 +    BOOL _bonjourPublished;
   29.35 +    NSInteger /*NSNetServicesError*/ _bonjourError;
   29.36 +
   29.37 +    Class _connectionClass;
   29.38 +}
   29.39 +
   29.40 +/** Initializes a new TCPListener that will listen on the given port when opened. */
   29.41 +- (id) initWithPort: (UInt16)port;
   29.42 +
   29.43 +/** The subclass of TCPConnection that will be instantiated. */
   29.44 +@property Class connectionClass;
   29.45 +
   29.46 +@property (assign) id<TCPListenerDelegate> delegate;
   29.47 +
   29.48 +/** Should the server listen for IPv6 connections (on the same port number)? Defaults to NO. */
   29.49 +@property BOOL useIPv6;
   29.50 +
   29.51 +/** The port number to listen on.
   29.52 +    If the pickAvailablePort property is enabled, this value may be updated after the server opens
   29.53 +    to reflect the actual port number being used. */
   29.54 +@property uint16_t port;
   29.55 +
   29.56 +/** Should the server pick a higher port number if the desired port is already in use?
   29.57 +    Defaults to NO. If enabled, the port number will be incremented until a free port is found. */
   29.58 +@property BOOL pickAvailablePort;
   29.59 +
   29.60 +/** Opens the server. You must call this after configuring all desired properties (property
   29.61 +    changes are ignored while the server is open.) */
   29.62 +- (BOOL) open: (NSError **)error;
   29.63 +
   29.64 +- (BOOL) open;
   29.65 +
   29.66 +/** Closes the server. */
   29.67 +- (void) close;
   29.68 +
   29.69 +/** Is the server currently open? */
   29.70 +@property (readonly) BOOL isOpen;
   29.71 +
   29.72 +
   29.73 +#pragma mark BONJOUR:
   29.74 +
   29.75 +/** The Bonjour service type to advertise. Defaults to nil; setting it implicitly enables Bonjour.
   29.76 +    The value should look like e.g. "_http._tcp."; for details, see the NSNetService documentation. */
   29.77 +@property (copy) NSString *bonjourServiceType;
   29.78 +
   29.79 +/** The Bonjour service name to advertise. Defaults to nil, meaning that a default name will be
   29.80 +    automatically generated if Bonjour is enabled (by setting -bonjourServiceType). */
   29.81 +@property (copy) NSString *bonjourServiceName;
   29.82 +
   29.83 +/** The dictionary form of the Bonjour TXT record: metadata about the service that can be browsed
   29.84 +    by peers. Changes to this dictionary will be pushed in near-real-time to interested peers. */
   29.85 +@property (copy) NSDictionary *bonjourTXTRecord;
   29.86 +
   29.87 +/** Is this service currently published/advertised via Bonjour? */
   29.88 +@property (readonly) BOOL bonjourPublished;
   29.89 +
   29.90 +/** Current error status of Bonjour service advertising. See NSNetServicesError for error codes. */
   29.91 +@property (readonly) NSInteger /*NSNetServicesError*/ bonjourError;
   29.92 +
   29.93 +
   29.94 +@end
   29.95 +
   29.96 +
   29.97 +
   29.98 +#pragma mark -
   29.99 +
  29.100 +/** The delegate messages sent by TCPListener. */
  29.101 +@protocol TCPListenerDelegate <NSObject>
  29.102 +
  29.103 +- (void) listener: (TCPListener*)listener didAcceptConnection: (TCPConnection*)connection;
  29.104 +
  29.105 +@optional
  29.106 +- (void) listenerDidOpen: (TCPListener*)listener;
  29.107 +- (void) listener: (TCPListener*)listener failedToOpen: (NSError*)error;
  29.108 +- (void) listenerDidClose: (TCPListener*)listener;
  29.109 +- (BOOL) listener: (TCPListener*)listener shouldAcceptConnectionFrom: (IPAddress*)address;
  29.110 +@end
    30.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.2 +++ b/TCP/TCPListener.m	Fri May 23 17:37:36 2008 -0700
    30.3 @@ -0,0 +1,342 @@
    30.4 +//
    30.5 +//  TCPListener.m
    30.6 +//  MYNetwork
    30.7 +//
    30.8 +//  Created by Jens Alfke on 5/10/08.
    30.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   30.10 +//  Portions based on TCPServer class from Apple's "CocoaEcho" sample code.
   30.11 +
   30.12 +#import "TCPListener.h"
   30.13 +#import "TCPConnection.h"
   30.14 +
   30.15 +#import "ExceptionUtils.h"
   30.16 +#import "IPAddress.h"
   30.17 +#include <sys/socket.h>
   30.18 +
   30.19 +#include <netinet/in.h>
   30.20 +#include <unistd.h>
   30.21 +
   30.22 +
   30.23 +static void TCPListenerAcceptCallBack(CFSocketRef socket, CFSocketCallBackType type, 
   30.24 +                                      CFDataRef address, const void *data, void *info);
   30.25 +
   30.26 +@interface TCPListener()
   30.27 +@property BOOL bonjourPublished;
   30.28 +@property NSInteger bonjourError;
   30.29 +- (void) _updateTXTRecord;
   30.30 +@end
   30.31 +
   30.32 +
   30.33 +@implementation TCPListener
   30.34 +
   30.35 +
   30.36 +- (id) initWithPort: (UInt16)port
   30.37 +{
   30.38 +    self = [super init];
   30.39 +    if (self != nil) {
   30.40 +        _port = port;
   30.41 +    }
   30.42 +    return self;
   30.43 +}
   30.44 +
   30.45 +
   30.46 +- (void) dealloc 
   30.47 +{
   30.48 +    [self close];
   30.49 +    LogTo(TCP,@"DEALLOC %@",self);
   30.50 +    [super dealloc];
   30.51 +}
   30.52 +
   30.53 +
   30.54 +@synthesize delegate=_delegate, port=_port, useIPv6=_useIPv6,
   30.55 +            bonjourServiceType=_bonjourServiceType, bonjourServiceName=_bonjourServiceName,
   30.56 +            bonjourPublished=_bonjourPublished, bonjourError=_bonjourError,
   30.57 +            pickAvailablePort=_pickAvailablePort;
   30.58 +
   30.59 +
   30.60 +- (NSString*) description
   30.61 +{
   30.62 +    return $sprintf(@"%@[port %hu]",self.class,_port);
   30.63 +}
   30.64 +
   30.65 +
   30.66 +// Stores the last error from CFSocketCreate or CFSocketSetAddress into *ouError.
   30.67 +static void* getLastCFSocketError( NSError **outError ) {
   30.68 +    if( outError )
   30.69 +        *outError = [NSError errorWithDomain: NSPOSIXErrorDomain code: errno userInfo: nil];
   30.70 +    return NULL;
   30.71 +}
   30.72 +
   30.73 +// Closes a socket (if it's not already NULL), and returns NULL to assign to it.
   30.74 +static CFSocketRef closeSocket( CFSocketRef socket ) {
   30.75 +    if( socket ) {
   30.76 +        CFSocketInvalidate(socket);
   30.77 +        CFRelease(socket);
   30.78 +    }
   30.79 +    return NULL;
   30.80 +}
   30.81 +
   30.82 +// opens a socket of a given protocol, either ipv4 or ipv6.
   30.83 +- (CFSocketRef) _openProtocol: (SInt32) protocolFamily 
   30.84 +                      address: (struct sockaddr*)address
   30.85 +                        error: (NSError**)error
   30.86 +{
   30.87 +    CFSocketContext socketCtxt = {0, self, NULL, NULL, NULL};
   30.88 +    CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP,
   30.89 +                                        kCFSocketAcceptCallBack, &TCPListenerAcceptCallBack, &socketCtxt);
   30.90 +    if( ! socket ) 
   30.91 +        return getLastCFSocketError(error);   // CFSocketCreate leaves error code in errno
   30.92 +    
   30.93 +    int yes = 1;
   30.94 +    setsockopt(CFSocketGetNative(socket), SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
   30.95 +    
   30.96 +    NSData *addressData = [NSData dataWithBytes:address length:address->sa_len];
   30.97 +    if (kCFSocketSuccess != CFSocketSetAddress(socket, (CFDataRef)addressData)) {
   30.98 +        getLastCFSocketError(error);
   30.99 +        return closeSocket(socket);
  30.100 +    }
  30.101 +    // set up the run loop source for the socket
  30.102 +    CFRunLoopRef cfrl = CFRunLoopGetCurrent();
  30.103 +    CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
  30.104 +    CFRunLoopAddSource(cfrl, source, kCFRunLoopCommonModes);
  30.105 +    CFRelease(source);
  30.106 +    return socket;
  30.107 +}
  30.108 +
  30.109 +- (BOOL) _failedToOpen: (NSError*)error
  30.110 +{
  30.111 +    LogTo(TCP,@"%@ failed to open: %@",self,error);
  30.112 +    [self tellDelegate: @selector(listener:failedToOpen:) withObject: error];
  30.113 +    return NO;
  30.114 +}
  30.115 +
  30.116 +
  30.117 +- (BOOL) open: (NSError**)outError 
  30.118 +{
  30.119 +    // set up the IPv4 endpoint; if port is 0, this will cause the kernel to choose a port for us
  30.120 +    do{
  30.121 +        struct sockaddr_in addr4;
  30.122 +        memset(&addr4, 0, sizeof(addr4));
  30.123 +        addr4.sin_len = sizeof(addr4);
  30.124 +        addr4.sin_family = AF_INET;
  30.125 +        addr4.sin_port = htons(_port);
  30.126 +        addr4.sin_addr.s_addr = htonl(INADDR_ANY);
  30.127 +
  30.128 +        NSError *error;
  30.129 +        _ipv4socket = [self _openProtocol: PF_INET address: (struct sockaddr*)&addr4 error: &error];
  30.130 +        if( ! _ipv4socket ) {
  30.131 +            if( error.code==EADDRINUSE && _pickAvailablePort && _port<0xFFFF ) {
  30.132 +                LogTo(BLIPVerbose,@"%@: port busy, trying %hu...",self,_port+1);
  30.133 +                self.port++;        // try the next port
  30.134 +            } else {
  30.135 +                if( outError ) *outError = error;
  30.136 +                return [self _failedToOpen: error];
  30.137 +            }
  30.138 +        }
  30.139 +    }while( ! _ipv4socket );
  30.140 +    
  30.141 +    if (0 == _port) {
  30.142 +        // now that the binding was successful, we get the port number 
  30.143 +        NSData *addr = [(NSData *)CFSocketCopyAddress(_ipv4socket) autorelease];
  30.144 +        const struct sockaddr_in *addr4 = addr.bytes;
  30.145 +        self.port = ntohs(addr4->sin_port);
  30.146 +    }
  30.147 +    
  30.148 +    if( _useIPv6 ) {
  30.149 +        // set up the IPv6 endpoint
  30.150 +        struct sockaddr_in6 addr6;
  30.151 +        memset(&addr6, 0, sizeof(addr6));
  30.152 +        addr6.sin6_len = sizeof(addr6);
  30.153 +        addr6.sin6_family = AF_INET6;
  30.154 +        addr6.sin6_port = htons(_port);
  30.155 +        memcpy(&(addr6.sin6_addr), &in6addr_any, sizeof(addr6.sin6_addr));
  30.156 +        
  30.157 +        _ipv6socket = [self _openProtocol: PF_INET6 address: (struct sockaddr*)&addr6 error: outError];
  30.158 +        if( ! _ipv6socket ) {
  30.159 +            _ipv4socket = closeSocket(_ipv4socket);
  30.160 +            return [self _failedToOpen: *outError];
  30.161 +        }
  30.162 +    }
  30.163 +    
  30.164 +    // Open Bonjour:
  30.165 +    if( _bonjourServiceType && !_netService) {
  30.166 +        // instantiate the NSNetService object that will advertise on our behalf.
  30.167 +        _netService = [[NSNetService alloc] initWithDomain: @"local." 
  30.168 +                                                      type: _bonjourServiceType
  30.169 +                                                      name: _bonjourServiceName ?:@""
  30.170 +                                                      port: _port];
  30.171 +        if( _netService ) {
  30.172 +            [_netService setDelegate:self];
  30.173 +            if( _bonjourTXTRecord )
  30.174 +                [self _updateTXTRecord];
  30.175 +            [_netService publish];
  30.176 +        } else {
  30.177 +            self.bonjourError = -1;
  30.178 +            Warn(@"%@: Failed to create NSNetService",self);
  30.179 +        }
  30.180 +    }
  30.181 +
  30.182 +    LogTo(TCP,@"%@ is open",self);
  30.183 +    [self tellDelegate: @selector(listenerDidOpen:) withObject: nil];
  30.184 +    return YES;
  30.185 +}
  30.186 +
  30.187 +- (BOOL) open
  30.188 +{
  30.189 +    return [self open: nil];
  30.190 +}
  30.191 +    
  30.192 +
  30.193 +- (void) close 
  30.194 +{
  30.195 +    if( _ipv4socket ) {
  30.196 +        if( _netService ) {
  30.197 +            [_netService stop];
  30.198 +            [_netService release];
  30.199 +            _netService = nil;
  30.200 +            self.bonjourPublished = NO;
  30.201 +        }
  30.202 +        self.bonjourError = 0;
  30.203 +
  30.204 +        _ipv4socket = closeSocket(_ipv4socket);
  30.205 +        _ipv6socket = closeSocket(_ipv6socket);
  30.206 +
  30.207 +        LogTo(BLIP,@"%@ is closed",self);
  30.208 +        [self tellDelegate: @selector(listenerDidClose:) withObject: nil];
  30.209 +    }
  30.210 +}
  30.211 +
  30.212 +
  30.213 +- (BOOL) isOpen
  30.214 +{
  30.215 +    return _ipv4socket != NULL;
  30.216 +}
  30.217 +
  30.218 +
  30.219 +#pragma mark -
  30.220 +#pragma mark ACCEPTING CONNECTIONS:
  30.221 +
  30.222 +
  30.223 +@synthesize connectionClass = _connectionClass;
  30.224 +
  30.225 +
  30.226 +- (BOOL) acceptConnection: (CFSocketNativeHandle)socket
  30.227 +{
  30.228 +    IPAddress *addr = [IPAddress addressOfSocket: socket];
  30.229 +    if( ! addr )
  30.230 +        return NO;
  30.231 +    if( [_delegate respondsToSelector: @selector(listener:shouldAcceptConnectionFrom:)]
  30.232 +       && ! [_delegate listener: self shouldAcceptConnectionFrom: addr] )
  30.233 +        return NO;
  30.234 +    
  30.235 +    Assert(_connectionClass);
  30.236 +    TCPConnection *conn = [[self.connectionClass alloc] initIncomingFromSocket: socket
  30.237 +                                                                      listener: self];
  30.238 +    if( ! conn )
  30.239 +        return NO;
  30.240 +    
  30.241 +    if( _sslProperties ) {
  30.242 +        conn.SSLProperties = _sslProperties;
  30.243 +        [conn setSSLProperty: $true forKey: (id)kCFStreamSSLIsServer];
  30.244 +    }
  30.245 +    [conn open];
  30.246 +    [self tellDelegate: @selector(listener:didAcceptConnection:) withObject: conn];
  30.247 +    return YES;
  30.248 +}
  30.249 +
  30.250 +
  30.251 +static void TCPListenerAcceptCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) 
  30.252 +{
  30.253 +    TCPListener *server = (TCPListener *)info;
  30.254 +    if (kCFSocketAcceptCallBack == type) { 
  30.255 +        CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle *)data;
  30.256 +        BOOL accepted = NO;
  30.257 +        @try{
  30.258 +            accepted = [server acceptConnection: nativeSocketHandle];
  30.259 +        }catchAndReport(@"TCPListenerAcceptCallBack");
  30.260 +        if( ! accepted )
  30.261 +            close(nativeSocketHandle);
  30.262 +    }
  30.263 +}
  30.264 +
  30.265 +
  30.266 +#pragma mark -
  30.267 +#pragma mark BONJOUR:
  30.268 +
  30.269 +
  30.270 +- (NSDictionary*) bonjourTXTRecord
  30.271 +{
  30.272 +    return _bonjourTXTRecord;
  30.273 +}
  30.274 +
  30.275 +- (void) setBonjourTXTRecord: (NSDictionary*)txt
  30.276 +{
  30.277 +    if( ifSetObj(&_bonjourTXTRecord,txt) )
  30.278 +        [self _updateTXTRecord];
  30.279 +}
  30.280 +
  30.281 +- (void) _updateTXTRecord
  30.282 +{
  30.283 +    if( _netService ) {
  30.284 +        NSData *data;
  30.285 +        if( _bonjourTXTRecord ) {
  30.286 +            data = [NSNetService dataFromTXTRecordDictionary: _bonjourTXTRecord];
  30.287 +            if( data )
  30.288 +                LogTo(BLIP,@"%@: Set %u-byte TXT record", self,data.length);
  30.289 +            else
  30.290 +                Warn(@"TCPListener: Couldn't convert txt dict to data: %@",_bonjourTXTRecord);
  30.291 +        } else
  30.292 +            data = nil;
  30.293 +        [_netService setTXTRecordData: data];
  30.294 +    }
  30.295 +}
  30.296 +
  30.297 +
  30.298 +- (void)netServiceWillPublish:(NSNetService *)sender
  30.299 +{
  30.300 +    LogTo(BLIP,@"%@: Advertising %@",self,sender);
  30.301 +    self.bonjourPublished = YES;
  30.302 +}
  30.303 +
  30.304 +- (void)netService:(NSNetService *)sender didNotPublish:(NSDictionary *)errorDict
  30.305 +{
  30.306 +    self.bonjourError = [[errorDict objectForKey:NSNetServicesErrorCode] intValue];
  30.307 +    LogTo(BLIP,@"%@: Failed to advertise %@: error %i",self,sender,self.bonjourError);
  30.308 +    [_netService release];
  30.309 +    _netService = nil;
  30.310 +}
  30.311 +
  30.312 +- (void)netServiceDidStop:(NSNetService *)sender
  30.313 +{
  30.314 +    LogTo(BLIP,@"%@: Stopped advertising %@",self,sender);
  30.315 +    self.bonjourPublished = NO;
  30.316 +    [_netService release];
  30.317 +    _netService = nil;
  30.318 +}
  30.319 +
  30.320 +
  30.321 +@end
  30.322 +
  30.323 +
  30.324 +
  30.325 +/*
  30.326 + Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
  30.327 + 
  30.328 + Redistribution and use in source and binary forms, with or without modification, are permitted
  30.329 + provided that the following conditions are met:
  30.330 + 
  30.331 + * Redistributions of source code must retain the above copyright notice, this list of conditions
  30.332 + and the following disclaimer.
  30.333 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
  30.334 + and the following disclaimer in the documentation and/or other materials provided with the
  30.335 + distribution.
  30.336 + 
  30.337 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  30.338 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
  30.339 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
  30.340 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  30.341 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  30.342 +  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  30.343 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
  30.344 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30.345 + */
    31.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    31.2 +++ b/TCP/TCPStream.h	Fri May 23 17:37:36 2008 -0700
    31.3 @@ -0,0 +1,85 @@
    31.4 +//
    31.5 +//  TCPStream.h
    31.6 +//  MYNetwork
    31.7 +//
    31.8 +//  Created by Jens Alfke on 5/10/08.
    31.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   31.10 +//
   31.11 +
   31.12 +#import <Foundation/Foundation.h>
   31.13 +@class TCPConnection, TCPWriter;
   31.14 +
   31.15 +
   31.16 +/** INTERNAL abstract superclass for data streams, used by TCPConnection. */
   31.17 +@interface TCPStream : NSObject 
   31.18 +{
   31.19 +    TCPConnection *_conn;
   31.20 +    NSStream *_stream;
   31.21 +    BOOL _shouldClose;
   31.22 +}
   31.23 +
   31.24 +- (id) initWithConnection: (TCPConnection*)conn stream: (NSStream*)stream;
   31.25 +
   31.26 +/** The connection's security level as reported by the underlying CFStream. */
   31.27 +@property (readonly) NSString *securityLevel;
   31.28 +
   31.29 +/** The SSL property dictionary for the CFStream. */
   31.30 +@property (copy) NSDictionary* SSLProperties;
   31.31 +
   31.32 +/** The SSL certificate(s) of the peer, if any. */
   31.33 +@property (readonly) NSArray *peerSSLCerts;
   31.34 +
   31.35 +/** Opens the stream. */
   31.36 +- (void) open;
   31.37 +
   31.38 +/** Disconnects abruptly. */
   31.39 +- (void) disconnect;
   31.40 +
   31.41 +/** Closes the stream politely, waiting until there's no data pending. */
   31.42 +- (BOOL) close;
   31.43 +
   31.44 +/** Is the stream open? */
   31.45 +@property (readonly) BOOL isOpen;
   31.46 +
   31.47 +/** Does the stream have pending data to read or write, that prevents it from closing? */
   31.48 +@property (readonly) BOOL isBusy;
   31.49 +
   31.50 +/** Generic accessor for CFStream/NSStream properties. */
   31.51 +- (id) propertyForKey: (CFStringRef)cfStreamProperty;
   31.52 +
   31.53 +/** Generic accessor for CFStream/NSStream properties. */
   31.54 +- (void) setProperty: (id)value forKey: (CFStringRef)cfStreamProperty;
   31.55 +
   31.56 +@end
   31.57 +
   31.58 +
   31.59 +/** Input stream for a TCPConnection. */
   31.60 +@interface TCPReader : TCPStream
   31.61 +/** The connection's TCPWriter. */
   31.62 +@property (readonly) TCPWriter *writer;
   31.63 +@end
   31.64 +
   31.65 +
   31.66 +
   31.67 +@interface TCPStream (Protected)
   31.68 +/** Called when the stream opens. */
   31.69 +- (void) _opened;
   31.70 +
   31.71 +/** Called when the stream has bytes available to read. */
   31.72 +- (void) _canRead;
   31.73 +
   31.74 +/** Called when the stream has space available in its output buffer to write to. */
   31.75 +- (void) _canWrite;
   31.76 + 
   31.77 +/** Called when the underlying stream closes due to the socket closing. */
   31.78 +- (void) _gotEOF;
   31.79 +
   31.80 +/** Call this if a read/write call returns -1 to report an error;
   31.81 +    it will look up the error from the NSStream and call gotError: with it.
   31.82 +    This method always returns NO, so you can "return [self _gotError]". */
   31.83 +- (BOOL) _gotError;
   31.84 +
   31.85 +/** Signals a fatal error to the TCPConnection.
   31.86 +    This method always returns NO, so you can "return [self _gotError: e]". */
   31.87 +- (BOOL) _gotError: (NSError*)error;
   31.88 +@end
    32.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    32.2 +++ b/TCP/TCPStream.m	Fri May 23 17:37:36 2008 -0700
    32.3 @@ -0,0 +1,290 @@
    32.4 +//
    32.5 +//  TCPStream.m
    32.6 +//  MYNetwork
    32.7 +//
    32.8 +//  Created by Jens Alfke on 5/10/08.
    32.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   32.10 +//
   32.11 +
   32.12 +#import "TCPStream.h"
   32.13 +#import "TCP_Internal.h"
   32.14 +
   32.15 +
   32.16 +extern const CFStringRef _kCFStreamPropertySSLClientSideAuthentication; // in CFNetwork
   32.17 +
   32.18 +static NSError* fixStreamError( NSError *error );
   32.19 +
   32.20 +
   32.21 +@implementation TCPStream
   32.22 +
   32.23 +
   32.24 +- (id) initWithConnection: (TCPConnection*)conn stream: (NSStream*)stream
   32.25 +{
   32.26 +    self = [super init];
   32.27 +    if (self != nil) {
   32.28 +        _conn = [conn retain];
   32.29 +        _stream = [stream retain];
   32.30 +        _stream.delegate = self;
   32.31 +        [_stream scheduleInRunLoop: [NSRunLoop currentRunLoop] forMode: NSRunLoopCommonModes];
   32.32 +        LogTo(TCPVerbose,@"%@ initialized; status=%i", self,_stream.streamStatus);
   32.33 +    }
   32.34 +    return self;
   32.35 +}
   32.36 +
   32.37 +
   32.38 +- (void) dealloc
   32.39 +{
   32.40 +    LogTo(TCP,@"DEALLOC %@",self);
   32.41 +    if( _stream )
   32.42 +        [self disconnect];
   32.43 +    [super dealloc];
   32.44 +}
   32.45 +
   32.46 +
   32.47 +- (id) propertyForKey: (CFStringRef)cfStreamProperty
   32.48 +{
   32.49 +    return nil; // abstract -- overridden by TCPReader and TCPWriter
   32.50 +}
   32.51 +
   32.52 +- (void) setProperty: (id)value forKey: (CFStringRef)cfStreamProperty
   32.53 +{ // abstract -- overridden by TCPReader and TCPWriter
   32.54 +}
   32.55 +
   32.56 +
   32.57 +#pragma mark -
   32.58 +#pragma mark SSL:
   32.59 +
   32.60 +
   32.61 +- (NSString*) securityLevel                 {return [_stream propertyForKey: NSStreamSocketSecurityLevelKey];}
   32.62 +
   32.63 +- (NSDictionary*) SSLProperties             {return [self propertyForKey: kCFStreamPropertySSLSettings];}
   32.64 +
   32.65 +- (void) setSSLProperties: (NSDictionary*)p
   32.66 +{
   32.67 +    LogTo(TCPVerbose,@"%@ SSL settings := %@",self,p);
   32.68 +    [self setProperty: p forKey: kCFStreamPropertySSLSettings];
   32.69 +    
   32.70 +    id clientAuth = [p objectForKey: kTCPPropertySSLClientSideAuthentication];
   32.71 +    if( clientAuth )
   32.72 +        [self setProperty: clientAuth forKey: _kCFStreamPropertySSLClientSideAuthentication];
   32.73 +}
   32.74 +
   32.75 +- (NSArray*) peerSSLCerts
   32.76 +{
   32.77 +    Assert(self.isOpen);
   32.78 +    return [self propertyForKey: kCFStreamPropertySSLPeerCertificates];
   32.79 +}
   32.80 +
   32.81 +
   32.82 +#pragma mark -
   32.83 +#pragma mark OPENING/CLOSING:
   32.84 +
   32.85 +
   32.86 +- (void) open
   32.87 +{
   32.88 +    Assert(_stream);
   32.89 +    AssertEq(_stream.streamStatus,NSStreamStatusNotOpen);
   32.90 +    LogTo(TCP,@"Opening %@",self);
   32.91 +    [_stream open];
   32.92 +}
   32.93 +
   32.94 +
   32.95 +- (void) disconnect
   32.96 +{
   32.97 +    if( _stream ) {
   32.98 +        LogTo(TCP,@"Disconnect %@",self);
   32.99 +        _stream.delegate = nil;
  32.100 +        [_stream close];
  32.101 +        setObj(&_stream,nil);
  32.102 +    }
  32.103 +    setObj(&_conn,nil);
  32.104 +}
  32.105 +
  32.106 +
  32.107 +- (BOOL) close
  32.108 +{
  32.109 +    if( self.isBusy ) {
  32.110 +        _shouldClose = YES;
  32.111 +        return NO;
  32.112 +    } else {
  32.113 +        LogTo(TCP,@"Closing %@",self);
  32.114 +        [[self retain] autorelease];    // don't let myself be dealloced in the midst of this
  32.115 +        [_conn _streamClosed: self];    // have to do this before disconnect
  32.116 +        [self disconnect];
  32.117 +        return YES;
  32.118 +    }
  32.119 +}
  32.120 +
  32.121 +
  32.122 +- (BOOL) isOpen
  32.123 +{
  32.124 +    NSStreamStatus status = _stream.streamStatus;
  32.125 +    return status >= NSStreamStatusOpen && status < NSStreamStatusAtEnd;
  32.126 +}
  32.127 +
  32.128 +- (BOOL) isBusy
  32.129 +{
  32.130 +    return NO;  // abstract
  32.131 +}
  32.132 +
  32.133 +
  32.134 +- (void) _opened
  32.135 +{
  32.136 +    [_conn _streamOpened: self];
  32.137 +}
  32.138 +
  32.139 +- (void) _canRead
  32.140 +{
  32.141 +    // abstract
  32.142 +}
  32.143 +
  32.144 +- (void) _canWrite
  32.145 +{
  32.146 +    // abstract
  32.147 +}
  32.148 +
  32.149 +- (void) _gotEOF
  32.150 +{
  32.151 +    if( self.isBusy )
  32.152 +        [self _gotError: [NSError errorWithDomain: NSPOSIXErrorDomain code: ECONNRESET userInfo: nil]];
  32.153 +    else {
  32.154 +        [self retain];
  32.155 +        [_conn _streamGotEOF: self];
  32.156 +        [self disconnect];
  32.157 +        [self release];
  32.158 +    }
  32.159 +}
  32.160 +
  32.161 +- (BOOL) _gotError: (NSError*)error
  32.162 +{
  32.163 +    [_conn _stream: self gotError: fixStreamError(error)];
  32.164 +    return NO;
  32.165 +}
  32.166 +
  32.167 +- (BOOL) _gotError
  32.168 +{
  32.169 +    NSError *error = _stream.streamError;
  32.170 +    if( ! error )
  32.171 +        error = [NSError errorWithDomain: NSPOSIXErrorDomain code: EIO userInfo: nil]; //fallback
  32.172 +    return [self _gotError: error];
  32.173 +}
  32.174 +
  32.175 +
  32.176 +- (void) stream: (NSStream*)stream handleEvent: (NSStreamEvent)streamEvent 
  32.177 +{
  32.178 +    [[self retain] autorelease];
  32.179 +    switch(streamEvent) {
  32.180 +        case NSStreamEventOpenCompleted:
  32.181 +            LogTo(TCPVerbose,@"%@ opened",self);
  32.182 +            [self _opened];
  32.183 +            break;
  32.184 +        case NSStreamEventHasBytesAvailable:
  32.185 +            if( ! [_conn _streamPeerCertAvailable: self] )
  32.186 +                return;
  32.187 +            LogTo(TCPVerbose,@"%@ can read",self);
  32.188 +            [self _canRead];
  32.189 +            break;
  32.190 +        case NSStreamEventHasSpaceAvailable:
  32.191 +            if( ! [_conn _streamPeerCertAvailable: self] )
  32.192 +                return;
  32.193 +            LogTo(TCPVerbose,@"%@ can write",self);
  32.194 +            [self _canWrite];
  32.195 +            break;
  32.196 +        case NSStreamEventErrorOccurred:
  32.197 +            LogTo(TCPVerbose,@"%@ got error",self);
  32.198 +            [self _gotError];
  32.199 +            break;
  32.200 +        case NSStreamEventEndEncountered:
  32.201 +            LogTo(TCPVerbose,@"%@ got EOF",self);
  32.202 +            [self _gotEOF];
  32.203 +            break;
  32.204 +        default:
  32.205 +            Warn(@"%@: unknown NSStreamEvent %i",self,streamEvent);
  32.206 +            break;
  32.207 +    }
  32.208 +    
  32.209 +    // If I was previously asked to close, try again in case I'm no longer busy
  32.210 +    if( _shouldClose )
  32.211 +        [self close];
  32.212 +}
  32.213 +
  32.214 +
  32.215 +@end
  32.216 +
  32.217 +
  32.218 +
  32.219 +
  32.220 +@implementation TCPReader
  32.221 +
  32.222 +
  32.223 +- (TCPWriter*) writer
  32.224 +{
  32.225 +    return _conn.writer;
  32.226 +}
  32.227 +
  32.228 +
  32.229 +- (id) propertyForKey: (CFStringRef)cfStreamProperty
  32.230 +{
  32.231 +    CFTypeRef value = CFReadStreamCopyProperty((CFReadStreamRef)_stream,cfStreamProperty);
  32.232 +    return [(id)CFMakeCollectable(value) autorelease];
  32.233 +}
  32.234 +
  32.235 +- (void) setProperty: (id)value forKey: (CFStringRef)cfStreamProperty
  32.236 +{
  32.237 +    if( ! CFReadStreamSetProperty((CFReadStreamRef)_stream,cfStreamProperty,(CFTypeRef)value) )
  32.238 +        Warn(@"%@ didn't accept property '%@'", self,cfStreamProperty);
  32.239 +}
  32.240 +
  32.241 +
  32.242 +@end
  32.243 +
  32.244 +
  32.245 +
  32.246 +
  32.247 +static NSError* fixStreamError( NSError *error )
  32.248 +{
  32.249 +    // NSStream incorrectly returns SSL errors without the correct error domain:
  32.250 +    if( $equal(error.domain,@"NSUnknownErrorDomain") ) {
  32.251 +        int code = error.code;
  32.252 +        if( -9899 <= code && code <= -9800 ) {
  32.253 +            NSMutableDictionary *userInfo = error.userInfo.mutableCopy;
  32.254 +            if( ! [userInfo objectForKey: NSLocalizedFailureReasonErrorKey] ) {
  32.255 +                // look up error message:
  32.256 +                NSBundle *secBundle = [NSBundle bundleWithPath: @"/System/Library/Frameworks/Security.framework"];
  32.257 +                NSString *message = [secBundle localizedStringForKey: $sprintf(@"%i",code)
  32.258 +                                                               value: nil
  32.259 +                                                               table: @"SecErrorMessages"];
  32.260 +                if( message ) {
  32.261 +                    if( ! userInfo ) userInfo = $mdict();
  32.262 +                    [userInfo setObject: message forKey: NSLocalizedFailureReasonErrorKey];
  32.263 +                }
  32.264 +            }
  32.265 +            error = [NSError errorWithDomain: NSStreamSocketSSLErrorDomain
  32.266 +                                        code: code userInfo: userInfo];
  32.267 +        } else
  32.268 +            Warn(@"NSStream returned error with unknown domain: %@",error);
  32.269 +    }
  32.270 +    return error;
  32.271 +}
  32.272 +
  32.273 +/*
  32.274 + Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
  32.275 + 
  32.276 + Redistribution and use in source and binary forms, with or without modification, are permitted
  32.277 + provided that the following conditions are met:
  32.278 + 
  32.279 + * Redistributions of source code must retain the above copyright notice, this list of conditions
  32.280 + and the following disclaimer.
  32.281 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
  32.282 + and the following disclaimer in the documentation and/or other materials provided with the
  32.283 + distribution.
  32.284 + 
  32.285 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  32.286 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
  32.287 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
  32.288 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  32.289 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  32.290 +  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  32.291 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
  32.292 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32.293 + */
    33.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    33.2 +++ b/TCP/TCPWriter.h	Fri May 23 17:37:36 2008 -0700
    33.3 @@ -0,0 +1,34 @@
    33.4 +//
    33.5 +//  TCPWriter.h
    33.6 +//  MYNetwork
    33.7 +//
    33.8 +//  Created by Jens Alfke on 5/10/08.
    33.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   33.10 +//
   33.11 +
   33.12 +#import "TCPStream.h"
   33.13 +
   33.14 +
   33.15 +/** INTERNAL class that writes a queue of arbitrary data blobs to the socket. */
   33.16 +@interface TCPWriter : TCPStream 
   33.17 +{
   33.18 +    NSMutableArray *_queue;
   33.19 +    NSData *_currentData;
   33.20 +    SInt32 _currentDataPos;
   33.21 +}
   33.22 +
   33.23 +/** The connection's TCPReader. */
   33.24 +@property (readonly) TCPReader *reader;
   33.25 +
   33.26 +/** Schedules data to be written to the socket.
   33.27 +    Always returns immediately; the bytes won't actually be sent until there's room. */
   33.28 +- (void) writeData: (NSData*)data;
   33.29 +
   33.30 +//protected:
   33.31 +
   33.32 +/** Will be called when the internal queue of data to be written is empty.
   33.33 +    Subclasses should override this and call -writeData: to refill the queue,
   33.34 +    if possible. */
   33.35 +- (void) queueIsEmpty;
   33.36 +
   33.37 +@end
    34.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    34.2 +++ b/TCP/TCPWriter.m	Fri May 23 17:37:36 2008 -0700
    34.3 @@ -0,0 +1,115 @@
    34.4 +//
    34.5 +//  TCPWriter.m
    34.6 +//  MYNetwork
    34.7 +//
    34.8 +//  Created by Jens Alfke on 5/10/08.
    34.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   34.10 +//
   34.11 +
   34.12 +#import "TCPWriter.h"
   34.13 +#import "TCP_Internal.h"
   34.14 +
   34.15 +
   34.16 +@implementation TCPWriter
   34.17 +
   34.18 +
   34.19 +- (void) dealloc
   34.20 +{
   34.21 +    [_queue release];
   34.22 +    [_currentData release];
   34.23 +    [super dealloc];
   34.24 +}
   34.25 +
   34.26 +
   34.27 +- (TCPReader*) reader
   34.28 +{
   34.29 +    return _conn.reader;
   34.30 +}
   34.31 +
   34.32 +
   34.33 +- (id) propertyForKey: (CFStringRef)cfStreamProperty
   34.34 +{
   34.35 +    CFTypeRef value = CFWriteStreamCopyProperty((CFWriteStreamRef)_stream,cfStreamProperty);
   34.36 +    return [(id)CFMakeCollectable(value) autorelease];
   34.37 +}
   34.38 +
   34.39 +- (void) setProperty: (id)value forKey: (CFStringRef)cfStreamProperty
   34.40 +{
   34.41 +    if( ! CFWriteStreamSetProperty((CFWriteStreamRef)_stream,cfStreamProperty,(CFTypeRef)value) )
   34.42 +        Warn(@"%@ didn't accept property '%@'", self,cfStreamProperty);
   34.43 +}
   34.44 +
   34.45 +
   34.46 +- (BOOL) isBusy
   34.47 +{
   34.48 +    return _currentData || _queue.count > 0;
   34.49 +}
   34.50 +
   34.51 +
   34.52 +- (void) writeData: (NSData*)data
   34.53 +{
   34.54 +    if( !_queue )
   34.55 +        _queue = [[NSMutableArray alloc] init];
   34.56 +    [_queue addObject: data];
   34.57 +    if( _queue.count==1 && ((NSOutputStream*)_stream).hasSpaceAvailable )
   34.58 +        [self _canWrite];
   34.59 +}
   34.60 +
   34.61 +
   34.62 +- (void) _canWrite
   34.63 +{
   34.64 +    if( ! _currentData ) {
   34.65 +        if( _queue.count==0 ) {
   34.66 +            [self queueIsEmpty]; // this may call -writeData, which will call _canWrite again
   34.67 +            return;
   34.68 +        }
   34.69 +        _currentData = [[_queue objectAtIndex: 0] retain];
   34.70 +        _currentDataPos = 0;
   34.71 +        [_queue removeObjectAtIndex: 0];
   34.72 +    }
   34.73 +    
   34.74 +    const uint8_t* src = _currentData.bytes;
   34.75 +    src += _currentDataPos;
   34.76 +    NSInteger len = _currentData.length - _currentDataPos;
   34.77 +    NSInteger written = [(NSOutputStream*)_stream write: src maxLength: len];
   34.78 +    if( written < 0 )
   34.79 +        [self _gotError];
   34.80 +    else if( written < len ) {
   34.81 +        LogTo(TCPVerbose,@"%@ wrote %i bytes (of %u)", self,written,len);
   34.82 +        _currentDataPos += written;
   34.83 +    } else {
   34.84 +        LogTo(TCPVerbose,@"%@ wrote %i bytes", self,written);
   34.85 +        setObj(&_currentData,nil);
   34.86 +    }
   34.87 +}
   34.88 +
   34.89 +
   34.90 +- (void) queueIsEmpty
   34.91 +{
   34.92 +}
   34.93 +
   34.94 +
   34.95 +@end
   34.96 +
   34.97 +
   34.98 +/*
   34.99 + Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
  34.100 + 
  34.101 + Redistribution and use in source and binary forms, with or without modification, are permitted
  34.102 + provided that the following conditions are met:
  34.103 + 
  34.104 + * Redistributions of source code must retain the above copyright notice, this list of conditions
  34.105 + and the following disclaimer.
  34.106 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
  34.107 + and the following disclaimer in the documentation and/or other materials provided with the
  34.108 + distribution.
  34.109 + 
  34.110 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  34.111 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
  34.112 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
  34.113 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  34.114 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  34.115 +  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  34.116 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
  34.117 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  34.118 + */
    35.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    35.2 +++ b/TCP/TCP_Internal.h	Fri May 23 17:37:36 2008 -0700
    35.3 @@ -0,0 +1,25 @@
    35.4 +//
    35.5 +//  TCP_Internal.h
    35.6 +//  MYNetwork
    35.7 +//
    35.8 +//  Created by Jens Alfke on 5/18/08.
    35.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   35.10 +//
   35.11 +
   35.12 +
   35.13 +#import "TCPWriter.h"
   35.14 +#import "TCPConnection.h"
   35.15 +#import "TCPListener.h"
   35.16 +
   35.17 +/* Private declarations and APIs for TCP client/server implementation. */
   35.18 +
   35.19 +
   35.20 +
   35.21 +@interface TCPConnection ()
   35.22 +- (void) _setStreamProperty: (id)value forKey: (NSString*)key;
   35.23 +- (void) _streamOpened: (TCPStream*)stream;
   35.24 +- (BOOL) _streamPeerCertAvailable: (TCPStream*)stream;
   35.25 +- (void) _stream: (TCPStream*)stream gotError: (NSError*)error;
   35.26 +- (void) _streamGotEOF: (TCPStream*)stream;
   35.27 +- (void) _streamClosed: (TCPStream*)stream;
   35.28 +@end