Implemented new close protocol with 'bye' meta-message.
5 // Created by Jens Alfke on 5/10/08.
6 // Copyright 2008 Jens Alfke. All rights reserved.
9 #import "BLIPMessage.h"
10 #import "BLIP_Internal.h"
11 #import "BLIPReader.h"
12 #import "BLIPWriter.h"
16 #import "ExceptionUtils.h"
19 // From Google Toolbox For Mac <http://code.google.com/p/google-toolbox-for-mac/>
20 #import "GTMNSData+zlib.h"
23 @implementation BLIPMessage
26 - (id) _initWithConnection: (BLIPConnection*)connection
28 flags: (BLIPMessageFlags)flags
34 _connection = [connection retain];
40 _properties = [[BLIPMutableProperties alloc] init];
41 _propertiesAvailable = YES;
43 _encodedBody = body.mutableCopy;
45 LogTo(BLIPVerbose,@"INIT %@",self);
52 LogTo(BLIPVerbose,@"DEALLOC %@",self);
53 [_properties release];
54 [_encodedBody release];
55 [_mutableBody release];
57 [_connection release];
62 - (NSString*) description
64 NSUInteger length = (_body.length ?: _mutableBody.length) ?: _encodedBody.length;
65 NSMutableString *desc = [NSMutableString stringWithFormat: @"%@[#%u, %u bytes",
66 self.class,_number, length];
67 if( _flags & kBLIP_Compressed ) {
68 if( _encodedBody && _encodedBody.length != length )
69 [desc appendFormat: @" (%u gzipped)", _encodedBody.length];
71 [desc appendString: @", gzipped"];
73 if( _flags & kBLIP_Urgent )
74 [desc appendString: @", urgent"];
75 if( _flags & kBLIP_NoReply )
76 [desc appendString: @", noreply"];
77 if( _flags & kBLIP_Meta )
78 [desc appendString: @", META"];
79 [desc appendString: @"]"];
83 - (NSString*) descriptionWithProperties
85 NSMutableString *desc = (NSMutableString*)self.description;
86 [desc appendFormat: @" %@", self.properties.allProperties];
92 #pragma mark PROPERTIES & METADATA:
95 @synthesize connection=_connection, number=_number, isMine=_isMine, isMutable=_isMutable,
96 _bytesWritten, sent=_sent, propertiesAvailable=_propertiesAvailable, complete=_complete;
99 - (void) _setFlag: (BLIPMessageFlags)flag value: (BOOL)value
101 Assert(_isMine && _isMutable);
108 - (BLIPMessageFlags) _flags {return _flags;}
110 - (BOOL) compressed {return (_flags & kBLIP_Compressed) != 0;}
111 - (BOOL) urgent {return (_flags & kBLIP_Urgent) != 0;}
112 - (void) setCompressed: (BOOL)compressed {[self _setFlag: kBLIP_Compressed value: compressed];}
113 - (void) setUrgent: (BOOL)high {[self _setFlag: kBLIP_Urgent value: high];}
118 if( ! _body && _isMine )
119 return [[_mutableBody copy] autorelease];
124 - (void) setBody: (NSData*)body
126 Assert(_isMine && _isMutable);
128 [_mutableBody setData: body];
130 _mutableBody = [body mutableCopy];
133 - (void) _addToBody: (NSData*)data
137 [_mutableBody appendData: data];
139 _mutableBody = [data mutableCopy];
144 - (void) addToBody: (NSData*)data
146 Assert(_isMine && _isMutable);
147 [self _addToBody: data];
151 - (NSString*) bodyString
153 NSData *body = self.body;
155 return [[[NSString alloc] initWithData: body encoding: NSUTF8StringEncoding] autorelease];
160 - (void) setBodyString: (NSString*)string
162 self.body = [string dataUsingEncoding: NSUTF8StringEncoding];
163 self.contentType = @"text/plain; charset=UTF-8";
167 - (BLIPProperties*) properties
172 - (BLIPMutableProperties*) mutableProperties
174 Assert(_isMine && _isMutable);
175 return (BLIPMutableProperties*)_properties;
178 - (NSString*) valueOfProperty: (NSString*)property
180 return [_properties valueOfProperty: property];
183 - (void) setValue: (NSString*)value ofProperty: (NSString*)property
185 [self.mutableProperties setValue: value ofProperty: property];
188 - (NSString*) contentType {return [_properties valueOfProperty: @"Content-Type"];}
189 - (void) setContentType: (NSString*)t {[self setValue: t ofProperty: @"Content-Type"];}
190 - (NSString*) profile {return [_properties valueOfProperty: @"Profile"];}
191 - (void) setProfile: (NSString*)p {[self setValue: p ofProperty: @"Profile"];}
200 Assert(_isMine && _isMutable);
203 BLIPProperties *oldProps = _properties;
204 _properties = [oldProps copy];
207 _encodedBody = [_properties.encodedData mutableCopy];
208 Assert(_encodedBody.length>=2);
210 NSData *body = _body ?: _mutableBody;
211 NSUInteger length = body.length;
213 if( self.compressed ) {
214 body = [NSData gtm_dataByGzippingData: body compressionLevel: 5];
215 LogTo(BLIPVerbose,@"Compressed %@ to %u bytes (%.0f%%)", self,body.length,
216 body.length*100.0/length);
218 [_encodedBody appendData: body];
223 - (void) _assignedNumber: (UInt32)number
225 Assert(_number==0,@"%@ has already been sent",self);
231 - (BOOL) _writeFrameTo: (BLIPWriter*)writer maxSize: (UInt16)maxSize
235 Assert(_encodedBody);
236 if( _bytesWritten==0 )
237 LogTo(BLIP,@"Now sending %@",self);
238 ssize_t lengthToWrite = _encodedBody.length - _bytesWritten;
239 if( lengthToWrite <= 0 && _bytesWritten > 0 )
241 Assert(maxSize > sizeof(BLIPFrameHeader));
242 maxSize -= sizeof(BLIPFrameHeader);
243 UInt16 flags = _flags;
244 if( lengthToWrite > maxSize ) {
245 lengthToWrite = maxSize;
246 flags |= kBLIP_MoreComing;
247 LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u", self, _bytesWritten, _bytesWritten+lengthToWrite);
249 flags &= ~kBLIP_MoreComing;
250 LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u (finished)", self, _bytesWritten, _bytesWritten+lengthToWrite);
253 // First write the frame header:
254 BLIPFrameHeader header = { NSSwapHostIntToBig(kBLIPFrameHeaderMagicNumber),
255 NSSwapHostIntToBig(_number),
256 NSSwapHostShortToBig(flags),
257 NSSwapHostShortToBig(sizeof(BLIPFrameHeader) + lengthToWrite) };
259 [writer writeData: [NSData dataWithBytes: &header length: sizeof(header)]];
261 // Then write the body:
262 if( lengthToWrite > 0 ) {
263 [writer writeData: [NSData dataWithBytes: (UInt8*)_encodedBody.bytes + _bytesWritten
264 length: lengthToWrite]];
265 _bytesWritten += lengthToWrite;
267 return (flags & kBLIP_MoreComing) != 0;
271 - (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body
274 AssertEq(header->number,_number);
275 Assert(_flags & kBLIP_MoreComing);
277 BLIPMessageType frameType = (header->flags & kBLIP_TypeMask), curType = (_flags & kBLIP_TypeMask);
278 if( frameType != curType ) {
279 Assert(curType==kBLIP_RPY && frameType==kBLIP_ERR && _mutableBody.length==0,
280 @"Incoming frame's type %i doesn't match %@",frameType,self);
281 _flags = (_flags & ~kBLIP_TypeMask) | frameType;
285 [_encodedBody appendData: body];
287 _encodedBody = [body mutableCopy];
288 LogTo(BLIPVerbose,@"%@ rcvd bytes %u-%u", self, _encodedBody.length-body.length, _encodedBody.length);
290 if( ! _properties ) {
291 // Try to extract the properties:
293 setObj(&_properties, [BLIPProperties propertiesWithEncodedData: _encodedBody usedLength: &usedLength]);
295 [_encodedBody replaceBytesInRange: NSMakeRange(0,usedLength)
296 withBytes: NULL length: 0];
297 } else if( usedLength < 0 )
299 self.propertiesAvailable = YES;
302 if( ! (header->flags & kBLIP_MoreComing) ) {
303 // After last frame, decode the data:
304 _flags &= ~kBLIP_MoreComing;
307 unsigned encodedLength = _encodedBody.length;
308 if( self.compressed && encodedLength>0 ) {
309 _body = [[NSData gtm_dataByInflatingData: _encodedBody] copy];
312 LogTo(BLIPVerbose,@"Uncompressed %@ from %u bytes (%.1fx)", self, encodedLength,
313 _body.length/(float)encodedLength);
315 _body = [_encodedBody copy];
317 setObj(&_encodedBody,nil);
318 self.propertiesAvailable = self.complete = YES;
324 - (void) _connectionClosed
328 _flags |= kBLIP_MoreComing;
337 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
339 Redistribution and use in source and binary forms, with or without modification, are permitted
340 provided that the following conditions are met:
342 * Redistributions of source code must retain the above copyright notice, this list of conditions
343 and the following disclaimer.
344 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
345 and the following disclaimer in the documentation and/or other materials provided with the
348 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
349 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
350 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
351 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
352 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
353 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
354 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
355 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.