jens@0: // jens@0: // BLIPConnection.m jens@0: // MYNetwork jens@0: // jens@0: // Created by Jens Alfke on 5/10/08. jens@0: // Copyright 2008 Jens Alfke. All rights reserved. jens@0: // jens@0: jens@0: #import "BLIPConnection.h" jens@0: #import "BLIP_Internal.h" jens@19: #import "TCP_Internal.h" jens@0: #import "BLIPReader.h" jens@0: #import "BLIPWriter.h" jens@0: #import "BLIPDispatcher.h" jens@0: jens@0: #import "Logging.h" jens@0: #import "Test.h" jens@0: #import "ExceptionUtils.h" jens@18: #import "Target.h" jens@0: jens@0: jens@0: NSString* const BLIPErrorDomain = @"BLIP"; jens@0: jens@0: NSError *BLIPMakeError( int errorCode, NSString *message, ... ) jens@0: { jens@0: va_list args; jens@0: va_start(args,message); jens@0: message = [[NSString alloc] initWithFormat: message arguments: args]; jens@0: va_end(args); jens@0: LogTo(BLIP,@"BLIPError #%i: %@",errorCode,message); jens@0: NSDictionary *userInfo = [NSDictionary dictionaryWithObject: message jens@0: forKey: NSLocalizedDescriptionKey]; jens@0: [message release]; jens@0: return [NSError errorWithDomain: BLIPErrorDomain code: errorCode userInfo: userInfo]; jens@0: } jens@0: jens@0: jens@18: @interface BLIPConnection () jens@18: - (void) _handleCloseRequest: (BLIPRequest*)request; jens@18: @end jens@0: jens@0: jens@0: @implementation BLIPConnection jens@0: jens@18: jens@0: - (void) dealloc jens@0: { jens@0: [_dispatcher release]; jens@0: [super dealloc]; jens@0: } jens@0: jens@0: - (Class) readerClass {return [BLIPReader class];} jens@0: - (Class) writerClass {return [BLIPWriter class];} jens@0: - (id) delegate {return (id)_delegate;} jens@0: - (void) setDelegate: (id)delegate {_delegate = delegate;} jens@0: jens@18: jens@18: #pragma mark - jens@18: #pragma mark RECEIVING: jens@18: jens@18: jens@0: - (BLIPDispatcher*) dispatcher jens@0: { jens@0: if( ! _dispatcher ) { jens@0: _dispatcher = [[BLIPDispatcher alloc] init]; jens@0: _dispatcher.parent = ((BLIPListener*)self.server).dispatcher; jens@0: } jens@0: return _dispatcher; jens@0: } jens@0: jens@0: jens@18: - (void) _dispatchMetaRequest: (BLIPRequest*)request jens@18: { jens@18: NSString* profile = request.profile; jens@18: if( [profile isEqualToString: kBLIPProfile_Bye] ) jens@18: [self _handleCloseRequest: request]; jens@18: else jens@18: [request respondWithErrorCode: kBLIPError_NotFound message: @"Unknown meta profile"]; jens@18: } jens@18: jens@18: jens@0: - (void) _dispatchRequest: (BLIPRequest*)request jens@0: { jens@0: LogTo(BLIP,@"Received all of %@",request.descriptionWithProperties); jens@0: @try{ jens@18: if( request._flags & kBLIP_Meta ) jens@18: [self _dispatchMetaRequest: request]; jens@18: else if( ! [self.dispatcher dispatchMessage: request] ) jens@0: [self tellDelegate: @selector(connection:receivedRequest:) withObject: request]; jens@0: if( ! request.noReply && ! request.repliedTo ) { jens@0: LogTo(BLIP,@"Returning default empty response to %@",request); jens@2: [request respondWithData: nil contentType: nil]; jens@0: } jens@0: }@catch( NSException *x ) { jens@0: MYReportException(x,@"Dispatching BLIP request"); jens@0: [request respondWithException: x]; jens@0: } jens@0: } jens@0: jens@0: - (void) _dispatchResponse: (BLIPResponse*)response jens@0: { jens@0: LogTo(BLIP,@"Received all of %@",response); jens@0: [self tellDelegate: @selector(connection:receivedResponse:) withObject: response]; jens@0: } jens@0: jens@0: jens@18: #pragma mark - jens@18: #pragma mark SENDING: jens@18: jens@18: jens@5: - (BLIPRequest*) request jens@0: { jens@5: return [[[BLIPRequest alloc] _initWithConnection: self body: nil properties: nil] autorelease]; jens@0: } jens@0: jens@0: - (BLIPRequest*) requestWithBody: (NSData*)body jens@0: properties: (NSDictionary*)properties jens@0: { jens@0: return [[[BLIPRequest alloc] _initWithConnection: self body: body properties: properties] autorelease]; jens@0: } jens@0: jens@0: - (BLIPResponse*) sendRequest: (BLIPRequest*)request jens@0: { jens@49: if (!request.isMine || request.sent) { jens@49: // This was an incoming request that I'm being asked to forward or echo; jens@49: // or it's an outgoing request being sent to multiple connections. jens@49: // Since a particular BLIPRequest can only be sent once, make a copy of it to send: jens@49: request = [[request mutableCopy] autorelease]; jens@49: } jens@0: BLIPConnection *itsConnection = request.connection; jens@0: if( itsConnection==nil ) jens@0: request.connection = self; jens@0: else jens@0: Assert(itsConnection==self,@"%@ is already assigned to a different BLIPConnection",request); jens@0: return [request send]; jens@0: } jens@0: jens@0: jens@18: #pragma mark - jens@18: #pragma mark CLOSING: jens@18: jens@18: jens@18: - (void) _beginClose jens@18: { jens@18: // Override of TCPConnection method. Instead of closing the socket, send a 'bye' request: jens@18: if( ! _blipClosing ) { jens@18: LogTo(BLIPVerbose,@"Sending close request..."); jens@18: BLIPRequest *r = [self request]; jens@18: [r _setFlag: kBLIP_Meta value: YES]; jens@18: r.profile = kBLIPProfile_Bye; jens@18: BLIPResponse *response = [r send]; jens@18: response.onComplete = $target(self,_receivedCloseResponse:); jens@18: } jens@18: // Put the writer in close mode, to prevent client from sending any more requests: jens@18: [self.writer close]; jens@18: } jens@18: jens@18: - (void) _receivedCloseResponse: (BLIPResponse*)response jens@18: { jens@18: NSError *error = response.error; jens@18: LogTo(BLIPVerbose,@"Received close response: error=%@",error); jens@18: if( error ) { jens@19: [self _unclose]; jens@19: [self tellDelegate: @selector(connection:closeRequestFailedWithError:) withObject: error]; jens@18: } else { jens@18: // Now finally close the socket: jens@18: [super _beginClose]; jens@18: } jens@18: } jens@18: jens@18: jens@18: - (void) _handleCloseRequest: (BLIPRequest*)request jens@18: { jens@18: LogTo(BLIPVerbose,@"Received a close request"); jens@18: if( [_delegate respondsToSelector: @selector(connectionReceivedCloseRequest:)] ) jens@18: if( ! [_delegate connectionReceivedCloseRequest: self] ) { jens@18: LogTo(BLIPVerbose,@"Responding with denial of close request"); jens@18: [request respondWithErrorCode: kBLIPError_Forbidden message: @"Close request denied"]; jens@18: return; jens@18: } jens@18: jens@18: LogTo(BLIPVerbose,@"Close request accepted"); jens@18: _blipClosing = YES; // this prevents _beginClose from sending a close request back jens@18: [self close]; jens@18: } jens@18: jens@18: jens@0: @end jens@0: jens@0: jens@0: jens@0: jens@18: #pragma mark - jens@0: @implementation BLIPListener jens@0: jens@49: - (id) init jens@0: { jens@49: self = [super init]; jens@0: if (self != nil) { jens@0: self.connectionClass = [BLIPConnection class]; jens@0: } jens@0: return self; jens@0: } jens@0: jens@0: - (void) dealloc jens@0: { jens@0: [_dispatcher release]; jens@0: [super dealloc]; jens@0: } jens@0: jens@0: - (BLIPDispatcher*) dispatcher jens@0: { jens@0: if( ! _dispatcher ) jens@0: _dispatcher = [[BLIPDispatcher alloc] init]; jens@0: return _dispatcher; 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: */