1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/BLIP/BLIPMessage.m Fri May 23 17:37:36 2008 -0700
1.3 @@ -0,0 +1,335 @@
1.4 +//
1.5 +// BLIPMessage.m
1.6 +// MYNetwork
1.7 +//
1.8 +// Created by Jens Alfke on 5/10/08.
1.9 +// Copyright 2008 Jens Alfke. All rights reserved.
1.10 +//
1.11 +
1.12 +#import "BLIPMessage.h"
1.13 +#import "BLIP_Internal.h"
1.14 +#import "BLIPReader.h"
1.15 +#import "BLIPWriter.h"
1.16 +
1.17 +#import "ExceptionUtils.h"
1.18 +#import "Target.h"
1.19 +
1.20 +// From Google Toolbox For Mac <http://code.google.com/p/google-toolbox-for-mac/>
1.21 +#import "GTMNSData+zlib.h"
1.22 +
1.23 +
1.24 +@implementation BLIPMessage
1.25 +
1.26 +
1.27 +- (id) _initWithConnection: (BLIPConnection*)connection
1.28 + isMine: (BOOL)isMine
1.29 + flags: (BLIPMessageFlags)flags
1.30 + number: (UInt32)msgNo
1.31 + body: (NSData*)body
1.32 +{
1.33 + self = [super init];
1.34 + if (self != nil) {
1.35 + _connection = [connection retain];
1.36 + _isMine = isMine;
1.37 + _flags = flags;
1.38 + _number = msgNo;
1.39 + if( isMine ) {
1.40 + _body = body.copy;
1.41 + _properties = [[BLIPMutableProperties alloc] init];
1.42 + _propertiesAvailable = YES;
1.43 + } else {
1.44 + _encodedBody = body.mutableCopy;
1.45 + }
1.46 + LogTo(BLIPVerbose,@"INIT %@",self);
1.47 + }
1.48 + return self;
1.49 +}
1.50 +
1.51 +- (void) dealloc
1.52 +{
1.53 + LogTo(BLIPVerbose,@"DEALLOC %@",self);
1.54 + [_properties release];
1.55 + [_encodedBody release];
1.56 + [_mutableBody release];
1.57 + [_body release];
1.58 + [_connection release];
1.59 + [super dealloc];
1.60 +}
1.61 +
1.62 +
1.63 +- (NSString*) description
1.64 +{
1.65 + NSUInteger length = (_body.length ?: _mutableBody.length) ?: _encodedBody.length;
1.66 + NSMutableString *desc = [NSMutableString stringWithFormat: @"%@[#%u, %u bytes",
1.67 + self.class,_number, length];
1.68 + if( _flags & kBLIP_Compressed ) {
1.69 + if( _encodedBody && _encodedBody.length != length )
1.70 + [desc appendFormat: @" (%u gzipped)", _encodedBody.length];
1.71 + else
1.72 + [desc appendString: @", gzipped"];
1.73 + }
1.74 + if( _flags & kBLIP_Urgent )
1.75 + [desc appendString: @", urgent"];
1.76 + if( _flags & kBLIP_NoReply )
1.77 + [desc appendString: @", noreply"];
1.78 + [desc appendString: @"]"];
1.79 + return desc;
1.80 +}
1.81 +
1.82 +- (NSString*) descriptionWithProperties
1.83 +{
1.84 + NSMutableString *desc = (NSMutableString*)self.description;
1.85 + [desc appendFormat: @" %@", self.properties.allProperties];
1.86 + return desc;
1.87 +}
1.88 +
1.89 +
1.90 +#pragma mark -
1.91 +#pragma mark PROPERTIES & METADATA:
1.92 +
1.93 +
1.94 +@synthesize connection=_connection, number=_number, isMine=_isMine, isMutable=_isMutable,
1.95 + _bytesWritten, sent=_sent, propertiesAvailable=_propertiesAvailable, complete=_complete;
1.96 +
1.97 +
1.98 +- (void) _setFlag: (BLIPMessageFlags)flag value: (BOOL)value
1.99 +{
1.100 + Assert(_isMine && _isMutable);
1.101 + if( value )
1.102 + _flags |= flag;
1.103 + else
1.104 + _flags &= ~flag;
1.105 +}
1.106 +
1.107 +- (BOOL) compressed {return (_flags & kBLIP_Compressed) != 0;}
1.108 +- (BOOL) urgent {return (_flags & kBLIP_Urgent) != 0;}
1.109 +- (void) setCompressed: (BOOL)compressed {[self _setFlag: kBLIP_Compressed value: compressed];}
1.110 +- (void) setUrgent: (BOOL)high {[self _setFlag: kBLIP_Urgent value: high];}
1.111 +
1.112 +
1.113 +- (NSData*) body
1.114 +{
1.115 + if( ! _body && _isMine )
1.116 + return [[_mutableBody copy] autorelease];
1.117 + else
1.118 + return _body;
1.119 +}
1.120 +
1.121 +- (void) setBody: (NSData*)body
1.122 +{
1.123 + Assert(_isMine && _isMutable);
1.124 + if( _mutableBody )
1.125 + [_mutableBody setData: body];
1.126 + else
1.127 + _mutableBody = [body mutableCopy];
1.128 +}
1.129 +
1.130 +- (void) _addToBody: (NSData*)data
1.131 +{
1.132 + if( data.length ) {
1.133 + if( _mutableBody )
1.134 + [_mutableBody appendData: data];
1.135 + else
1.136 + _mutableBody = [data mutableCopy];
1.137 + setObj(&_body,nil);
1.138 + }
1.139 +}
1.140 +
1.141 +- (void) addToBody: (NSData*)data
1.142 +{
1.143 + Assert(_isMine && _isMutable);
1.144 + [self _addToBody: data];
1.145 +}
1.146 +
1.147 +
1.148 +- (BLIPProperties*) properties
1.149 +{
1.150 + return _properties;
1.151 +}
1.152 +
1.153 +- (BLIPMutableProperties*) mutableProperties
1.154 +{
1.155 + Assert(_isMine && _isMutable);
1.156 + return (BLIPMutableProperties*)_properties;
1.157 +}
1.158 +
1.159 +- (NSString*) valueOfProperty: (NSString*)property
1.160 +{
1.161 + return [_properties valueOfProperty: property];
1.162 +}
1.163 +
1.164 +- (void) setValue: (NSString*)value ofProperty: (NSString*)property
1.165 +{
1.166 + [self.mutableProperties setValue: value ofProperty: property];
1.167 +}
1.168 +
1.169 +- (NSString*) contentType {return [_properties valueOfProperty: @"Content-Type"];}
1.170 +- (void) setContentType: (NSString*)t {[self setValue: t ofProperty: @"Content-Type"];}
1.171 +- (NSString*) profile {return [_properties valueOfProperty: @"Profile"];}
1.172 +- (void) setProfile: (NSString*)p {[self setValue: p ofProperty: @"Profile"];}
1.173 +
1.174 +
1.175 +#pragma mark -
1.176 +#pragma mark I/O:
1.177 +
1.178 +
1.179 +- (void) _encode
1.180 +{
1.181 + Assert(_isMine && _isMutable);
1.182 + _isMutable = NO;
1.183 +
1.184 + BLIPProperties *oldProps = _properties;
1.185 + _properties = [oldProps copy];
1.186 + [oldProps release];
1.187 +
1.188 + _encodedBody = [_properties.encodedData mutableCopy];
1.189 + Assert(_encodedBody.length>=2);
1.190 +
1.191 + NSData *body = _body ?: _mutableBody;
1.192 + NSUInteger length = body.length;
1.193 + if( length > 0 ) {
1.194 + if( self.compressed ) {
1.195 + body = [NSData gtm_dataByGzippingData: body compressionLevel: 5];
1.196 + LogTo(BLIPVerbose,@"Compressed %@ to %u bytes (%.0f%%)", self,body.length,
1.197 + body.length*100.0/length);
1.198 + }
1.199 + [_encodedBody appendData: body];
1.200 + }
1.201 +}
1.202 +
1.203 +
1.204 +- (void) _assignedNumber: (UInt32)number
1.205 +{
1.206 + Assert(_number==0,@"%@ has already been sent",self);
1.207 + _number = number;
1.208 + _isMutable = NO;
1.209 +}
1.210 +
1.211 +
1.212 +- (BOOL) _writeFrameTo: (BLIPWriter*)writer maxSize: (UInt16)maxSize
1.213 +{
1.214 + Assert(_number!=0);
1.215 + Assert(_isMine);
1.216 + Assert(_encodedBody);
1.217 + if( _bytesWritten==0 )
1.218 + LogTo(BLIP,@"Now sending %@",self);
1.219 + ssize_t lengthToWrite = _encodedBody.length - _bytesWritten;
1.220 + if( lengthToWrite <= 0 && _bytesWritten > 0 )
1.221 + return NO; // done
1.222 + Assert(maxSize > sizeof(BLIPFrameHeader));
1.223 + maxSize -= sizeof(BLIPFrameHeader);
1.224 + UInt16 flags = _flags;
1.225 + if( lengthToWrite > maxSize ) {
1.226 + lengthToWrite = maxSize;
1.227 + flags |= kBLIP_MoreComing;
1.228 + LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u", self, _bytesWritten, _bytesWritten+lengthToWrite);
1.229 + } else {
1.230 + flags &= ~kBLIP_MoreComing;
1.231 + LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u (finished)", self, _bytesWritten, _bytesWritten+lengthToWrite);
1.232 + }
1.233 +
1.234 + // First write the frame header:
1.235 + BLIPFrameHeader header = { EndianU32_NtoB(kBLIPFrameHeaderMagicNumber),
1.236 + EndianU32_NtoB(_number),
1.237 + EndianU16_NtoB(flags),
1.238 + EndianU16_NtoB(sizeof(BLIPFrameHeader) + lengthToWrite) };
1.239 +
1.240 + [writer writeData: [NSData dataWithBytes: &header length: sizeof(header)]];
1.241 +
1.242 + // Then write the body:
1.243 + if( lengthToWrite > 0 ) {
1.244 + [writer writeData: [NSData dataWithBytesNoCopy: (UInt8*)_encodedBody.bytes + _bytesWritten
1.245 + length: lengthToWrite
1.246 + freeWhenDone: NO]];
1.247 + _bytesWritten += lengthToWrite;
1.248 + }
1.249 + return (flags & kBLIP_MoreComing) != 0;
1.250 +}
1.251 +
1.252 +
1.253 +- (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body
1.254 +{
1.255 + Assert(!_isMine);
1.256 + AssertEq(header->number,_number);
1.257 + Assert(_flags & kBLIP_MoreComing);
1.258 +
1.259 + BLIPMessageType frameType = (header->flags & kBLIP_TypeMask), curType = (_flags & kBLIP_TypeMask);
1.260 + if( frameType != curType ) {
1.261 + Assert(curType==kBLIP_RPY && frameType==kBLIP_ERR && _mutableBody.length==0,
1.262 + @"Incoming frame's type %i doesn't match %@",frameType,self);
1.263 + _flags = (_flags & ~kBLIP_TypeMask) | frameType;
1.264 + }
1.265 +
1.266 + if( _encodedBody )
1.267 + [_encodedBody appendData: body];
1.268 + else
1.269 + _encodedBody = [body mutableCopy];
1.270 + LogTo(BLIPVerbose,@"%@ rcvd bytes %u-%u", self, _encodedBody.length-body.length, _encodedBody.length);
1.271 +
1.272 + if( ! _properties ) {
1.273 + // Try to extract the properties:
1.274 + ssize_t usedLength;
1.275 + setObj(&_properties, [BLIPProperties propertiesWithEncodedData: _encodedBody usedLength: &usedLength]);
1.276 + if( _properties ) {
1.277 + [_encodedBody replaceBytesInRange: NSMakeRange(0,usedLength)
1.278 + withBytes: NULL length: 0];
1.279 + } else if( usedLength < 0 )
1.280 + return NO;
1.281 + self.propertiesAvailable = YES;
1.282 + }
1.283 +
1.284 + if( ! (header->flags & kBLIP_MoreComing) ) {
1.285 + // After last frame, decode the data:
1.286 + _flags &= ~kBLIP_MoreComing;
1.287 + if( ! _properties )
1.288 + return NO;
1.289 + unsigned encodedLength = _encodedBody.length;
1.290 + if( self.compressed && encodedLength>0 ) {
1.291 + _body = [[NSData gtm_dataByInflatingData: _encodedBody] copy];
1.292 + if( ! _body )
1.293 + return NO;
1.294 + LogTo(BLIPVerbose,@"Uncompressed %@ from %u bytes (%.1fx)", self, encodedLength,
1.295 + _body.length/(float)encodedLength);
1.296 + } else {
1.297 + _body = [_encodedBody copy];
1.298 + }
1.299 + setObj(&_encodedBody,nil);
1.300 + self.propertiesAvailable = self.complete = YES;
1.301 + }
1.302 + return YES;
1.303 +}
1.304 +
1.305 +
1.306 +- (void) _connectionClosed
1.307 +{
1.308 + if( _isMine ) {
1.309 + _bytesWritten = 0;
1.310 + _flags |= kBLIP_MoreComing;
1.311 + }
1.312 +}
1.313 +
1.314 +
1.315 +@end
1.316 +
1.317 +
1.318 +/*
1.319 + Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
1.320 +
1.321 + Redistribution and use in source and binary forms, with or without modification, are permitted
1.322 + provided that the following conditions are met:
1.323 +
1.324 + * Redistributions of source code must retain the above copyright notice, this list of conditions
1.325 + and the following disclaimer.
1.326 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
1.327 + and the following disclaimer in the documentation and/or other materials provided with the
1.328 + distribution.
1.329 +
1.330 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
1.331 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
1.332 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
1.333 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1.334 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1.335 + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1.336 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
1.337 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.338 + */