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