jens@0: // jens@0: // BLIPRequest.m jens@0: // MYNetwork jens@0: // jens@0: // Created by Jens Alfke on 5/22/08. jens@0: // Copyright 2008 Jens Alfke. All rights reserved. jens@0: // jens@0: jens@0: #import "BLIPRequest.h" jens@0: #import "BLIP_Internal.h" jens@0: #import "BLIPWriter.h" jens@0: #import "BLIPReader.h" jens@1: jens@0: #import "Target.h" jens@1: #import "Logging.h" jens@1: #import "Test.h" jens@0: #import "ExceptionUtils.h" jens@0: jens@0: jens@0: @implementation BLIPRequest jens@0: jens@0: jens@0: - (id) _initWithConnection: (BLIPConnection*)connection jens@0: body: (NSData*)body jens@0: properties: (NSDictionary*)properties jens@0: { jens@0: self = [self _initWithConnection: connection jens@0: isMine: YES jens@0: flags: kBLIP_MSG jens@0: number: 0 jens@0: body: body]; jens@0: if( self ) { jens@0: _isMutable = YES; jens@0: if( body ) jens@0: self.body = body; jens@0: if( properties ) jens@0: [self.mutableProperties setAllProperties: properties]; jens@0: } jens@0: return self; jens@0: } jens@0: jens@0: + (BLIPRequest*) requestWithBody: (NSData*)body jens@0: { jens@0: return [[[self alloc] _initWithConnection: nil body: body properties: nil] autorelease]; jens@0: } jens@0: jens@49: + (BLIPRequest*) requestWithBodyString: (NSString*)bodyString { jens@49: return [self requestWithBody: [bodyString dataUsingEncoding: NSUTF8StringEncoding]]; jens@49: } jens@49: jens@0: + (BLIPRequest*) requestWithBody: (NSData*)body jens@0: properties: (NSDictionary*)properties jens@0: { jens@0: return [[[self alloc] _initWithConnection: nil body: body properties: properties] autorelease]; jens@0: } jens@0: jens@49: - (id)mutableCopyWithZone:(NSZone *)zone jens@49: { jens@49: Assert(self.complete); jens@49: BLIPRequest *copy = [[self class] requestWithBody: self.body jens@49: properties: self.properties.allProperties]; jens@49: copy.compressed = self.compressed; jens@49: copy.urgent = self.urgent; jens@49: copy.noReply = self.noReply; jens@49: return [copy retain]; jens@49: } jens@49: jens@0: jens@0: - (void) dealloc jens@0: { jens@0: [_response release]; jens@0: [super dealloc]; jens@0: } jens@0: jens@0: jens@0: - (BOOL) noReply {return (_flags & kBLIP_NoReply) != 0;} jens@0: - (void) setNoReply: (BOOL)noReply {[self _setFlag: kBLIP_NoReply value: noReply];} jens@0: - (BLIPConnection*) connection {return _connection;} jens@0: jens@0: - (void) setConnection: (BLIPConnection*)conn jens@0: { jens@0: Assert(_isMine && !_sent,@"Connection can only be set before sending"); jens@0: setObj(&_connection,conn); jens@0: } jens@0: jens@0: jens@0: - (BLIPResponse*) send jens@0: { jens@0: Assert(_connection,@"%@ has no connection to send over",self); jens@0: Assert(!_sent,@"%@ was already sent",self); jens@0: [self _encode]; jens@0: BLIPResponse *response = self.response; jens@0: if( [(BLIPWriter*)_connection.writer sendRequest: self response: response] ) jens@0: self.sent = YES; jens@0: else jens@0: response = nil; jens@0: return response; jens@0: } jens@0: jens@0: jens@0: - (BLIPResponse*) response jens@0: { jens@0: if( ! _response && ! self.noReply ) jens@0: _response = [[BLIPResponse alloc] _initWithRequest: self]; jens@0: return _response; jens@0: } jens@0: jens@0: - (void) deferResponse jens@0: { jens@0: // This will allocate _response, causing -repliedTo to become YES, so BLIPConnection won't jens@0: // send an automatic empty response after the current request handler returns. jens@0: LogTo(BLIP,@"Deferring response to %@",self); jens@0: [self response]; jens@0: } jens@0: jens@0: - (BOOL) repliedTo jens@0: { jens@0: return _response != nil; jens@0: } jens@0: jens@2: - (void) respondWithData: (NSData*)data contentType: (NSString*)contentType jens@2: { jens@2: BLIPResponse *response = self.response; jens@2: response.body = data; jens@2: response.contentType = contentType; jens@2: [response send]; jens@2: } jens@2: jens@2: - (void) respondWithString: (NSString*)string jens@2: { jens@2: [self respondWithData: [string dataUsingEncoding: NSUTF8StringEncoding] jens@2: contentType: @"text/plain; charset=UTF-8"]; jens@2: } jens@2: jens@2: - (void) respondWithError: (NSError*)error jens@2: { jens@2: self.response.error = error; jens@2: [self.response send]; jens@2: } jens@0: jens@0: - (void) respondWithErrorCode: (int)errorCode message: (NSString*)errorMessage jens@0: { jens@0: [self respondWithError: BLIPMakeError(errorCode, @"%@",errorMessage)]; jens@0: } jens@0: jens@0: - (void) respondWithException: (NSException*)exception jens@0: { jens@0: [self respondWithError: BLIPMakeError(kBLIPError_HandlerFailed, @"%@", exception.reason)]; jens@0: } jens@0: jens@0: jens@0: @end jens@0: jens@0: jens@0: jens@0: jens@0: #pragma mark - jens@0: @implementation BLIPResponse jens@0: jens@0: - (id) _initWithRequest: (BLIPRequest*)request jens@0: { jens@0: Assert(request); jens@0: self = [super _initWithConnection: request.connection jens@0: isMine: !request.isMine jens@0: flags: kBLIP_RPY | kBLIP_MoreComing jens@0: number: request.number jens@0: body: nil]; jens@0: if (self != nil) { jens@0: if( _isMine ) { jens@0: _isMutable = YES; jens@0: if( request.urgent ) jens@0: _flags |= kBLIP_Urgent; jens@0: } else { jens@0: _flags |= kBLIP_MoreComing; jens@0: } jens@0: } jens@0: return self; jens@0: } jens@0: jens@0: - (void) dealloc jens@0: { jens@0: [_error release]; jens@0: [_onComplete release]; jens@0: [super dealloc]; jens@0: } jens@0: jens@0: jens@0: - (NSError*) error jens@0: { jens@0: if( ! (_flags & kBLIP_ERR) ) jens@0: return nil; jens@0: jens@22: NSMutableDictionary *userInfo = [[[self.properties allProperties] mutableCopy] autorelease]; jens@0: NSString *domain = [userInfo objectForKey: @"Error-Domain"]; jens@0: int code = [[userInfo objectForKey: @"Error-Code"] intValue]; jens@0: if( domain==nil || code==0 ) { jens@0: domain = BLIPErrorDomain; jens@0: if( code==0 ) jens@0: code = kBLIPError_Unspecified; jens@0: } jens@0: [userInfo removeObjectForKey: @"Error-Domain"]; jens@0: [userInfo removeObjectForKey: @"Error-Code"]; jens@0: return [NSError errorWithDomain: domain code: code userInfo: userInfo]; jens@0: } jens@0: jens@0: - (void) _setError: (NSError*)error jens@0: { jens@0: _flags &= ~kBLIP_TypeMask; jens@0: if( error ) { jens@0: // Setting this stuff is a PITA because this object might be technically immutable, jens@0: // in which case the standard setters would barf if I called them. jens@0: _flags |= kBLIP_ERR; jens@0: setObj(&_body,nil); jens@0: setObj(&_mutableBody,nil); jens@0: jens@0: BLIPMutableProperties *errorProps = [self.properties mutableCopy]; jens@18: if( ! errorProps ) jens@18: errorProps = [[BLIPMutableProperties alloc] init]; jens@0: NSDictionary *userInfo = error.userInfo; jens@0: for( NSString *key in userInfo ) { jens@0: id value = $castIf(NSString,[userInfo objectForKey: key]); jens@0: if( value ) jens@0: [errorProps setValue: value ofProperty: key]; jens@0: } jens@0: [errorProps setValue: error.domain ofProperty: @"Error-Domain"]; jens@0: [errorProps setValue: $sprintf(@"%i",error.code) ofProperty: @"Error-Code"]; jens@0: setObj(&_properties,errorProps); jens@0: [errorProps release]; jens@0: jens@0: } else { jens@0: _flags |= kBLIP_RPY; jens@0: [self.mutableProperties setAllProperties: nil]; jens@0: } jens@0: } jens@0: jens@0: - (void) setError: (NSError*)error jens@0: { jens@0: Assert(_isMine && _isMutable); jens@0: [self _setError: error]; jens@0: } jens@0: jens@0: jens@0: - (BOOL) send jens@0: { jens@0: Assert(_connection,@"%@ has no connection to send over",self); jens@0: Assert(!_sent,@"%@ was already sent",self); jens@18: BLIPWriter *writer = (BLIPWriter*)_connection.writer; jens@18: Assert(writer,@"%@'s connection has no writer (already closed?)",self); jens@0: [self _encode]; jens@18: BOOL sent = self.sent = [writer sendMessage: self]; jens@18: Assert(sent); jens@18: return sent; jens@0: } jens@0: jens@0: jens@0: @synthesize onComplete=_onComplete; jens@0: jens@0: jens@0: - (void) setComplete: (BOOL)complete jens@0: { jens@0: [super setComplete: complete]; jens@0: if( complete && _onComplete ) { jens@0: @try{ jens@0: [_onComplete invokeWithSender: self]; jens@50: }catchAndReport(@"BLIPRequest onComplete target"); jens@0: } jens@0: } jens@0: jens@0: jens@0: - (void) _connectionClosed jens@0: { jens@0: [super _connectionClosed]; jens@22: if( !_isMine && !_complete ) { jens@63: NSError *error = _connection.error; jens@63: if (!error) jens@63: error = BLIPMakeError(kBLIPError_Disconnected, jens@63: @"Connection closed before response was received"); jens@0: // Change incoming response to an error: jens@0: _isMutable = YES; jens@0: [_properties autorelease]; jens@0: _properties = [_properties mutableCopy]; jens@63: [self _setError: error]; jens@0: _isMutable = NO; jens@63: jens@22: self.complete = YES; // Calls onComplete target jens@0: } jens@0: } jens@0: jens@0: jens@0: @end jens@0: jens@0: jens@0: /* jens@0: Copyright (c) 2008, Jens Alfke . All rights reserved. jens@0: jens@0: Redistribution and use in source and binary forms, with or without modification, are permitted jens@0: provided that the following conditions are met: jens@0: jens@0: * Redistributions of source code must retain the above copyright notice, this list of conditions jens@0: and the following disclaimer. jens@0: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions jens@0: and the following disclaimer in the documentation and/or other materials provided with the jens@0: distribution. jens@0: jens@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR jens@0: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND jens@0: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- jens@0: BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES jens@0: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR jens@0: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN jens@0: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF jens@0: THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. jens@0: */