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