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;
44 _encodedBody = body.mutableCopy;
46 LogTo(BLIPVerbose,@"INIT %@",self);
53 LogTo(BLIPVerbose,@"DEALLOC %@",self);
54 [_properties release];
55 [_encodedBody release];
56 [_mutableBody release];
58 [_connection release];
63 - (NSString*) description
65 NSUInteger length = (_body.length ?: _mutableBody.length) ?: _encodedBody.length;
66 NSMutableString *desc = [NSMutableString stringWithFormat: @"%@[#%u, %u bytes",
67 self.class,_number, length];
68 if( _flags & kBLIP_Compressed ) {
69 if( _encodedBody && _encodedBody.length != length )
70 [desc appendFormat: @" (%u gzipped)", _encodedBody.length];
72 [desc appendString: @", gzipped"];
74 if( _flags & kBLIP_Urgent )
75 [desc appendString: @", urgent"];
76 if( _flags & kBLIP_NoReply )
77 [desc appendString: @", noreply"];
78 if( _flags & kBLIP_Meta )
79 [desc appendString: @", META"];
80 [desc appendString: @"]"];
84 - (NSString*) descriptionWithProperties
86 NSMutableString *desc = (NSMutableString*)self.description;
87 [desc appendFormat: @" %@", self.properties.allProperties];
93 #pragma mark PROPERTIES & METADATA:
96 @synthesize connection=_connection, number=_number, isMine=_isMine, isMutable=_isMutable,
97 _bytesWritten, sent=_sent, propertiesAvailable=_propertiesAvailable, complete=_complete,
98 representedObject=_representedObject;
101 - (void) _setFlag: (BLIPMessageFlags)flag value: (BOOL)value
103 Assert(_isMine && _isMutable);
110 - (BLIPMessageFlags) _flags {return _flags;}
112 - (BOOL) compressed {return (_flags & kBLIP_Compressed) != 0;}
113 - (BOOL) urgent {return (_flags & kBLIP_Urgent) != 0;}
114 - (void) setCompressed: (BOOL)compressed {[self _setFlag: kBLIP_Compressed value: compressed];}
115 - (void) setUrgent: (BOOL)high {[self _setFlag: kBLIP_Urgent value: high];}
120 if( ! _body && _isMine )
121 return [[_mutableBody copy] autorelease];
126 - (void) setBody: (NSData*)body
128 Assert(_isMine && _isMutable);
130 [_mutableBody setData: body];
132 _mutableBody = [body mutableCopy];
135 - (void) _addToBody: (NSData*)data
139 [_mutableBody appendData: data];
141 _mutableBody = [data mutableCopy];
146 - (void) addToBody: (NSData*)data
148 Assert(_isMine && _isMutable);
149 [self _addToBody: data];
153 - (NSString*) bodyString
155 NSData *body = self.body;
157 return [[[NSString alloc] initWithData: body encoding: NSUTF8StringEncoding] autorelease];
162 - (void) setBodyString: (NSString*)string
164 self.body = [string dataUsingEncoding: NSUTF8StringEncoding];
165 self.contentType = @"text/plain; charset=UTF-8";
169 - (BLIPProperties*) properties
174 - (BLIPMutableProperties*) mutableProperties
176 Assert(_isMine && _isMutable);
177 return (BLIPMutableProperties*)_properties;
180 - (NSString*) valueOfProperty: (NSString*)property
182 return [_properties valueOfProperty: property];
185 - (void) setValue: (NSString*)value ofProperty: (NSString*)property
187 [self.mutableProperties setValue: value ofProperty: property];
190 - (NSString*) contentType {return [_properties valueOfProperty: @"Content-Type"];}
191 - (void) setContentType: (NSString*)t {[self setValue: t ofProperty: @"Content-Type"];}
192 - (NSString*) profile {return [_properties valueOfProperty: @"Profile"];}
193 - (void) setProfile: (NSString*)p {[self setValue: p ofProperty: @"Profile"];}
202 Assert(_isMine && _isMutable);
205 BLIPProperties *oldProps = _properties;
206 _properties = [oldProps copy];
209 _encodedBody = [_properties.encodedData mutableCopy];
210 Assert(_encodedBody.length>=2);
212 NSData *body = _body ?: _mutableBody;
213 NSUInteger length = body.length;
215 if( self.compressed ) {
216 body = [NSData gtm_dataByGzippingData: body compressionLevel: 5];
217 LogTo(BLIPVerbose,@"Compressed %@ to %u bytes (%.0f%%)", self,body.length,
218 body.length*100.0/length);
220 [_encodedBody appendData: body];
225 - (void) _assignedNumber: (UInt32)number
227 Assert(_number==0,@"%@ has already been sent",self);
233 - (BOOL) _writeFrameTo: (BLIPWriter*)writer maxSize: (UInt16)maxSize
237 Assert(_encodedBody);
238 if( _bytesWritten==0 )
239 LogTo(BLIP,@"Now sending %@",self);
240 ssize_t lengthToWrite = _encodedBody.length - _bytesWritten;
241 if( lengthToWrite <= 0 && _bytesWritten > 0 )
243 Assert(maxSize > sizeof(BLIPFrameHeader));
244 maxSize -= sizeof(BLIPFrameHeader);
245 UInt16 flags = _flags;
246 if( lengthToWrite > maxSize ) {
247 lengthToWrite = maxSize;
248 flags |= kBLIP_MoreComing;
249 LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u", self, _bytesWritten, _bytesWritten+lengthToWrite);
251 flags &= ~kBLIP_MoreComing;
252 LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u (finished)", self, _bytesWritten, _bytesWritten+lengthToWrite);
255 // First write the frame header:
256 BLIPFrameHeader header = { NSSwapHostIntToBig(kBLIPFrameHeaderMagicNumber),
257 NSSwapHostIntToBig(_number),
258 NSSwapHostShortToBig(flags),
259 NSSwapHostShortToBig(sizeof(BLIPFrameHeader) + lengthToWrite) };
261 [writer writeData: [NSData dataWithBytes: &header length: sizeof(header)]];
263 // Then write the body:
264 if( lengthToWrite > 0 ) {
265 [writer writeData: [NSData dataWithBytes: (UInt8*)_encodedBody.bytes + _bytesWritten
266 length: lengthToWrite]];
267 _bytesWritten += lengthToWrite;
269 return (flags & kBLIP_MoreComing) != 0;
273 - (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body
276 AssertEq(header->number,_number);
277 Assert(_flags & kBLIP_MoreComing);
279 BLIPMessageType frameType = (header->flags & kBLIP_TypeMask), curType = (_flags & kBLIP_TypeMask);
280 if( frameType != curType ) {
281 Assert(curType==kBLIP_RPY && frameType==kBLIP_ERR && _mutableBody.length==0,
282 @"Incoming frame's type %i doesn't match %@",frameType,self);
283 _flags = (_flags & ~kBLIP_TypeMask) | frameType;
287 [_encodedBody appendData: body];
289 _encodedBody = [body mutableCopy];
290 LogTo(BLIPVerbose,@"%@ rcvd bytes %u-%u", self, _encodedBody.length-body.length, _encodedBody.length);
292 if( ! _properties ) {
293 // Try to extract the properties:
295 setObj(&_properties, [BLIPProperties propertiesWithEncodedData: _encodedBody usedLength: &usedLength]);
297 [_encodedBody replaceBytesInRange: NSMakeRange(0,usedLength)
298 withBytes: NULL length: 0];
299 } else if( usedLength < 0 )
301 self.propertiesAvailable = YES;
304 if( ! (header->flags & kBLIP_MoreComing) ) {
305 // After last frame, decode the data:
306 _flags &= ~kBLIP_MoreComing;
309 unsigned encodedLength = _encodedBody.length;
310 if( self.compressed && encodedLength>0 ) {
311 _body = [[NSData gtm_dataByInflatingData: _encodedBody] copy];
314 LogTo(BLIPVerbose,@"Uncompressed %@ from %u bytes (%.1fx)", self, encodedLength,
315 _body.length/(float)encodedLength);
317 _body = [_encodedBody copy];
319 setObj(&_encodedBody,nil);
320 self.propertiesAvailable = self.complete = YES;
326 - (void) _connectionClosed
330 _flags |= kBLIP_MoreComing;
339 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
341 Redistribution and use in source and binary forms, with or without modification, are permitted
342 provided that the following conditions are met:
344 * Redistributions of source code must retain the above copyright notice, this list of conditions
345 and the following disclaimer.
346 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
347 and the following disclaimer in the documentation and/or other materials provided with the
350 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
351 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
352 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
353 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
354 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
355 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
356 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
357 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.