BLIP/BLIPMessage.m
author Jens Alfke <jens@mooseyard.com>
Sun May 25 13:43:03 2008 -0700 (2008-05-25)
changeset 7 5936db2c1987
parent 1 8267d5c429c4
child 8 6f539dd9921c
permissions -rw-r--r--
Added -[TCPConnection initToNetService:] to make it easier to use with Bonjour. This allowed me to simplify BLIPEchoClient quite a lot.
     1 //
     2 //  BLIPMessage.m
     3 //  MYNetwork
     4 //
     5 //  Created by Jens Alfke on 5/10/08.
     6 //  Copyright 2008 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "BLIPMessage.h"
    10 #import "BLIP_Internal.h"
    11 #import "BLIPReader.h"
    12 #import "BLIPWriter.h"
    13 
    14 #import "Logging.h"
    15 #import "Test.h"
    16 #import "ExceptionUtils.h"
    17 #import "Target.h"
    18 
    19 // From Google Toolbox For Mac <http://code.google.com/p/google-toolbox-for-mac/>
    20 #import "GTMNSData+zlib.h"
    21 
    22 
    23 @implementation BLIPMessage
    24 
    25 
    26 - (id) _initWithConnection: (BLIPConnection*)connection
    27                     isMine: (BOOL)isMine
    28                      flags: (BLIPMessageFlags)flags
    29                     number: (UInt32)msgNo
    30                       body: (NSData*)body
    31 {
    32     self = [super init];
    33     if (self != nil) {
    34         _connection = [connection retain];
    35         _isMine = isMine;
    36         _flags = flags;
    37         _number = msgNo;
    38         if( isMine ) {
    39             _body = body.copy;
    40             _properties = [[BLIPMutableProperties alloc] init];
    41             _propertiesAvailable = YES;
    42         } else {
    43             _encodedBody = body.mutableCopy;
    44         }
    45         LogTo(BLIPVerbose,@"INIT %@",self);
    46     }
    47     return self;
    48 }
    49 
    50 - (void) dealloc
    51 {
    52     LogTo(BLIPVerbose,@"DEALLOC %@",self);
    53     [_properties release];
    54     [_encodedBody release];
    55     [_mutableBody release];
    56     [_body release];
    57     [_connection release];
    58     [super dealloc];
    59 }
    60 
    61 
    62 - (NSString*) description
    63 {
    64     NSUInteger length = (_body.length ?: _mutableBody.length) ?: _encodedBody.length;
    65     NSMutableString *desc = [NSMutableString stringWithFormat: @"%@[#%u, %u bytes",
    66                              self.class,_number, length];
    67     if( _flags & kBLIP_Compressed ) {
    68         if( _encodedBody && _encodedBody.length != length )
    69             [desc appendFormat: @" (%u gzipped)", _encodedBody.length];
    70         else
    71             [desc appendString: @", gzipped"];
    72     }
    73     if( _flags & kBLIP_Urgent )
    74         [desc appendString: @", urgent"];
    75     if( _flags & kBLIP_NoReply )
    76         [desc appendString: @", noreply"];
    77     [desc appendString: @"]"];
    78     return desc;
    79 }
    80 
    81 - (NSString*) descriptionWithProperties
    82 {
    83     NSMutableString *desc = (NSMutableString*)self.description;
    84     [desc appendFormat: @" %@", self.properties.allProperties];
    85     return desc;
    86 }
    87 
    88 
    89 #pragma mark -
    90 #pragma mark PROPERTIES & METADATA:
    91 
    92 
    93 @synthesize connection=_connection, number=_number, isMine=_isMine, isMutable=_isMutable,
    94             _bytesWritten, sent=_sent, propertiesAvailable=_propertiesAvailable, complete=_complete;
    95 
    96 
    97 - (void) _setFlag: (BLIPMessageFlags)flag value: (BOOL)value
    98 {
    99     Assert(_isMine && _isMutable);
   100     if( value )
   101         _flags |= flag;
   102     else
   103         _flags &= ~flag;
   104 }
   105 
   106 - (BOOL) compressed                         {return (_flags & kBLIP_Compressed) != 0;}
   107 - (BOOL) urgent                             {return (_flags & kBLIP_Urgent) != 0;}
   108 - (void) setCompressed: (BOOL)compressed    {[self _setFlag: kBLIP_Compressed value: compressed];}
   109 - (void) setUrgent: (BOOL)high              {[self _setFlag: kBLIP_Urgent value: high];}
   110 
   111 
   112 - (NSData*) body
   113 {
   114     if( ! _body && _isMine )
   115         return [[_mutableBody copy] autorelease];
   116     else
   117         return _body;
   118 }
   119 
   120 - (void) setBody: (NSData*)body
   121 {
   122     Assert(_isMine && _isMutable);
   123     if( _mutableBody )
   124         [_mutableBody setData: body];
   125     else
   126         _mutableBody = [body mutableCopy];
   127 }
   128 
   129 - (void) _addToBody: (NSData*)data
   130 {
   131     if( data.length ) {
   132         if( _mutableBody )
   133             [_mutableBody appendData: data];
   134         else
   135             _mutableBody = [data mutableCopy];
   136         setObj(&_body,nil);
   137     }
   138 }
   139 
   140 - (void) addToBody: (NSData*)data
   141 {
   142     Assert(_isMine && _isMutable);
   143     [self _addToBody: data];
   144 }
   145 
   146 
   147 - (NSString*) bodyString
   148 {
   149     NSData *body = self.body;
   150     if( body )
   151         return [[[NSString alloc] initWithData: body encoding: NSUTF8StringEncoding] autorelease];
   152     else
   153         return nil;
   154 }
   155 
   156 - (void) setBodyString: (NSString*)string
   157 {
   158     self.body = [string dataUsingEncoding: NSUTF8StringEncoding];
   159     self.contentType = @"text/plain; charset=UTF-8";
   160 }
   161 
   162 
   163 - (BLIPProperties*) properties
   164 {
   165     return _properties;
   166 }
   167 
   168 - (BLIPMutableProperties*) mutableProperties
   169 {
   170     Assert(_isMine && _isMutable);
   171     return (BLIPMutableProperties*)_properties;
   172 }
   173 
   174 - (NSString*) valueOfProperty: (NSString*)property
   175 {
   176     return [_properties valueOfProperty: property];
   177 }
   178 
   179 - (void) setValue: (NSString*)value ofProperty: (NSString*)property
   180 {
   181     [self.mutableProperties setValue: value ofProperty: property];
   182 }
   183 
   184 - (NSString*) contentType               {return [_properties valueOfProperty: @"Content-Type"];}
   185 - (void) setContentType: (NSString*)t   {[self setValue: t ofProperty: @"Content-Type"];}
   186 - (NSString*) profile                   {return [_properties valueOfProperty: @"Profile"];}
   187 - (void) setProfile: (NSString*)p       {[self setValue: p ofProperty: @"Profile"];}
   188 
   189 
   190 #pragma mark -
   191 #pragma mark I/O:
   192 
   193 
   194 - (void) _encode
   195 {
   196     Assert(_isMine && _isMutable);
   197     _isMutable = NO;
   198 
   199     BLIPProperties *oldProps = _properties;
   200     _properties = [oldProps copy];
   201     [oldProps release];
   202     
   203     _encodedBody = [_properties.encodedData mutableCopy];
   204     Assert(_encodedBody.length>=2);
   205 
   206     NSData *body = _body ?: _mutableBody;
   207     NSUInteger length = body.length;
   208     if( length > 0 ) {
   209         if( self.compressed ) {
   210             body = [NSData gtm_dataByGzippingData: body compressionLevel: 5];
   211             LogTo(BLIPVerbose,@"Compressed %@ to %u bytes (%.0f%%)", self,body.length,
   212                   body.length*100.0/length);
   213         }
   214         [_encodedBody appendData: body];
   215     }
   216 }
   217 
   218 
   219 - (void) _assignedNumber: (UInt32)number
   220 {
   221     Assert(_number==0,@"%@ has already been sent",self);
   222     _number = number;
   223     _isMutable = NO;
   224 }
   225 
   226 
   227 - (BOOL) _writeFrameTo: (BLIPWriter*)writer maxSize: (UInt16)maxSize
   228 {
   229     Assert(_number!=0);
   230     Assert(_isMine);
   231     Assert(_encodedBody);
   232     if( _bytesWritten==0 )
   233         LogTo(BLIP,@"Now sending %@",self);
   234     ssize_t lengthToWrite = _encodedBody.length - _bytesWritten;
   235     if( lengthToWrite <= 0 && _bytesWritten > 0 )
   236         return NO; // done
   237     Assert(maxSize > sizeof(BLIPFrameHeader));
   238     maxSize -= sizeof(BLIPFrameHeader);
   239     UInt16 flags = _flags;
   240     if( lengthToWrite > maxSize ) {
   241         lengthToWrite = maxSize;
   242         flags |= kBLIP_MoreComing;
   243         LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u", self, _bytesWritten, _bytesWritten+lengthToWrite);
   244     } else {
   245         flags &= ~kBLIP_MoreComing;
   246         LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u (finished)", self, _bytesWritten, _bytesWritten+lengthToWrite);
   247     }
   248         
   249     // First write the frame header:
   250     BLIPFrameHeader header = {  EndianU32_NtoB(kBLIPFrameHeaderMagicNumber),
   251                                 EndianU32_NtoB(_number),
   252                                 EndianU16_NtoB(flags),
   253                                 EndianU16_NtoB(sizeof(BLIPFrameHeader) + lengthToWrite) };
   254     
   255     [writer writeData: [NSData dataWithBytes: &header length: sizeof(header)]];
   256     
   257     // Then write the body:
   258     if( lengthToWrite > 0 ) {
   259         [writer writeData: [NSData dataWithBytesNoCopy: (UInt8*)_encodedBody.bytes + _bytesWritten
   260                                               length: lengthToWrite
   261                                         freeWhenDone: NO]];
   262         _bytesWritten += lengthToWrite;
   263     }
   264     return (flags & kBLIP_MoreComing) != 0;
   265 }
   266 
   267 
   268 - (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body
   269 {
   270     Assert(!_isMine);
   271     AssertEq(header->number,_number);
   272     Assert(_flags & kBLIP_MoreComing);
   273     
   274     BLIPMessageType frameType = (header->flags & kBLIP_TypeMask), curType = (_flags & kBLIP_TypeMask);
   275     if( frameType != curType ) {
   276         Assert(curType==kBLIP_RPY && frameType==kBLIP_ERR && _mutableBody.length==0,
   277                @"Incoming frame's type %i doesn't match %@",frameType,self);
   278         _flags = (_flags & ~kBLIP_TypeMask) | frameType;
   279     }
   280 
   281     if( _encodedBody )
   282         [_encodedBody appendData: body];
   283     else
   284         _encodedBody = [body mutableCopy];
   285     LogTo(BLIPVerbose,@"%@ rcvd bytes %u-%u", self, _encodedBody.length-body.length, _encodedBody.length);
   286     
   287     if( ! _properties ) {
   288         // Try to extract the properties:
   289         ssize_t usedLength;
   290         setObj(&_properties, [BLIPProperties propertiesWithEncodedData: _encodedBody usedLength: &usedLength]);
   291         if( _properties ) {
   292             [_encodedBody replaceBytesInRange: NSMakeRange(0,usedLength)
   293                                     withBytes: NULL length: 0];
   294         } else if( usedLength < 0 )
   295             return NO;
   296         self.propertiesAvailable = YES;
   297     }
   298     
   299     if( ! (header->flags & kBLIP_MoreComing) ) {
   300         // After last frame, decode the data:
   301         _flags &= ~kBLIP_MoreComing;
   302         if( ! _properties )
   303             return NO;
   304         unsigned encodedLength = _encodedBody.length;
   305         if( self.compressed && encodedLength>0 ) {
   306             _body = [[NSData gtm_dataByInflatingData: _encodedBody] copy];
   307             if( ! _body )
   308                 return NO;
   309             LogTo(BLIPVerbose,@"Uncompressed %@ from %u bytes (%.1fx)", self, encodedLength,
   310                   _body.length/(float)encodedLength);
   311         } else {
   312             _body = [_encodedBody copy];
   313         }
   314         setObj(&_encodedBody,nil);
   315         self.propertiesAvailable = self.complete = YES;
   316     }
   317     return YES;
   318 }
   319 
   320 
   321 - (void) _connectionClosed
   322 {
   323     if( _isMine ) {
   324         _bytesWritten = 0;
   325         _flags |= kBLIP_MoreComing;
   326     }
   327 }
   328 
   329 
   330 @end
   331 
   332 
   333 /*
   334  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   335  
   336  Redistribution and use in source and binary forms, with or without modification, are permitted
   337  provided that the following conditions are met:
   338  
   339  * Redistributions of source code must retain the above copyright notice, this list of conditions
   340  and the following disclaimer.
   341  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   342  and the following disclaimer in the documentation and/or other materials provided with the
   343  distribution.
   344  
   345  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   346  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   347  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   348  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   349  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   350   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   351  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   352  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   353  */