BLIP/BLIPReader.m
author Jens Alfke <jens@mooseyard.com>
Sun May 25 13:43:03 2008 -0700 (2008-05-25)
changeset 7 5936db2c1987
parent 1 8267d5c429c4
child 8 6f539dd9921c
permissions -rw-r--r--
Added -[TCPConnection initToNetService:] to make it easier to use with Bonjour. This allowed me to simplify BLIPEchoClient quite a lot.
jens@0
     1
//
jens@0
     2
//  BLIPReader.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 "BLIPReader.h"
jens@0
    10
#import "BLIP_Internal.h"
jens@0
    11
#import "BLIPWriter.h"
jens@0
    12
#import "BLIPDispatcher.h"
jens@0
    13
jens@1
    14
#import "Logging.h"
jens@1
    15
#import "Test.h"
jens@1
    16
#import "CollectionUtils.h"
jens@1
    17
jens@0
    18
jens@0
    19
@interface BLIPReader ()
jens@0
    20
- (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body;
jens@0
    21
@end
jens@0
    22
jens@0
    23
jens@0
    24
@implementation BLIPReader
jens@0
    25
jens@0
    26
jens@0
    27
#define _blipConn ((BLIPConnection*)_conn)
jens@0
    28
jens@0
    29
jens@0
    30
- (id) initWithConnection: (BLIPConnection*)conn stream: (NSStream*)stream
jens@0
    31
{
jens@0
    32
    self = [super initWithConnection: conn stream: stream];
jens@0
    33
    if (self != nil) {
jens@0
    34
        _pendingQueries = [[NSMutableDictionary alloc] init];
jens@0
    35
        _pendingReplies = [[NSMutableDictionary alloc] init];
jens@0
    36
    }
jens@0
    37
    return self;
jens@0
    38
}
jens@0
    39
jens@0
    40
- (void) dealloc
jens@0
    41
{
jens@0
    42
    [_pendingQueries release];
jens@0
    43
    [_pendingReplies release];
jens@0
    44
    [_curBody release];
jens@0
    45
    [super dealloc];
jens@0
    46
}
jens@0
    47
jens@0
    48
- (void) disconnect
jens@0
    49
{
jens@0
    50
    for( BLIPResponse *response in [_pendingReplies allValues] ) {
jens@0
    51
        [response _connectionClosed];
jens@0
    52
        [_conn tellDelegate: @selector(connection:receivedResponse:) withObject: response];
jens@0
    53
    }
jens@0
    54
    setObj(&_pendingReplies,nil);
jens@0
    55
    [super disconnect];
jens@0
    56
}
jens@0
    57
jens@0
    58
jens@0
    59
#pragma mark -
jens@0
    60
#pragma mark READING FRAMES:
jens@0
    61
jens@0
    62
jens@0
    63
- (NSString*) _validateHeader
jens@0
    64
{
jens@0
    65
    // Convert header to native byte order:
jens@0
    66
    _curHeader.magic = EndianU32_BtoN(_curHeader.magic);
jens@0
    67
    _curHeader.number= EndianU32_BtoN(_curHeader.number);
jens@0
    68
    _curHeader.flags = EndianU16_BtoN(_curHeader.flags);
jens@0
    69
    _curHeader.size  = EndianU16_BtoN(_curHeader.size);
jens@0
    70
    
jens@0
    71
    if( _curHeader.magic != kBLIPFrameHeaderMagicNumber )
jens@0
    72
        return $sprintf(@"Incorrect magic number (%08X not %08X)",
jens@0
    73
                        _curHeader.magic,kBLIPFrameHeaderMagicNumber);
jens@0
    74
    size_t bodyLength = _curHeader.size;
jens@0
    75
    if( bodyLength < sizeof(BLIPFrameHeader) )
jens@0
    76
        return @"Length is impossibly short";
jens@0
    77
    bodyLength -= sizeof(BLIPFrameHeader);
jens@0
    78
    _curBody = [[NSMutableData alloc] initWithLength: bodyLength];
jens@0
    79
    return nil;
jens@0
    80
}
jens@0
    81
    
jens@0
    82
jens@0
    83
- (void) _endCurFrame
jens@0
    84
{
jens@0
    85
    [self retain];
jens@0
    86
    [self _receivedFrameWithHeader: &_curHeader body: _curBody];
jens@0
    87
    memset(&_curHeader,0,sizeof(_curHeader));
jens@0
    88
    setObj(&_curBody,nil);
jens@0
    89
    _curBytesRead = 0;
jens@0
    90
    [self release];
jens@0
    91
}
jens@0
    92
jens@0
    93
jens@0
    94
- (BOOL) isBusy
jens@0
    95
{
jens@0
    96
    return _curBytesRead > 0;
jens@0
    97
}
jens@0
    98
jens@0
    99
jens@0
   100
- (void) _canRead
jens@0
   101
{
jens@0
   102
    SInt32 headerLeft = sizeof(BLIPFrameHeader) - _curBytesRead;
jens@0
   103
    if( headerLeft > 0 ) {
jens@0
   104
        // Read (more of) the header:
jens@2
   105
        NSInteger bytesRead = [self read: (uint8_t*)&_curHeader +_curBytesRead
jens@2
   106
                               maxLength: headerLeft];
jens@2
   107
        if( bytesRead > 0 ) {
jens@0
   108
            _curBytesRead += bytesRead;
jens@0
   109
            if( _curBytesRead < sizeof(BLIPFrameHeader) ) {
jens@0
   110
                // Incomplete header:
jens@0
   111
                LogTo(BLIPVerbose,@"%@ read %u bytes of header (%u left)",
jens@0
   112
                      self,bytesRead,sizeof(BLIPFrameHeader)-_curBytesRead);
jens@0
   113
            } else {
jens@0
   114
                // Finished reading the header!
jens@0
   115
                headerLeft = 0;
jens@0
   116
                NSString *err = [self _validateHeader];
jens@0
   117
                if( err ) {
jens@0
   118
                    Warn(@"%@ read bogus frame header: %@",self,err);
jens@0
   119
                    return (void)[self _gotError: BLIPMakeError(kBLIPError_BadData, @"%@", err)];
jens@0
   120
                }
jens@0
   121
                LogTo(BLIPVerbose,@"%@: Read header; next is %u-byte body",self,_curBody.length);
jens@0
   122
                
jens@0
   123
                if( _curBody.length == 0 ) {
jens@0
   124
                    // Zero-byte body, so no need to wait for another read
jens@0
   125
                    [self _endCurFrame];
jens@0
   126
                }
jens@0
   127
            }
jens@0
   128
        }
jens@0
   129
        
jens@0
   130
    } else {
jens@0
   131
        // Read (more of) the current frame's body:
jens@0
   132
        SInt32 bodyRemaining = (SInt32)_curBody.length + headerLeft;
jens@0
   133
        if( bodyRemaining > 0 ) {
jens@0
   134
            uint8_t *dst = _curBody.mutableBytes;
jens@0
   135
            dst += _curBody.length - bodyRemaining;
jens@2
   136
            NSInteger bytesRead = [self read: dst maxLength: bodyRemaining];
jens@2
   137
            if( bytesRead > 0 ) {
jens@0
   138
                _curBytesRead += bytesRead;
jens@0
   139
                bodyRemaining -= bytesRead;
jens@0
   140
                LogTo(BLIPVerbose,@"%@: Read %u bytes of frame body (%u left)",self,bytesRead,bodyRemaining);
jens@0
   141
            }
jens@0
   142
        }
jens@0
   143
        if( bodyRemaining==0 ) {
jens@0
   144
            // Done reading this frame: give it to the Connection and reset my state
jens@0
   145
            [self _endCurFrame];
jens@0
   146
        }
jens@0
   147
    }
jens@0
   148
}
jens@0
   149
jens@0
   150
jens@0
   151
#pragma mark -
jens@0
   152
#pragma mark PROCESSING FRAMES:
jens@0
   153
jens@0
   154
jens@0
   155
- (void) _addPendingResponse: (BLIPResponse*)response
jens@0
   156
{
jens@0
   157
    [_pendingReplies setObject: response forKey: $object(response.number)];
jens@0
   158
}
jens@0
   159
jens@0
   160
jens@0
   161
- (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body
jens@0
   162
{
jens@0
   163
    static const char* kTypeStrs[16] = {"MSG","RPY","ERR","3??","4??","5??","6??","7??"};
jens@0
   164
    BLIPMessageType type = header->flags & kBLIP_TypeMask;
jens@0
   165
    LogTo(BLIPVerbose,@"%@ rcvd frame of %s #%u, length %u",self,kTypeStrs[type],header->number,body.length);
jens@0
   166
jens@0
   167
    id key = $object(header->number);
jens@0
   168
    BOOL complete = ! (header->flags & kBLIP_MoreComing);
jens@0
   169
    switch(type) {
jens@0
   170
        case kBLIP_MSG: {
jens@0
   171
            // Incoming request:
jens@0
   172
            BLIPRequest *request = [_pendingQueries objectForKey: key];
jens@0
   173
            if( request ) {
jens@0
   174
                // Continuation frame of a request:
jens@0
   175
                if( complete ) {
jens@0
   176
                    [[request retain] autorelease];
jens@0
   177
                    [_pendingQueries removeObjectForKey: key];
jens@0
   178
                }
jens@0
   179
            } else if( header->number == _numQueriesReceived+1 ) {
jens@0
   180
                // Next new request:
jens@0
   181
                request = [[[BLIPRequest alloc] _initWithConnection: _blipConn
jens@0
   182
                                                         isMine: NO
jens@0
   183
                                                          flags: header->flags | kBLIP_MoreComing
jens@0
   184
                                                         number: header->number
jens@0
   185
                                                           body: nil]
jens@0
   186
                                autorelease];
jens@0
   187
                if( ! complete )
jens@0
   188
                    [_pendingQueries setObject: request forKey: key];
jens@0
   189
                _numQueriesReceived++;
jens@0
   190
            } else
jens@0
   191
                return [self _gotError: BLIPMakeError(kBLIPError_BadFrame, 
jens@0
   192
                                               @"Received bad request frame #%u (next is #%u)",
jens@0
   193
                                               header->number,_numQueriesReceived+1)];
jens@0
   194
            
jens@0
   195
            if( ! [request _receivedFrameWithHeader: header body: body] )
jens@0
   196
                return [self _gotError: BLIPMakeError(kBLIPError_BadFrame, 
jens@0
   197
                                               @"Couldn't parse message frame")];
jens@0
   198
            
jens@0
   199
            if( complete )
jens@0
   200
                [_blipConn _dispatchRequest: request];
jens@0
   201
            break;
jens@0
   202
        }
jens@0
   203
            
jens@0
   204
        case kBLIP_RPY:
jens@0
   205
        case kBLIP_ERR: {
jens@0
   206
            BLIPResponse *response = [_pendingReplies objectForKey: key];
jens@0
   207
            if( response ) {
jens@0
   208
                if( complete ) {
jens@0
   209
                    [[response retain] autorelease];
jens@0
   210
                    [_pendingReplies removeObjectForKey: key];
jens@0
   211
                }
jens@0
   212
                
jens@0
   213
                if( ! [response _receivedFrameWithHeader: header body: body] ) {
jens@0
   214
                    return [self _gotError: BLIPMakeError(kBLIPError_BadFrame, 
jens@0
   215
                                                          @"Couldn't parse response frame")];
jens@0
   216
                } else if( complete ) 
jens@0
   217
                    [_blipConn _dispatchResponse: response];
jens@0
   218
                
jens@0
   219
            } else {
jens@0
   220
                if( header->number <= ((BLIPWriter*)self.writer).numQueriesSent )
jens@0
   221
                    LogTo(BLIP,@"??? %@ got unexpected response frame to my msg #%u",
jens@0
   222
                          self,header->number); //benign
jens@0
   223
                else
jens@0
   224
                    return [self _gotError: BLIPMakeError(kBLIPError_BadFrame, 
jens@0
   225
                                                          @"Bogus message number %u in response",
jens@0
   226
                                                          header->number)];
jens@0
   227
            }
jens@0
   228
            break;
jens@0
   229
        }
jens@0
   230
            
jens@0
   231
        default:
jens@0
   232
            // To leave room for future expansion, undefined message types are just ignored.
jens@0
   233
            Log(@"??? %@ received header with unknown message type %i", self,type);
jens@0
   234
            break;
jens@0
   235
    }
jens@0
   236
    return YES;
jens@0
   237
}
jens@0
   238
jens@0
   239
jens@0
   240
@end
jens@0
   241
jens@0
   242
jens@0
   243
/*
jens@0
   244
 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
jens@0
   245
 
jens@0
   246
 Redistribution and use in source and binary forms, with or without modification, are permitted
jens@0
   247
 provided that the following conditions are met:
jens@0
   248
 
jens@0
   249
 * Redistributions of source code must retain the above copyright notice, this list of conditions
jens@0
   250
 and the following disclaimer.
jens@0
   251
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
jens@0
   252
 and the following disclaimer in the documentation and/or other materials provided with the
jens@0
   253
 distribution.
jens@0
   254
 
jens@0
   255
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
jens@0
   256
 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
jens@0
   257
 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
jens@0
   258
 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
jens@0
   259
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
jens@0
   260
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
jens@0
   261
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
jens@0
   262
 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
jens@0
   263
 */