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