Added -[TCPConnection initToNetService:] to make it easier to use with Bonjour. This allowed me to simplify BLIPEchoClient quite a lot.
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 = { EndianU32_NtoB(kBLIPFrameHeaderMagicNumber),
251 EndianU32_NtoB(_number),
252 EndianU16_NtoB(flags),
253 EndianU16_NtoB(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 dataWithBytesNoCopy: (UInt8*)_encodedBody.bytes + _bytesWritten
260 length: lengthToWrite
262 _bytesWritten += lengthToWrite;
264 return (flags & kBLIP_MoreComing) != 0;
268 - (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body
271 AssertEq(header->number,_number);
272 Assert(_flags & kBLIP_MoreComing);
274 BLIPMessageType frameType = (header->flags & kBLIP_TypeMask), curType = (_flags & kBLIP_TypeMask);
275 if( frameType != curType ) {
276 Assert(curType==kBLIP_RPY && frameType==kBLIP_ERR && _mutableBody.length==0,
277 @"Incoming frame's type %i doesn't match %@",frameType,self);
278 _flags = (_flags & ~kBLIP_TypeMask) | frameType;
282 [_encodedBody appendData: body];
284 _encodedBody = [body mutableCopy];
285 LogTo(BLIPVerbose,@"%@ rcvd bytes %u-%u", self, _encodedBody.length-body.length, _encodedBody.length);
287 if( ! _properties ) {
288 // Try to extract the properties:
290 setObj(&_properties, [BLIPProperties propertiesWithEncodedData: _encodedBody usedLength: &usedLength]);
292 [_encodedBody replaceBytesInRange: NSMakeRange(0,usedLength)
293 withBytes: NULL length: 0];
294 } else if( usedLength < 0 )
296 self.propertiesAvailable = YES;
299 if( ! (header->flags & kBLIP_MoreComing) ) {
300 // After last frame, decode the data:
301 _flags &= ~kBLIP_MoreComing;
304 unsigned encodedLength = _encodedBody.length;
305 if( self.compressed && encodedLength>0 ) {
306 _body = [[NSData gtm_dataByInflatingData: _encodedBody] copy];
309 LogTo(BLIPVerbose,@"Uncompressed %@ from %u bytes (%.1fx)", self, encodedLength,
310 _body.length/(float)encodedLength);
312 _body = [_encodedBody copy];
314 setObj(&_encodedBody,nil);
315 self.propertiesAvailable = self.complete = YES;
321 - (void) _connectionClosed
325 _flags |= kBLIP_MoreComing;
334 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
336 Redistribution and use in source and binary forms, with or without modification, are permitted
337 provided that the following conditions are met:
339 * Redistributions of source code must retain the above copyright notice, this list of conditions
340 and the following disclaimer.
341 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
342 and the following disclaimer in the documentation and/or other materials provided with the
345 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
346 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
347 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
348 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
349 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
350 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
351 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
352 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.