diff -r 000000000000 -r 9d67172bb323 BLIP/BLIPMessage.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BLIP/BLIPMessage.m Fri May 23 17:37:36 2008 -0700 @@ -0,0 +1,335 @@ +// +// BLIPMessage.m +// MYNetwork +// +// Created by Jens Alfke on 5/10/08. +// Copyright 2008 Jens Alfke. All rights reserved. +// + +#import "BLIPMessage.h" +#import "BLIP_Internal.h" +#import "BLIPReader.h" +#import "BLIPWriter.h" + +#import "ExceptionUtils.h" +#import "Target.h" + +// From Google Toolbox For Mac +#import "GTMNSData+zlib.h" + + +@implementation BLIPMessage + + +- (id) _initWithConnection: (BLIPConnection*)connection + isMine: (BOOL)isMine + flags: (BLIPMessageFlags)flags + number: (UInt32)msgNo + body: (NSData*)body +{ + self = [super init]; + if (self != nil) { + _connection = [connection retain]; + _isMine = isMine; + _flags = flags; + _number = msgNo; + if( isMine ) { + _body = body.copy; + _properties = [[BLIPMutableProperties alloc] init]; + _propertiesAvailable = YES; + } else { + _encodedBody = body.mutableCopy; + } + LogTo(BLIPVerbose,@"INIT %@",self); + } + return self; +} + +- (void) dealloc +{ + LogTo(BLIPVerbose,@"DEALLOC %@",self); + [_properties release]; + [_encodedBody release]; + [_mutableBody release]; + [_body release]; + [_connection release]; + [super dealloc]; +} + + +- (NSString*) description +{ + NSUInteger length = (_body.length ?: _mutableBody.length) ?: _encodedBody.length; + NSMutableString *desc = [NSMutableString stringWithFormat: @"%@[#%u, %u bytes", + self.class,_number, length]; + if( _flags & kBLIP_Compressed ) { + if( _encodedBody && _encodedBody.length != length ) + [desc appendFormat: @" (%u gzipped)", _encodedBody.length]; + else + [desc appendString: @", gzipped"]; + } + if( _flags & kBLIP_Urgent ) + [desc appendString: @", urgent"]; + if( _flags & kBLIP_NoReply ) + [desc appendString: @", noreply"]; + [desc appendString: @"]"]; + return desc; +} + +- (NSString*) descriptionWithProperties +{ + NSMutableString *desc = (NSMutableString*)self.description; + [desc appendFormat: @" %@", self.properties.allProperties]; + return desc; +} + + +#pragma mark - +#pragma mark PROPERTIES & METADATA: + + +@synthesize connection=_connection, number=_number, isMine=_isMine, isMutable=_isMutable, + _bytesWritten, sent=_sent, propertiesAvailable=_propertiesAvailable, complete=_complete; + + +- (void) _setFlag: (BLIPMessageFlags)flag value: (BOOL)value +{ + Assert(_isMine && _isMutable); + if( value ) + _flags |= flag; + else + _flags &= ~flag; +} + +- (BOOL) compressed {return (_flags & kBLIP_Compressed) != 0;} +- (BOOL) urgent {return (_flags & kBLIP_Urgent) != 0;} +- (void) setCompressed: (BOOL)compressed {[self _setFlag: kBLIP_Compressed value: compressed];} +- (void) setUrgent: (BOOL)high {[self _setFlag: kBLIP_Urgent value: high];} + + +- (NSData*) body +{ + if( ! _body && _isMine ) + return [[_mutableBody copy] autorelease]; + else + return _body; +} + +- (void) setBody: (NSData*)body +{ + Assert(_isMine && _isMutable); + if( _mutableBody ) + [_mutableBody setData: body]; + else + _mutableBody = [body mutableCopy]; +} + +- (void) _addToBody: (NSData*)data +{ + if( data.length ) { + if( _mutableBody ) + [_mutableBody appendData: data]; + else + _mutableBody = [data mutableCopy]; + setObj(&_body,nil); + } +} + +- (void) addToBody: (NSData*)data +{ + Assert(_isMine && _isMutable); + [self _addToBody: data]; +} + + +- (BLIPProperties*) properties +{ + return _properties; +} + +- (BLIPMutableProperties*) mutableProperties +{ + Assert(_isMine && _isMutable); + return (BLIPMutableProperties*)_properties; +} + +- (NSString*) valueOfProperty: (NSString*)property +{ + return [_properties valueOfProperty: property]; +} + +- (void) setValue: (NSString*)value ofProperty: (NSString*)property +{ + [self.mutableProperties setValue: value ofProperty: property]; +} + +- (NSString*) contentType {return [_properties valueOfProperty: @"Content-Type"];} +- (void) setContentType: (NSString*)t {[self setValue: t ofProperty: @"Content-Type"];} +- (NSString*) profile {return [_properties valueOfProperty: @"Profile"];} +- (void) setProfile: (NSString*)p {[self setValue: p ofProperty: @"Profile"];} + + +#pragma mark - +#pragma mark I/O: + + +- (void) _encode +{ + Assert(_isMine && _isMutable); + _isMutable = NO; + + BLIPProperties *oldProps = _properties; + _properties = [oldProps copy]; + [oldProps release]; + + _encodedBody = [_properties.encodedData mutableCopy]; + Assert(_encodedBody.length>=2); + + NSData *body = _body ?: _mutableBody; + NSUInteger length = body.length; + if( length > 0 ) { + if( self.compressed ) { + body = [NSData gtm_dataByGzippingData: body compressionLevel: 5]; + LogTo(BLIPVerbose,@"Compressed %@ to %u bytes (%.0f%%)", self,body.length, + body.length*100.0/length); + } + [_encodedBody appendData: body]; + } +} + + +- (void) _assignedNumber: (UInt32)number +{ + Assert(_number==0,@"%@ has already been sent",self); + _number = number; + _isMutable = NO; +} + + +- (BOOL) _writeFrameTo: (BLIPWriter*)writer maxSize: (UInt16)maxSize +{ + Assert(_number!=0); + Assert(_isMine); + Assert(_encodedBody); + if( _bytesWritten==0 ) + LogTo(BLIP,@"Now sending %@",self); + ssize_t lengthToWrite = _encodedBody.length - _bytesWritten; + if( lengthToWrite <= 0 && _bytesWritten > 0 ) + return NO; // done + Assert(maxSize > sizeof(BLIPFrameHeader)); + maxSize -= sizeof(BLIPFrameHeader); + UInt16 flags = _flags; + if( lengthToWrite > maxSize ) { + lengthToWrite = maxSize; + flags |= kBLIP_MoreComing; + LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u", self, _bytesWritten, _bytesWritten+lengthToWrite); + } else { + flags &= ~kBLIP_MoreComing; + LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u (finished)", self, _bytesWritten, _bytesWritten+lengthToWrite); + } + + // First write the frame header: + BLIPFrameHeader header = { EndianU32_NtoB(kBLIPFrameHeaderMagicNumber), + EndianU32_NtoB(_number), + EndianU16_NtoB(flags), + EndianU16_NtoB(sizeof(BLIPFrameHeader) + lengthToWrite) }; + + [writer writeData: [NSData dataWithBytes: &header length: sizeof(header)]]; + + // Then write the body: + if( lengthToWrite > 0 ) { + [writer writeData: [NSData dataWithBytesNoCopy: (UInt8*)_encodedBody.bytes + _bytesWritten + length: lengthToWrite + freeWhenDone: NO]]; + _bytesWritten += lengthToWrite; + } + return (flags & kBLIP_MoreComing) != 0; +} + + +- (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body +{ + Assert(!_isMine); + AssertEq(header->number,_number); + Assert(_flags & kBLIP_MoreComing); + + BLIPMessageType frameType = (header->flags & kBLIP_TypeMask), curType = (_flags & kBLIP_TypeMask); + if( frameType != curType ) { + Assert(curType==kBLIP_RPY && frameType==kBLIP_ERR && _mutableBody.length==0, + @"Incoming frame's type %i doesn't match %@",frameType,self); + _flags = (_flags & ~kBLIP_TypeMask) | frameType; + } + + if( _encodedBody ) + [_encodedBody appendData: body]; + else + _encodedBody = [body mutableCopy]; + LogTo(BLIPVerbose,@"%@ rcvd bytes %u-%u", self, _encodedBody.length-body.length, _encodedBody.length); + + if( ! _properties ) { + // Try to extract the properties: + ssize_t usedLength; + setObj(&_properties, [BLIPProperties propertiesWithEncodedData: _encodedBody usedLength: &usedLength]); + if( _properties ) { + [_encodedBody replaceBytesInRange: NSMakeRange(0,usedLength) + withBytes: NULL length: 0]; + } else if( usedLength < 0 ) + return NO; + self.propertiesAvailable = YES; + } + + if( ! (header->flags & kBLIP_MoreComing) ) { + // After last frame, decode the data: + _flags &= ~kBLIP_MoreComing; + if( ! _properties ) + return NO; + unsigned encodedLength = _encodedBody.length; + if( self.compressed && encodedLength>0 ) { + _body = [[NSData gtm_dataByInflatingData: _encodedBody] copy]; + if( ! _body ) + return NO; + LogTo(BLIPVerbose,@"Uncompressed %@ from %u bytes (%.1fx)", self, encodedLength, + _body.length/(float)encodedLength); + } else { + _body = [_encodedBody copy]; + } + setObj(&_encodedBody,nil); + self.propertiesAvailable = self.complete = YES; + } + return YES; +} + + +- (void) _connectionClosed +{ + if( _isMine ) { + _bytesWritten = 0; + _flags |= kBLIP_MoreComing; + } +} + + +@end + + +/* + Copyright (c) 2008, Jens Alfke . All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted + provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + and the following disclaimer in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */