Added a BLIP port of Apple's "CocoaEcho" sample code.
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 _pendingQueries = [[NSMutableDictionary alloc] init];
35 _pendingReplies = [[NSMutableDictionary alloc] init];
42 [_pendingQueries release];
43 [_pendingReplies release];
50 for( BLIPResponse *response in [_pendingReplies allValues] ) {
51 [response _connectionClosed];
52 [_conn tellDelegate: @selector(connection:receivedResponse:) withObject: response];
54 setObj(&_pendingReplies,nil);
60 #pragma mark READING FRAMES:
63 - (NSString*) _validateHeader
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);
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;
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!
116 NSString *err = [self _validateHeader];
118 Warn(@"%@ read bogus frame header: %@",self,err);
119 return (void)[self _gotError: BLIPMakeError(kBLIPError_BadData, @"%@", err)];
121 LogTo(BLIPVerbose,@"%@: Read header; next is %u-byte body",self,_curBody.length);
123 if( _curBody.length == 0 ) {
124 // Zero-byte body, so no need to wait for another read
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);
143 if( bodyRemaining==0 ) {
144 // Done reading this frame: give it to the Connection and reset my state
152 #pragma mark PROCESSING FRAMES:
155 - (void) _addPendingResponse: (BLIPResponse*)response
157 [_pendingReplies setObject: response forKey: $object(response.number)];
161 - (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body
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);
167 id key = $object(header->number);
168 BOOL complete = ! (header->flags & kBLIP_MoreComing);
172 BLIPRequest *request = [_pendingQueries objectForKey: key];
174 // Continuation frame of a request:
176 [[request retain] autorelease];
177 [_pendingQueries removeObjectForKey: key];
179 } else if( header->number == _numQueriesReceived+1 ) {
181 request = [[[BLIPRequest alloc] _initWithConnection: _blipConn
183 flags: header->flags | kBLIP_MoreComing
184 number: header->number
188 [_pendingQueries setObject: request forKey: key];
189 _numQueriesReceived++;
191 return [self _gotError: BLIPMakeError(kBLIPError_BadFrame,
192 @"Received bad request frame #%u (next is #%u)",
193 header->number,_numQueriesReceived+1)];
195 if( ! [request _receivedFrameWithHeader: header body: body] )
196 return [self _gotError: BLIPMakeError(kBLIPError_BadFrame,
197 @"Couldn't parse message frame")];
200 [_blipConn _dispatchRequest: request];
206 BLIPResponse *response = [_pendingReplies objectForKey: key];
209 [[response retain] autorelease];
210 [_pendingReplies removeObjectForKey: key];
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];
220 if( header->number <= ((BLIPWriter*)self.writer).numQueriesSent )
221 LogTo(BLIP,@"??? %@ got unexpected response frame to my msg #%u",
222 self,header->number); //benign
224 return [self _gotError: BLIPMakeError(kBLIPError_BadFrame,
225 @"Bogus message number %u in response",
232 // To leave room for future expansion, undefined message types are just ignored.
233 Log(@"??? %@ received header with unknown message type %i", self,type);
244 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
246 Redistribution and use in source and binary forms, with or without modification, are permitted
247 provided that the following conditions are met:
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
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.