* The BLIPConnection receivedRequest: delegate method now returns BOOL. If the method returns NO (or if the method isn't implemented in the delegate), that means it didn't handle the message at all; an error will be returned to the sender.
* If the connection closes unexpectedly due to an error, then the auto-generated responses to pending requests will contain that error. This makes it easier to display a meaningful error message in the handler for the request.
5 // Created by Jens Alfke on 5/22/08.
6 // Copyright 2008 Jens Alfke. All rights reserved.
9 #import "BLIPRequest.h"
10 #import "BLIP_Internal.h"
11 #import "BLIPWriter.h"
12 #import "BLIPReader.h"
17 #import "ExceptionUtils.h"
20 @implementation BLIPRequest
23 - (id) _initWithConnection: (BLIPConnection*)connection
25 properties: (NSDictionary*)properties
27 self = [self _initWithConnection: connection
37 [self.mutableProperties setAllProperties: properties];
42 + (BLIPRequest*) requestWithBody: (NSData*)body
44 return [[[self alloc] _initWithConnection: nil body: body properties: nil] autorelease];
47 + (BLIPRequest*) requestWithBodyString: (NSString*)bodyString {
48 return [self requestWithBody: [bodyString dataUsingEncoding: NSUTF8StringEncoding]];
51 + (BLIPRequest*) requestWithBody: (NSData*)body
52 properties: (NSDictionary*)properties
54 return [[[self alloc] _initWithConnection: nil body: body properties: properties] autorelease];
57 - (id)mutableCopyWithZone:(NSZone *)zone
59 Assert(self.complete);
60 BLIPRequest *copy = [[self class] requestWithBody: self.body
61 properties: self.properties.allProperties];
62 copy.compressed = self.compressed;
63 copy.urgent = self.urgent;
64 copy.noReply = self.noReply;
76 - (BOOL) noReply {return (_flags & kBLIP_NoReply) != 0;}
77 - (void) setNoReply: (BOOL)noReply {[self _setFlag: kBLIP_NoReply value: noReply];}
78 - (BLIPConnection*) connection {return _connection;}
80 - (void) setConnection: (BLIPConnection*)conn
82 Assert(_isMine && !_sent,@"Connection can only be set before sending");
83 setObj(&_connection,conn);
87 - (BLIPResponse*) send
89 Assert(_connection,@"%@ has no connection to send over",self);
90 Assert(!_sent,@"%@ was already sent",self);
92 BLIPResponse *response = self.response;
93 if( [(BLIPWriter*)_connection.writer sendRequest: self response: response] )
101 - (BLIPResponse*) response
103 if( ! _response && ! self.noReply )
104 _response = [[BLIPResponse alloc] _initWithRequest: self];
108 - (void) deferResponse
110 // This will allocate _response, causing -repliedTo to become YES, so BLIPConnection won't
111 // send an automatic empty response after the current request handler returns.
112 LogTo(BLIP,@"Deferring response to %@",self);
118 return _response != nil;
121 - (void) respondWithData: (NSData*)data contentType: (NSString*)contentType
123 BLIPResponse *response = self.response;
124 response.body = data;
125 response.contentType = contentType;
129 - (void) respondWithString: (NSString*)string
131 [self respondWithData: [string dataUsingEncoding: NSUTF8StringEncoding]
132 contentType: @"text/plain; charset=UTF-8"];
135 - (void) respondWithError: (NSError*)error
137 self.response.error = error;
138 [self.response send];
141 - (void) respondWithErrorCode: (int)errorCode message: (NSString*)errorMessage
143 [self respondWithError: BLIPMakeError(errorCode, @"%@",errorMessage)];
146 - (void) respondWithException: (NSException*)exception
148 [self respondWithError: BLIPMakeError(kBLIPError_HandlerFailed, @"%@", exception.reason)];
158 @implementation BLIPResponse
160 - (id) _initWithRequest: (BLIPRequest*)request
163 self = [super _initWithConnection: request.connection
164 isMine: !request.isMine
165 flags: kBLIP_RPY | kBLIP_MoreComing
166 number: request.number
172 _flags |= kBLIP_Urgent;
174 _flags |= kBLIP_MoreComing;
183 [_onComplete release];
190 if( ! (_flags & kBLIP_ERR) )
193 NSMutableDictionary *userInfo = [[[self.properties allProperties] mutableCopy] autorelease];
194 NSString *domain = [userInfo objectForKey: @"Error-Domain"];
195 int code = [[userInfo objectForKey: @"Error-Code"] intValue];
196 if( domain==nil || code==0 ) {
197 domain = BLIPErrorDomain;
199 code = kBLIPError_Unspecified;
201 [userInfo removeObjectForKey: @"Error-Domain"];
202 [userInfo removeObjectForKey: @"Error-Code"];
203 return [NSError errorWithDomain: domain code: code userInfo: userInfo];
206 - (void) _setError: (NSError*)error
208 _flags &= ~kBLIP_TypeMask;
210 // Setting this stuff is a PITA because this object might be technically immutable,
211 // in which case the standard setters would barf if I called them.
214 setObj(&_mutableBody,nil);
216 BLIPMutableProperties *errorProps = [self.properties mutableCopy];
218 errorProps = [[BLIPMutableProperties alloc] init];
219 NSDictionary *userInfo = error.userInfo;
220 for( NSString *key in userInfo ) {
221 id value = $castIf(NSString,[userInfo objectForKey: key]);
223 [errorProps setValue: value ofProperty: key];
225 [errorProps setValue: error.domain ofProperty: @"Error-Domain"];
226 [errorProps setValue: $sprintf(@"%i",error.code) ofProperty: @"Error-Code"];
227 setObj(&_properties,errorProps);
228 [errorProps release];
232 [self.mutableProperties setAllProperties: nil];
236 - (void) setError: (NSError*)error
238 Assert(_isMine && _isMutable);
239 [self _setError: error];
245 Assert(_connection,@"%@ has no connection to send over",self);
246 Assert(!_sent,@"%@ was already sent",self);
247 BLIPWriter *writer = (BLIPWriter*)_connection.writer;
248 Assert(writer,@"%@'s connection has no writer (already closed?)",self);
250 BOOL sent = self.sent = [writer sendMessage: self];
256 @synthesize onComplete=_onComplete;
259 - (void) setComplete: (BOOL)complete
261 [super setComplete: complete];
262 if( complete && _onComplete ) {
264 [_onComplete invokeWithSender: self];
265 }catchAndReport(@"BLIPRequest onComplete target");
270 - (void) _connectionClosed
272 [super _connectionClosed];
273 if( !_isMine && !_complete ) {
274 NSError *error = _connection.error;
276 error = BLIPMakeError(kBLIPError_Disconnected,
277 @"Connection closed before response was received");
278 // Change incoming response to an error:
280 [_properties autorelease];
281 _properties = [_properties mutableCopy];
282 [self _setError: error];
285 self.complete = YES; // Calls onComplete target
294 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
296 Redistribution and use in source and binary forms, with or without modification, are permitted
297 provided that the following conditions are met:
299 * Redistributions of source code must retain the above copyright notice, this list of conditions
300 and the following disclaimer.
301 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
302 and the following disclaimer in the documentation and/or other materials provided with the
305 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
306 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
307 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
308 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
309 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
310 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
311 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
312 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.