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