BLIP/BLIPMessage.m
author Jens Alfke <jens@mooseyard.com>
Wed Jun 11 14:58:38 2008 -0700 (2008-06-11)
changeset 16 6f608b552b77
parent 8 6f539dd9921c
child 18 3be241de1630
permissions -rw-r--r--
* Added a timeout property to TCPConnection. Set it before calling -open, if you want a shorter timeout than the default.
* Made the utility function BLIPMakeError public.
     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 = {  NSSwapHostIntToBig(kBLIPFrameHeaderMagicNumber),
   251                                 NSSwapHostIntToBig(_number),
   252                                 NSSwapHostShortToBig(flags),
   253                                 NSSwapHostShortToBig(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 dataWithBytes: (UInt8*)_encodedBody.bytes + _bytesWritten
   260                                           length: lengthToWrite]];
   261         _bytesWritten += lengthToWrite;
   262     }
   263     return (flags & kBLIP_MoreComing) != 0;
   264 }
   265 
   266 
   267 - (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body
   268 {
   269     Assert(!_isMine);
   270     AssertEq(header->number,_number);
   271     Assert(_flags & kBLIP_MoreComing);
   272     
   273     BLIPMessageType frameType = (header->flags & kBLIP_TypeMask), curType = (_flags & kBLIP_TypeMask);
   274     if( frameType != curType ) {
   275         Assert(curType==kBLIP_RPY && frameType==kBLIP_ERR && _mutableBody.length==0,
   276                @"Incoming frame's type %i doesn't match %@",frameType,self);
   277         _flags = (_flags & ~kBLIP_TypeMask) | frameType;
   278     }
   279 
   280     if( _encodedBody )
   281         [_encodedBody appendData: body];
   282     else
   283         _encodedBody = [body mutableCopy];
   284     LogTo(BLIPVerbose,@"%@ rcvd bytes %u-%u", self, _encodedBody.length-body.length, _encodedBody.length);
   285     
   286     if( ! _properties ) {
   287         // Try to extract the properties:
   288         ssize_t usedLength;
   289         setObj(&_properties, [BLIPProperties propertiesWithEncodedData: _encodedBody usedLength: &usedLength]);
   290         if( _properties ) {
   291             [_encodedBody replaceBytesInRange: NSMakeRange(0,usedLength)
   292                                     withBytes: NULL length: 0];
   293         } else if( usedLength < 0 )
   294             return NO;
   295         self.propertiesAvailable = YES;
   296     }
   297     
   298     if( ! (header->flags & kBLIP_MoreComing) ) {
   299         // After last frame, decode the data:
   300         _flags &= ~kBLIP_MoreComing;
   301         if( ! _properties )
   302             return NO;
   303         unsigned encodedLength = _encodedBody.length;
   304         if( self.compressed && encodedLength>0 ) {
   305             _body = [[NSData gtm_dataByInflatingData: _encodedBody] copy];
   306             if( ! _body )
   307                 return NO;
   308             LogTo(BLIPVerbose,@"Uncompressed %@ from %u bytes (%.1fx)", self, encodedLength,
   309                   _body.length/(float)encodedLength);
   310         } else {
   311             _body = [_encodedBody copy];
   312         }
   313         setObj(&_encodedBody,nil);
   314         self.propertiesAvailable = self.complete = YES;
   315     }
   316     return YES;
   317 }
   318 
   319 
   320 - (void) _connectionClosed
   321 {
   322     if( _isMine ) {
   323         _bytesWritten = 0;
   324         _flags |= kBLIP_MoreComing;
   325     }
   326 }
   327 
   328 
   329 @end
   330 
   331 
   332 /*
   333  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   334  
   335  Redistribution and use in source and binary forms, with or without modification, are permitted
   336  provided that the following conditions are met:
   337  
   338  * Redistributions of source code must retain the above copyright notice, this list of conditions
   339  and the following disclaimer.
   340  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   341  and the following disclaimer in the documentation and/or other materials provided with the
   342  distribution.
   343  
   344  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   345  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   346  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   347  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   348  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   349   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   350  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   351  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   352  */