Python implementation much improved. Can send requests now. Fully interoperable with Obj-C implementation's test cases.
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 [desc appendString: @"]"];
81 - (NSString*) descriptionWithProperties
83 NSMutableString *desc = (NSMutableString*)self.description;
84 [desc appendFormat: @" %@", self.properties.allProperties];
90 #pragma mark PROPERTIES & METADATA:
93 @synthesize connection=_connection, number=_number, isMine=_isMine, isMutable=_isMutable,
94 _bytesWritten, sent=_sent, propertiesAvailable=_propertiesAvailable, complete=_complete;
97 - (void) _setFlag: (BLIPMessageFlags)flag value: (BOOL)value
99 Assert(_isMine && _isMutable);
106 - (BOOL) compressed {return (_flags & kBLIP_Compressed) != 0;}
107 - (BOOL) urgent {return (_flags & kBLIP_Urgent) != 0;}
108 - (void) setCompressed: (BOOL)compressed {[self _setFlag: kBLIP_Compressed value: compressed];}
109 - (void) setUrgent: (BOOL)high {[self _setFlag: kBLIP_Urgent value: high];}
114 if( ! _body && _isMine )
115 return [[_mutableBody copy] autorelease];
120 - (void) setBody: (NSData*)body
122 Assert(_isMine && _isMutable);
124 [_mutableBody setData: body];
126 _mutableBody = [body mutableCopy];
129 - (void) _addToBody: (NSData*)data
133 [_mutableBody appendData: data];
135 _mutableBody = [data mutableCopy];
140 - (void) addToBody: (NSData*)data
142 Assert(_isMine && _isMutable);
143 [self _addToBody: data];
147 - (NSString*) bodyString
149 NSData *body = self.body;
151 return [[[NSString alloc] initWithData: body encoding: NSUTF8StringEncoding] autorelease];
156 - (void) setBodyString: (NSString*)string
158 self.body = [string dataUsingEncoding: NSUTF8StringEncoding];
159 self.contentType = @"text/plain; charset=UTF-8";
163 - (BLIPProperties*) properties
168 - (BLIPMutableProperties*) mutableProperties
170 Assert(_isMine && _isMutable);
171 return (BLIPMutableProperties*)_properties;
174 - (NSString*) valueOfProperty: (NSString*)property
176 return [_properties valueOfProperty: property];
179 - (void) setValue: (NSString*)value ofProperty: (NSString*)property
181 [self.mutableProperties setValue: value ofProperty: property];
184 - (NSString*) contentType {return [_properties valueOfProperty: @"Content-Type"];}
185 - (void) setContentType: (NSString*)t {[self setValue: t ofProperty: @"Content-Type"];}
186 - (NSString*) profile {return [_properties valueOfProperty: @"Profile"];}
187 - (void) setProfile: (NSString*)p {[self setValue: p ofProperty: @"Profile"];}
196 Assert(_isMine && _isMutable);
199 BLIPProperties *oldProps = _properties;
200 _properties = [oldProps copy];
203 _encodedBody = [_properties.encodedData mutableCopy];
204 Assert(_encodedBody.length>=2);
206 NSData *body = _body ?: _mutableBody;
207 NSUInteger length = body.length;
209 if( self.compressed ) {
210 body = [NSData gtm_dataByGzippingData: body compressionLevel: 5];
211 LogTo(BLIPVerbose,@"Compressed %@ to %u bytes (%.0f%%)", self,body.length,
212 body.length*100.0/length);
214 [_encodedBody appendData: body];
219 - (void) _assignedNumber: (UInt32)number
221 Assert(_number==0,@"%@ has already been sent",self);
227 - (BOOL) _writeFrameTo: (BLIPWriter*)writer maxSize: (UInt16)maxSize
231 Assert(_encodedBody);
232 if( _bytesWritten==0 )
233 LogTo(BLIP,@"Now sending %@",self);
234 ssize_t lengthToWrite = _encodedBody.length - _bytesWritten;
235 if( lengthToWrite <= 0 && _bytesWritten > 0 )
237 Assert(maxSize > sizeof(BLIPFrameHeader));
238 maxSize -= sizeof(BLIPFrameHeader);
239 UInt16 flags = _flags;
240 if( lengthToWrite > maxSize ) {
241 lengthToWrite = maxSize;
242 flags |= kBLIP_MoreComing;
243 LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u", self, _bytesWritten, _bytesWritten+lengthToWrite);
245 flags &= ~kBLIP_MoreComing;
246 LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u (finished)", self, _bytesWritten, _bytesWritten+lengthToWrite);
249 // First write the frame header:
250 BLIPFrameHeader header = { NSSwapHostIntToBig(kBLIPFrameHeaderMagicNumber),
251 NSSwapHostIntToBig(_number),
252 NSSwapHostShortToBig(flags),
253 NSSwapHostShortToBig(sizeof(BLIPFrameHeader) + lengthToWrite) };
255 [writer writeData: [NSData dataWithBytes: &header length: sizeof(header)]];
257 // Then write the body:
258 if( lengthToWrite > 0 ) {
259 [writer writeData: [NSData dataWithBytes: (UInt8*)_encodedBody.bytes + _bytesWritten
260 length: lengthToWrite]];
261 _bytesWritten += lengthToWrite;
263 return (flags & kBLIP_MoreComing) != 0;
267 - (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body
270 AssertEq(header->number,_number);
271 Assert(_flags & kBLIP_MoreComing);
273 BLIPMessageType frameType = (header->flags & kBLIP_TypeMask), curType = (_flags & kBLIP_TypeMask);
274 if( frameType != curType ) {
275 Assert(curType==kBLIP_RPY && frameType==kBLIP_ERR && _mutableBody.length==0,
276 @"Incoming frame's type %i doesn't match %@",frameType,self);
277 _flags = (_flags & ~kBLIP_TypeMask) | frameType;
281 [_encodedBody appendData: body];
283 _encodedBody = [body mutableCopy];
284 LogTo(BLIPVerbose,@"%@ rcvd bytes %u-%u", self, _encodedBody.length-body.length, _encodedBody.length);
286 if( ! _properties ) {
287 // Try to extract the properties:
289 setObj(&_properties, [BLIPProperties propertiesWithEncodedData: _encodedBody usedLength: &usedLength]);
291 [_encodedBody replaceBytesInRange: NSMakeRange(0,usedLength)
292 withBytes: NULL length: 0];
293 } else if( usedLength < 0 )
295 self.propertiesAvailable = YES;
298 if( ! (header->flags & kBLIP_MoreComing) ) {
299 // After last frame, decode the data:
300 _flags &= ~kBLIP_MoreComing;
303 unsigned encodedLength = _encodedBody.length;
304 if( self.compressed && encodedLength>0 ) {
305 _body = [[NSData gtm_dataByInflatingData: _encodedBody] copy];
308 LogTo(BLIPVerbose,@"Uncompressed %@ from %u bytes (%.1fx)", self, encodedLength,
309 _body.length/(float)encodedLength);
311 _body = [_encodedBody copy];
313 setObj(&_encodedBody,nil);
314 self.propertiesAvailable = self.complete = YES;
320 - (void) _connectionClosed
324 _flags |= kBLIP_MoreComing;
333 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
335 Redistribution and use in source and binary forms, with or without modification, are permitted
336 provided that the following conditions are met:
338 * Redistributions of source code must retain the above copyright notice, this list of conditions
339 and the following disclaimer.
340 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
341 and the following disclaimer in the documentation and/or other materials provided with the
344 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
345 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
346 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
347 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
348 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
349 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
350 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
351 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.