BLIP/BLIPMessage.m
author Jens Alfke <jens@mooseyard.com>
Wed Apr 29 21:05:01 2009 -0700 (2009-04-29)
changeset 33 a9c59b0acbbc
parent 18 3be241de1630
child 50 63baa74c903f
permissions -rw-r--r--
Added -[TCPConnection initToBonjourService:] since MYBonjourService no longer vends an NSNetService.
     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     if( _flags & kBLIP_Meta )
    78         [desc appendString: @", META"];
    79     [desc appendString: @"]"];
    80     return desc;
    81 }
    82 
    83 - (NSString*) descriptionWithProperties
    84 {
    85     NSMutableString *desc = (NSMutableString*)self.description;
    86     [desc appendFormat: @" %@", self.properties.allProperties];
    87     return desc;
    88 }
    89 
    90 
    91 #pragma mark -
    92 #pragma mark PROPERTIES & METADATA:
    93 
    94 
    95 @synthesize connection=_connection, number=_number, isMine=_isMine, isMutable=_isMutable,
    96             _bytesWritten, sent=_sent, propertiesAvailable=_propertiesAvailable, complete=_complete,
    97             representedObject=_representedObject;
    98 
    99 
   100 - (void) _setFlag: (BLIPMessageFlags)flag value: (BOOL)value
   101 {
   102     Assert(_isMine && _isMutable);
   103     if( value )
   104         _flags |= flag;
   105     else
   106         _flags &= ~flag;
   107 }
   108 
   109 - (BLIPMessageFlags) _flags                 {return _flags;}
   110 
   111 - (BOOL) compressed                         {return (_flags & kBLIP_Compressed) != 0;}
   112 - (BOOL) urgent                             {return (_flags & kBLIP_Urgent) != 0;}
   113 - (void) setCompressed: (BOOL)compressed    {[self _setFlag: kBLIP_Compressed value: compressed];}
   114 - (void) setUrgent: (BOOL)high              {[self _setFlag: kBLIP_Urgent value: high];}
   115 
   116 
   117 - (NSData*) body
   118 {
   119     if( ! _body && _isMine )
   120         return [[_mutableBody copy] autorelease];
   121     else
   122         return _body;
   123 }
   124 
   125 - (void) setBody: (NSData*)body
   126 {
   127     Assert(_isMine && _isMutable);
   128     if( _mutableBody )
   129         [_mutableBody setData: body];
   130     else
   131         _mutableBody = [body mutableCopy];
   132 }
   133 
   134 - (void) _addToBody: (NSData*)data
   135 {
   136     if( data.length ) {
   137         if( _mutableBody )
   138             [_mutableBody appendData: data];
   139         else
   140             _mutableBody = [data mutableCopy];
   141         setObj(&_body,nil);
   142     }
   143 }
   144 
   145 - (void) addToBody: (NSData*)data
   146 {
   147     Assert(_isMine && _isMutable);
   148     [self _addToBody: data];
   149 }
   150 
   151 
   152 - (NSString*) bodyString
   153 {
   154     NSData *body = self.body;
   155     if( body )
   156         return [[[NSString alloc] initWithData: body encoding: NSUTF8StringEncoding] autorelease];
   157     else
   158         return nil;
   159 }
   160 
   161 - (void) setBodyString: (NSString*)string
   162 {
   163     self.body = [string dataUsingEncoding: NSUTF8StringEncoding];
   164     self.contentType = @"text/plain; charset=UTF-8";
   165 }
   166 
   167 
   168 - (BLIPProperties*) properties
   169 {
   170     return _properties;
   171 }
   172 
   173 - (BLIPMutableProperties*) mutableProperties
   174 {
   175     Assert(_isMine && _isMutable);
   176     return (BLIPMutableProperties*)_properties;
   177 }
   178 
   179 - (NSString*) valueOfProperty: (NSString*)property
   180 {
   181     return [_properties valueOfProperty: property];
   182 }
   183 
   184 - (void) setValue: (NSString*)value ofProperty: (NSString*)property
   185 {
   186     [self.mutableProperties setValue: value ofProperty: property];
   187 }
   188 
   189 - (NSString*) contentType               {return [_properties valueOfProperty: @"Content-Type"];}
   190 - (void) setContentType: (NSString*)t   {[self setValue: t ofProperty: @"Content-Type"];}
   191 - (NSString*) profile                   {return [_properties valueOfProperty: @"Profile"];}
   192 - (void) setProfile: (NSString*)p       {[self setValue: p ofProperty: @"Profile"];}
   193 
   194 
   195 #pragma mark -
   196 #pragma mark I/O:
   197 
   198 
   199 - (void) _encode
   200 {
   201     Assert(_isMine && _isMutable);
   202     _isMutable = NO;
   203 
   204     BLIPProperties *oldProps = _properties;
   205     _properties = [oldProps copy];
   206     [oldProps release];
   207     
   208     _encodedBody = [_properties.encodedData mutableCopy];
   209     Assert(_encodedBody.length>=2);
   210 
   211     NSData *body = _body ?: _mutableBody;
   212     NSUInteger length = body.length;
   213     if( length > 0 ) {
   214         if( self.compressed ) {
   215             body = [NSData gtm_dataByGzippingData: body compressionLevel: 5];
   216             LogTo(BLIPVerbose,@"Compressed %@ to %u bytes (%.0f%%)", self,body.length,
   217                   body.length*100.0/length);
   218         }
   219         [_encodedBody appendData: body];
   220     }
   221 }
   222 
   223 
   224 - (void) _assignedNumber: (UInt32)number
   225 {
   226     Assert(_number==0,@"%@ has already been sent",self);
   227     _number = number;
   228     _isMutable = NO;
   229 }
   230 
   231 
   232 - (BOOL) _writeFrameTo: (BLIPWriter*)writer maxSize: (UInt16)maxSize
   233 {
   234     Assert(_number!=0);
   235     Assert(_isMine);
   236     Assert(_encodedBody);
   237     if( _bytesWritten==0 )
   238         LogTo(BLIP,@"Now sending %@",self);
   239     ssize_t lengthToWrite = _encodedBody.length - _bytesWritten;
   240     if( lengthToWrite <= 0 && _bytesWritten > 0 )
   241         return NO; // done
   242     Assert(maxSize > sizeof(BLIPFrameHeader));
   243     maxSize -= sizeof(BLIPFrameHeader);
   244     UInt16 flags = _flags;
   245     if( lengthToWrite > maxSize ) {
   246         lengthToWrite = maxSize;
   247         flags |= kBLIP_MoreComing;
   248         LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u", self, _bytesWritten, _bytesWritten+lengthToWrite);
   249     } else {
   250         flags &= ~kBLIP_MoreComing;
   251         LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u (finished)", self, _bytesWritten, _bytesWritten+lengthToWrite);
   252     }
   253         
   254     // First write the frame header:
   255     BLIPFrameHeader header = {  NSSwapHostIntToBig(kBLIPFrameHeaderMagicNumber),
   256                                 NSSwapHostIntToBig(_number),
   257                                 NSSwapHostShortToBig(flags),
   258                                 NSSwapHostShortToBig(sizeof(BLIPFrameHeader) + lengthToWrite) };
   259     
   260     [writer writeData: [NSData dataWithBytes: &header length: sizeof(header)]];
   261     
   262     // Then write the body:
   263     if( lengthToWrite > 0 ) {
   264         [writer writeData: [NSData dataWithBytes: (UInt8*)_encodedBody.bytes + _bytesWritten
   265                                           length: lengthToWrite]];
   266         _bytesWritten += lengthToWrite;
   267     }
   268     return (flags & kBLIP_MoreComing) != 0;
   269 }
   270 
   271 
   272 - (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body
   273 {
   274     Assert(!_isMine);
   275     AssertEq(header->number,_number);
   276     Assert(_flags & kBLIP_MoreComing);
   277     
   278     BLIPMessageType frameType = (header->flags & kBLIP_TypeMask), curType = (_flags & kBLIP_TypeMask);
   279     if( frameType != curType ) {
   280         Assert(curType==kBLIP_RPY && frameType==kBLIP_ERR && _mutableBody.length==0,
   281                @"Incoming frame's type %i doesn't match %@",frameType,self);
   282         _flags = (_flags & ~kBLIP_TypeMask) | frameType;
   283     }
   284 
   285     if( _encodedBody )
   286         [_encodedBody appendData: body];
   287     else
   288         _encodedBody = [body mutableCopy];
   289     LogTo(BLIPVerbose,@"%@ rcvd bytes %u-%u", self, _encodedBody.length-body.length, _encodedBody.length);
   290     
   291     if( ! _properties ) {
   292         // Try to extract the properties:
   293         ssize_t usedLength;
   294         setObj(&_properties, [BLIPProperties propertiesWithEncodedData: _encodedBody usedLength: &usedLength]);
   295         if( _properties ) {
   296             [_encodedBody replaceBytesInRange: NSMakeRange(0,usedLength)
   297                                     withBytes: NULL length: 0];
   298         } else if( usedLength < 0 )
   299             return NO;
   300         self.propertiesAvailable = YES;
   301     }
   302     
   303     if( ! (header->flags & kBLIP_MoreComing) ) {
   304         // After last frame, decode the data:
   305         _flags &= ~kBLIP_MoreComing;
   306         if( ! _properties )
   307             return NO;
   308         unsigned encodedLength = _encodedBody.length;
   309         if( self.compressed && encodedLength>0 ) {
   310             _body = [[NSData gtm_dataByInflatingData: _encodedBody] copy];
   311             if( ! _body )
   312                 return NO;
   313             LogTo(BLIPVerbose,@"Uncompressed %@ from %u bytes (%.1fx)", self, encodedLength,
   314                   _body.length/(float)encodedLength);
   315         } else {
   316             _body = [_encodedBody copy];
   317         }
   318         setObj(&_encodedBody,nil);
   319         self.propertiesAvailable = self.complete = YES;
   320     }
   321     return YES;
   322 }
   323 
   324 
   325 - (void) _connectionClosed
   326 {
   327     if( _isMine ) {
   328         _bytesWritten = 0;
   329         _flags |= kBLIP_MoreComing;
   330     }
   331 }
   332 
   333 
   334 @end
   335 
   336 
   337 /*
   338  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   339  
   340  Redistribution and use in source and binary forms, with or without modification, are permitted
   341  provided that the following conditions are met:
   342  
   343  * Redistributions of source code must retain the above copyright notice, this list of conditions
   344  and the following disclaimer.
   345  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   346  and the following disclaimer in the documentation and/or other materials provided with the
   347  distribution.
   348  
   349  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   350  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   351  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   352  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   353  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   354   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   355  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   356  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   357  */