5 // Created by Jens Alfke on 5/10/08.
6 // Copyright 2008 Jens Alfke. All rights reserved.
10 #import "BLIP_Internal.h"
11 #import "BLIPWriter.h"
12 #import "BLIPDispatcher.h"
16 #import "CollectionUtils.h"
19 @interface BLIPReader ()
20 - (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body;
24 @implementation BLIPReader
27 #define _blipConn ((BLIPConnection*)_conn)
30 - (id) initWithConnection: (BLIPConnection*)conn stream: (NSStream*)stream
32 self = [super initWithConnection: conn stream: stream];
34 _pendingRequests = [[NSMutableDictionary alloc] init];
35 _pendingResponses = [[NSMutableDictionary alloc] init];
42 [_pendingRequests release];
43 [_pendingResponses release];
50 for( BLIPResponse *response in [_pendingResponses allValues] ) {
51 [response _connectionClosed];
52 [_conn tellDelegate: @selector(connection:receivedResponse:) withObject: response];
54 setObj(&_pendingResponses,nil);
60 #pragma mark READING FRAMES:
63 - (NSString*) _validateHeader
65 // Convert header to native byte order:
66 _curHeader.magic = NSSwapBigIntToHost(_curHeader.magic);
67 _curHeader.number= NSSwapBigIntToHost(_curHeader.number);
68 _curHeader.flags = NSSwapBigShortToHost(_curHeader.flags);
69 _curHeader.size = NSSwapBigShortToHost(_curHeader.size);
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];
86 [self _receivedFrameWithHeader: &_curHeader body: _curBody];
87 memset(&_curHeader,0,sizeof(_curHeader));
88 setObj(&_curBody,nil);
96 return _curBytesRead > 0 || _pendingRequests.count > 0 || _pendingResponses.count > 0;
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);
114 // Finished reading the header!
115 NSString *err = [self _validateHeader];
117 Warn(@"%@ read bogus frame header: %@",self,err);
118 return (void)[self _gotError: BLIPMakeError(kBLIPError_BadData, @"%@", err)];
120 LogTo(BLIPVerbose,@"%@: Read header; next is %u-byte body",self,_curBody.length);
122 if( _curBody.length == 0 ) {
123 // Zero-byte body, so no need to wait for another read
130 // Read (more of) the current frame's body:
131 SInt32 bodyRemaining = (SInt32)_curBody.length + headerLeft;
132 if( bodyRemaining > 0 ) {
133 uint8_t *dst = _curBody.mutableBytes;
134 dst += _curBody.length - bodyRemaining;
135 NSInteger bytesRead = [self read: dst maxLength: bodyRemaining];
136 if( bytesRead > 0 ) {
137 _curBytesRead += bytesRead;
138 bodyRemaining -= bytesRead;
139 LogTo(BLIPVerbose,@"%@: Read %u bytes of frame body (%u left)",self,bytesRead,bodyRemaining);
142 if( bodyRemaining==0 ) {
143 // Done reading this frame: give it to the Connection and reset my state
151 #pragma mark PROCESSING FRAMES:
154 - (void) _addPendingResponse: (BLIPResponse*)response
156 [_pendingResponses setObject: response forKey: $object(response.number)];
160 - (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body
162 static const char* kTypeStrs[16] = {"MSG","RPY","ERR","3??","4??","5??","6??","7??"};
163 BLIPMessageType type = header->flags & kBLIP_TypeMask;
164 LogTo(BLIPVerbose,@"%@ rcvd frame of %s #%u, length %u",self,kTypeStrs[type],header->number,body.length);
166 id key = $object(header->number);
167 BOOL complete = ! (header->flags & kBLIP_MoreComing);
171 BLIPRequest *request = [_pendingRequests objectForKey: key];
173 // Continuation frame of a request:
175 [[request retain] autorelease];
176 [_pendingRequests removeObjectForKey: key];
178 } else if( header->number == _numRequestsReceived+1 ) {
180 request = [[[BLIPRequest alloc] _initWithConnection: _blipConn
182 flags: header->flags | kBLIP_MoreComing
183 number: header->number
187 [_pendingRequests setObject: request forKey: key];
188 _numRequestsReceived++;
190 return [self _gotError: BLIPMakeError(kBLIPError_BadFrame,
191 @"Received bad request frame #%u (next is #%u)",
192 header->number,_numRequestsReceived+1)];
194 if( ! [request _receivedFrameWithHeader: header body: body] )
195 return [self _gotError: BLIPMakeError(kBLIPError_BadFrame,
196 @"Couldn't parse message frame")];
199 [_blipConn _dispatchRequest: request];
205 BLIPResponse *response = [_pendingResponses objectForKey: key];
208 [[response retain] autorelease];
209 [_pendingResponses removeObjectForKey: key];
212 if( ! [response _receivedFrameWithHeader: header body: body] ) {
213 return [self _gotError: BLIPMakeError(kBLIPError_BadFrame,
214 @"Couldn't parse response frame")];
215 } else if( complete )
216 [_blipConn _dispatchResponse: response];
219 if( header->number <= ((BLIPWriter*)self.writer).numRequestsSent )
220 LogTo(BLIP,@"??? %@ got unexpected response frame to my msg #%u",
221 self,header->number); //benign
223 return [self _gotError: BLIPMakeError(kBLIPError_BadFrame,
224 @"Bogus message number %u in response",
231 // To leave room for future expansion, undefined message types are just ignored.
232 Log(@"??? %@ received header with unknown message type %i", self,type);
243 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
245 Redistribution and use in source and binary forms, with or without modification, are permitted
246 provided that the following conditions are met:
248 * Redistributions of source code must retain the above copyright notice, this list of conditions
249 and the following disclaimer.
250 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
251 and the following disclaimer in the documentation and/or other materials provided with the
254 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
255 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
256 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
257 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
258 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
259 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
260 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
261 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.