jens@0
|
1 |
//
|
jens@0
|
2 |
// BLIPMessage.m
|
jens@0
|
3 |
// MYNetwork
|
jens@0
|
4 |
//
|
jens@0
|
5 |
// Created by Jens Alfke on 5/10/08.
|
jens@0
|
6 |
// Copyright 2008 Jens Alfke. All rights reserved.
|
jens@0
|
7 |
//
|
jens@0
|
8 |
|
jens@0
|
9 |
#import "BLIPMessage.h"
|
jens@0
|
10 |
#import "BLIP_Internal.h"
|
jens@0
|
11 |
#import "BLIPReader.h"
|
jens@0
|
12 |
#import "BLIPWriter.h"
|
jens@0
|
13 |
|
jens@1
|
14 |
#import "Logging.h"
|
jens@1
|
15 |
#import "Test.h"
|
jens@0
|
16 |
#import "ExceptionUtils.h"
|
jens@0
|
17 |
#import "Target.h"
|
jens@0
|
18 |
|
jens@0
|
19 |
// From Google Toolbox For Mac <http://code.google.com/p/google-toolbox-for-mac/>
|
jens@0
|
20 |
#import "GTMNSData+zlib.h"
|
jens@0
|
21 |
|
jens@0
|
22 |
|
jens@0
|
23 |
@implementation BLIPMessage
|
jens@0
|
24 |
|
jens@0
|
25 |
|
jens@0
|
26 |
- (id) _initWithConnection: (BLIPConnection*)connection
|
jens@0
|
27 |
isMine: (BOOL)isMine
|
jens@0
|
28 |
flags: (BLIPMessageFlags)flags
|
jens@0
|
29 |
number: (UInt32)msgNo
|
jens@0
|
30 |
body: (NSData*)body
|
jens@0
|
31 |
{
|
jens@0
|
32 |
self = [super init];
|
jens@0
|
33 |
if (self != nil) {
|
jens@0
|
34 |
_connection = [connection retain];
|
jens@0
|
35 |
_isMine = isMine;
|
jens@0
|
36 |
_flags = flags;
|
jens@0
|
37 |
_number = msgNo;
|
jens@0
|
38 |
if( isMine ) {
|
jens@0
|
39 |
_body = body.copy;
|
jens@0
|
40 |
_properties = [[BLIPMutableProperties alloc] init];
|
jens@0
|
41 |
_propertiesAvailable = YES;
|
jens@0
|
42 |
} else {
|
jens@0
|
43 |
_encodedBody = body.mutableCopy;
|
jens@0
|
44 |
}
|
jens@0
|
45 |
LogTo(BLIPVerbose,@"INIT %@",self);
|
jens@0
|
46 |
}
|
jens@0
|
47 |
return self;
|
jens@0
|
48 |
}
|
jens@0
|
49 |
|
jens@0
|
50 |
- (void) dealloc
|
jens@0
|
51 |
{
|
jens@0
|
52 |
LogTo(BLIPVerbose,@"DEALLOC %@",self);
|
jens@0
|
53 |
[_properties release];
|
jens@0
|
54 |
[_encodedBody release];
|
jens@0
|
55 |
[_mutableBody release];
|
jens@0
|
56 |
[_body release];
|
jens@0
|
57 |
[_connection release];
|
jens@0
|
58 |
[super dealloc];
|
jens@0
|
59 |
}
|
jens@0
|
60 |
|
jens@0
|
61 |
|
jens@0
|
62 |
- (NSString*) description
|
jens@0
|
63 |
{
|
jens@0
|
64 |
NSUInteger length = (_body.length ?: _mutableBody.length) ?: _encodedBody.length;
|
jens@0
|
65 |
NSMutableString *desc = [NSMutableString stringWithFormat: @"%@[#%u, %u bytes",
|
jens@0
|
66 |
self.class,_number, length];
|
jens@0
|
67 |
if( _flags & kBLIP_Compressed ) {
|
jens@0
|
68 |
if( _encodedBody && _encodedBody.length != length )
|
jens@0
|
69 |
[desc appendFormat: @" (%u gzipped)", _encodedBody.length];
|
jens@0
|
70 |
else
|
jens@0
|
71 |
[desc appendString: @", gzipped"];
|
jens@0
|
72 |
}
|
jens@0
|
73 |
if( _flags & kBLIP_Urgent )
|
jens@0
|
74 |
[desc appendString: @", urgent"];
|
jens@0
|
75 |
if( _flags & kBLIP_NoReply )
|
jens@0
|
76 |
[desc appendString: @", noreply"];
|
jens@0
|
77 |
[desc appendString: @"]"];
|
jens@0
|
78 |
return desc;
|
jens@0
|
79 |
}
|
jens@0
|
80 |
|
jens@0
|
81 |
- (NSString*) descriptionWithProperties
|
jens@0
|
82 |
{
|
jens@0
|
83 |
NSMutableString *desc = (NSMutableString*)self.description;
|
jens@0
|
84 |
[desc appendFormat: @" %@", self.properties.allProperties];
|
jens@0
|
85 |
return desc;
|
jens@0
|
86 |
}
|
jens@0
|
87 |
|
jens@0
|
88 |
|
jens@0
|
89 |
#pragma mark -
|
jens@0
|
90 |
#pragma mark PROPERTIES & METADATA:
|
jens@0
|
91 |
|
jens@0
|
92 |
|
jens@0
|
93 |
@synthesize connection=_connection, number=_number, isMine=_isMine, isMutable=_isMutable,
|
jens@0
|
94 |
_bytesWritten, sent=_sent, propertiesAvailable=_propertiesAvailable, complete=_complete;
|
jens@0
|
95 |
|
jens@0
|
96 |
|
jens@0
|
97 |
- (void) _setFlag: (BLIPMessageFlags)flag value: (BOOL)value
|
jens@0
|
98 |
{
|
jens@0
|
99 |
Assert(_isMine && _isMutable);
|
jens@0
|
100 |
if( value )
|
jens@0
|
101 |
_flags |= flag;
|
jens@0
|
102 |
else
|
jens@0
|
103 |
_flags &= ~flag;
|
jens@0
|
104 |
}
|
jens@0
|
105 |
|
jens@0
|
106 |
- (BOOL) compressed {return (_flags & kBLIP_Compressed) != 0;}
|
jens@0
|
107 |
- (BOOL) urgent {return (_flags & kBLIP_Urgent) != 0;}
|
jens@0
|
108 |
- (void) setCompressed: (BOOL)compressed {[self _setFlag: kBLIP_Compressed value: compressed];}
|
jens@0
|
109 |
- (void) setUrgent: (BOOL)high {[self _setFlag: kBLIP_Urgent value: high];}
|
jens@0
|
110 |
|
jens@0
|
111 |
|
jens@0
|
112 |
- (NSData*) body
|
jens@0
|
113 |
{
|
jens@0
|
114 |
if( ! _body && _isMine )
|
jens@0
|
115 |
return [[_mutableBody copy] autorelease];
|
jens@0
|
116 |
else
|
jens@0
|
117 |
return _body;
|
jens@0
|
118 |
}
|
jens@0
|
119 |
|
jens@0
|
120 |
- (void) setBody: (NSData*)body
|
jens@0
|
121 |
{
|
jens@0
|
122 |
Assert(_isMine && _isMutable);
|
jens@0
|
123 |
if( _mutableBody )
|
jens@0
|
124 |
[_mutableBody setData: body];
|
jens@0
|
125 |
else
|
jens@0
|
126 |
_mutableBody = [body mutableCopy];
|
jens@0
|
127 |
}
|
jens@0
|
128 |
|
jens@0
|
129 |
- (void) _addToBody: (NSData*)data
|
jens@0
|
130 |
{
|
jens@0
|
131 |
if( data.length ) {
|
jens@0
|
132 |
if( _mutableBody )
|
jens@0
|
133 |
[_mutableBody appendData: data];
|
jens@0
|
134 |
else
|
jens@0
|
135 |
_mutableBody = [data mutableCopy];
|
jens@0
|
136 |
setObj(&_body,nil);
|
jens@0
|
137 |
}
|
jens@0
|
138 |
}
|
jens@0
|
139 |
|
jens@0
|
140 |
- (void) addToBody: (NSData*)data
|
jens@0
|
141 |
{
|
jens@0
|
142 |
Assert(_isMine && _isMutable);
|
jens@0
|
143 |
[self _addToBody: data];
|
jens@0
|
144 |
}
|
jens@0
|
145 |
|
jens@0
|
146 |
|
jens@3
|
147 |
- (NSString*) bodyString
|
jens@3
|
148 |
{
|
jens@3
|
149 |
NSData *body = self.body;
|
jens@3
|
150 |
if( body )
|
jens@3
|
151 |
return [[[NSString alloc] initWithData: body encoding: NSUTF8StringEncoding] autorelease];
|
jens@3
|
152 |
else
|
jens@3
|
153 |
return nil;
|
jens@3
|
154 |
}
|
jens@3
|
155 |
|
jens@3
|
156 |
- (void) setBodyString: (NSString*)string
|
jens@3
|
157 |
{
|
jens@3
|
158 |
self.body = [string dataUsingEncoding: NSUTF8StringEncoding];
|
jens@3
|
159 |
self.contentType = @"text/plain; charset=UTF-8";
|
jens@3
|
160 |
}
|
jens@3
|
161 |
|
jens@3
|
162 |
|
jens@0
|
163 |
- (BLIPProperties*) properties
|
jens@0
|
164 |
{
|
jens@0
|
165 |
return _properties;
|
jens@0
|
166 |
}
|
jens@0
|
167 |
|
jens@0
|
168 |
- (BLIPMutableProperties*) mutableProperties
|
jens@0
|
169 |
{
|
jens@0
|
170 |
Assert(_isMine && _isMutable);
|
jens@0
|
171 |
return (BLIPMutableProperties*)_properties;
|
jens@0
|
172 |
}
|
jens@0
|
173 |
|
jens@0
|
174 |
- (NSString*) valueOfProperty: (NSString*)property
|
jens@0
|
175 |
{
|
jens@0
|
176 |
return [_properties valueOfProperty: property];
|
jens@0
|
177 |
}
|
jens@0
|
178 |
|
jens@0
|
179 |
- (void) setValue: (NSString*)value ofProperty: (NSString*)property
|
jens@0
|
180 |
{
|
jens@0
|
181 |
[self.mutableProperties setValue: value ofProperty: property];
|
jens@0
|
182 |
}
|
jens@0
|
183 |
|
jens@0
|
184 |
- (NSString*) contentType {return [_properties valueOfProperty: @"Content-Type"];}
|
jens@0
|
185 |
- (void) setContentType: (NSString*)t {[self setValue: t ofProperty: @"Content-Type"];}
|
jens@0
|
186 |
- (NSString*) profile {return [_properties valueOfProperty: @"Profile"];}
|
jens@0
|
187 |
- (void) setProfile: (NSString*)p {[self setValue: p ofProperty: @"Profile"];}
|
jens@0
|
188 |
|
jens@0
|
189 |
|
jens@0
|
190 |
#pragma mark -
|
jens@0
|
191 |
#pragma mark I/O:
|
jens@0
|
192 |
|
jens@0
|
193 |
|
jens@0
|
194 |
- (void) _encode
|
jens@0
|
195 |
{
|
jens@0
|
196 |
Assert(_isMine && _isMutable);
|
jens@0
|
197 |
_isMutable = NO;
|
jens@0
|
198 |
|
jens@0
|
199 |
BLIPProperties *oldProps = _properties;
|
jens@0
|
200 |
_properties = [oldProps copy];
|
jens@0
|
201 |
[oldProps release];
|
jens@0
|
202 |
|
jens@0
|
203 |
_encodedBody = [_properties.encodedData mutableCopy];
|
jens@0
|
204 |
Assert(_encodedBody.length>=2);
|
jens@0
|
205 |
|
jens@0
|
206 |
NSData *body = _body ?: _mutableBody;
|
jens@0
|
207 |
NSUInteger length = body.length;
|
jens@0
|
208 |
if( length > 0 ) {
|
jens@0
|
209 |
if( self.compressed ) {
|
jens@0
|
210 |
body = [NSData gtm_dataByGzippingData: body compressionLevel: 5];
|
jens@0
|
211 |
LogTo(BLIPVerbose,@"Compressed %@ to %u bytes (%.0f%%)", self,body.length,
|
jens@0
|
212 |
body.length*100.0/length);
|
jens@0
|
213 |
}
|
jens@0
|
214 |
[_encodedBody appendData: body];
|
jens@0
|
215 |
}
|
jens@0
|
216 |
}
|
jens@0
|
217 |
|
jens@0
|
218 |
|
jens@0
|
219 |
- (void) _assignedNumber: (UInt32)number
|
jens@0
|
220 |
{
|
jens@0
|
221 |
Assert(_number==0,@"%@ has already been sent",self);
|
jens@0
|
222 |
_number = number;
|
jens@0
|
223 |
_isMutable = NO;
|
jens@0
|
224 |
}
|
jens@0
|
225 |
|
jens@0
|
226 |
|
jens@0
|
227 |
- (BOOL) _writeFrameTo: (BLIPWriter*)writer maxSize: (UInt16)maxSize
|
jens@0
|
228 |
{
|
jens@0
|
229 |
Assert(_number!=0);
|
jens@0
|
230 |
Assert(_isMine);
|
jens@0
|
231 |
Assert(_encodedBody);
|
jens@0
|
232 |
if( _bytesWritten==0 )
|
jens@0
|
233 |
LogTo(BLIP,@"Now sending %@",self);
|
jens@0
|
234 |
ssize_t lengthToWrite = _encodedBody.length - _bytesWritten;
|
jens@0
|
235 |
if( lengthToWrite <= 0 && _bytesWritten > 0 )
|
jens@0
|
236 |
return NO; // done
|
jens@0
|
237 |
Assert(maxSize > sizeof(BLIPFrameHeader));
|
jens@0
|
238 |
maxSize -= sizeof(BLIPFrameHeader);
|
jens@0
|
239 |
UInt16 flags = _flags;
|
jens@0
|
240 |
if( lengthToWrite > maxSize ) {
|
jens@0
|
241 |
lengthToWrite = maxSize;
|
jens@0
|
242 |
flags |= kBLIP_MoreComing;
|
jens@0
|
243 |
LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u", self, _bytesWritten, _bytesWritten+lengthToWrite);
|
jens@0
|
244 |
} else {
|
jens@0
|
245 |
flags &= ~kBLIP_MoreComing;
|
jens@0
|
246 |
LogTo(BLIPVerbose,@"%@ pushing frame, bytes %u-%u (finished)", self, _bytesWritten, _bytesWritten+lengthToWrite);
|
jens@0
|
247 |
}
|
jens@0
|
248 |
|
jens@0
|
249 |
// First write the frame header:
|
jens@8
|
250 |
BLIPFrameHeader header = { NSSwapHostIntToBig(kBLIPFrameHeaderMagicNumber),
|
jens@8
|
251 |
NSSwapHostIntToBig(_number),
|
jens@8
|
252 |
NSSwapHostShortToBig(flags),
|
jens@8
|
253 |
NSSwapHostShortToBig(sizeof(BLIPFrameHeader) + lengthToWrite) };
|
jens@0
|
254 |
|
jens@0
|
255 |
[writer writeData: [NSData dataWithBytes: &header length: sizeof(header)]];
|
jens@0
|
256 |
|
jens@0
|
257 |
// Then write the body:
|
jens@0
|
258 |
if( lengthToWrite > 0 ) {
|
jens@0
|
259 |
[writer writeData: [NSData dataWithBytesNoCopy: (UInt8*)_encodedBody.bytes + _bytesWritten
|
jens@0
|
260 |
length: lengthToWrite
|
jens@0
|
261 |
freeWhenDone: NO]];
|
jens@0
|
262 |
_bytesWritten += lengthToWrite;
|
jens@0
|
263 |
}
|
jens@0
|
264 |
return (flags & kBLIP_MoreComing) != 0;
|
jens@0
|
265 |
}
|
jens@0
|
266 |
|
jens@0
|
267 |
|
jens@0
|
268 |
- (BOOL) _receivedFrameWithHeader: (const BLIPFrameHeader*)header body: (NSData*)body
|
jens@0
|
269 |
{
|
jens@0
|
270 |
Assert(!_isMine);
|
jens@0
|
271 |
AssertEq(header->number,_number);
|
jens@0
|
272 |
Assert(_flags & kBLIP_MoreComing);
|
jens@0
|
273 |
|
jens@0
|
274 |
BLIPMessageType frameType = (header->flags & kBLIP_TypeMask), curType = (_flags & kBLIP_TypeMask);
|
jens@0
|
275 |
if( frameType != curType ) {
|
jens@0
|
276 |
Assert(curType==kBLIP_RPY && frameType==kBLIP_ERR && _mutableBody.length==0,
|
jens@0
|
277 |
@"Incoming frame's type %i doesn't match %@",frameType,self);
|
jens@0
|
278 |
_flags = (_flags & ~kBLIP_TypeMask) | frameType;
|
jens@0
|
279 |
}
|
jens@0
|
280 |
|
jens@0
|
281 |
if( _encodedBody )
|
jens@0
|
282 |
[_encodedBody appendData: body];
|
jens@0
|
283 |
else
|
jens@0
|
284 |
_encodedBody = [body mutableCopy];
|
jens@0
|
285 |
LogTo(BLIPVerbose,@"%@ rcvd bytes %u-%u", self, _encodedBody.length-body.length, _encodedBody.length);
|
jens@0
|
286 |
|
jens@0
|
287 |
if( ! _properties ) {
|
jens@0
|
288 |
// Try to extract the properties:
|
jens@0
|
289 |
ssize_t usedLength;
|
jens@0
|
290 |
setObj(&_properties, [BLIPProperties propertiesWithEncodedData: _encodedBody usedLength: &usedLength]);
|
jens@0
|
291 |
if( _properties ) {
|
jens@0
|
292 |
[_encodedBody replaceBytesInRange: NSMakeRange(0,usedLength)
|
jens@0
|
293 |
withBytes: NULL length: 0];
|
jens@0
|
294 |
} else if( usedLength < 0 )
|
jens@0
|
295 |
return NO;
|
jens@0
|
296 |
self.propertiesAvailable = YES;
|
jens@0
|
297 |
}
|
jens@0
|
298 |
|
jens@0
|
299 |
if( ! (header->flags & kBLIP_MoreComing) ) {
|
jens@0
|
300 |
// After last frame, decode the data:
|
jens@0
|
301 |
_flags &= ~kBLIP_MoreComing;
|
jens@0
|
302 |
if( ! _properties )
|
jens@0
|
303 |
return NO;
|
jens@0
|
304 |
unsigned encodedLength = _encodedBody.length;
|
jens@0
|
305 |
if( self.compressed && encodedLength>0 ) {
|
jens@0
|
306 |
_body = [[NSData gtm_dataByInflatingData: _encodedBody] copy];
|
jens@0
|
307 |
if( ! _body )
|
jens@0
|
308 |
return NO;
|
jens@0
|
309 |
LogTo(BLIPVerbose,@"Uncompressed %@ from %u bytes (%.1fx)", self, encodedLength,
|
jens@0
|
310 |
_body.length/(float)encodedLength);
|
jens@0
|
311 |
} else {
|
jens@0
|
312 |
_body = [_encodedBody copy];
|
jens@0
|
313 |
}
|
jens@0
|
314 |
setObj(&_encodedBody,nil);
|
jens@0
|
315 |
self.propertiesAvailable = self.complete = YES;
|
jens@0
|
316 |
}
|
jens@0
|
317 |
return YES;
|
jens@0
|
318 |
}
|
jens@0
|
319 |
|
jens@0
|
320 |
|
jens@0
|
321 |
- (void) _connectionClosed
|
jens@0
|
322 |
{
|
jens@0
|
323 |
if( _isMine ) {
|
jens@0
|
324 |
_bytesWritten = 0;
|
jens@0
|
325 |
_flags |= kBLIP_MoreComing;
|
jens@0
|
326 |
}
|
jens@0
|
327 |
}
|
jens@0
|
328 |
|
jens@0
|
329 |
|
jens@0
|
330 |
@end
|
jens@0
|
331 |
|
jens@0
|
332 |
|
jens@0
|
333 |
/*
|
jens@0
|
334 |
Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
|
jens@0
|
335 |
|
jens@0
|
336 |
Redistribution and use in source and binary forms, with or without modification, are permitted
|
jens@0
|
337 |
provided that the following conditions are met:
|
jens@0
|
338 |
|
jens@0
|
339 |
* Redistributions of source code must retain the above copyright notice, this list of conditions
|
jens@0
|
340 |
and the following disclaimer.
|
jens@0
|
341 |
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions
|
jens@0
|
342 |
and the following disclaimer in the documentation and/or other materials provided with the
|
jens@0
|
343 |
distribution.
|
jens@0
|
344 |
|
jens@0
|
345 |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
jens@0
|
346 |
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
jens@0
|
347 |
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
|
jens@0
|
348 |
BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
jens@0
|
349 |
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
jens@0
|
350 |
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
jens@0
|
351 |
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
jens@0
|
352 |
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
jens@0
|
353 |
*/
|