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