BLIP/BLIPConnection.m
author Jens Alfke <jens@mooseyard.com>
Wed Jul 01 14:14:32 2009 -0700 (2009-07-01)
changeset 50 63baa74c903f
parent 19 16454d63d4c2
child 63 5e4855a592ee
permissions -rw-r--r--
Fix to BLIPMessage for Chatty (mark new outgoing BLIPMessages as "complete".)
Lots of fixes for Bonjour stuff, including making the hostname lookup asynchronous in BonjourService.
     1 //
     2 //  BLIPConnection.m
     3 //  MYNetwork
     4 //
     5 //  Created by Jens Alfke on 5/10/08.
     6 //  Copyright 2008 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "BLIPConnection.h"
    10 #import "BLIP_Internal.h"
    11 #import "TCP_Internal.h"
    12 #import "BLIPReader.h"
    13 #import "BLIPWriter.h"
    14 #import "BLIPDispatcher.h"
    15 
    16 #import "Logging.h"
    17 #import "Test.h"
    18 #import "ExceptionUtils.h"
    19 #import "Target.h"
    20 
    21 
    22 NSString* const BLIPErrorDomain = @"BLIP";
    23 
    24 NSError *BLIPMakeError( int errorCode, NSString *message, ... )
    25 {
    26     va_list args;
    27     va_start(args,message);
    28     message = [[NSString alloc] initWithFormat: message arguments: args];
    29     va_end(args);
    30     LogTo(BLIP,@"BLIPError #%i: %@",errorCode,message);
    31     NSDictionary *userInfo = [NSDictionary dictionaryWithObject: message
    32                                                          forKey: NSLocalizedDescriptionKey];
    33     [message release];
    34     return [NSError errorWithDomain: BLIPErrorDomain code: errorCode userInfo: userInfo];
    35 }
    36 
    37 
    38 @interface BLIPConnection ()
    39 - (void) _handleCloseRequest: (BLIPRequest*)request;
    40 @end
    41 
    42 
    43 @implementation BLIPConnection
    44 
    45 
    46 - (void) dealloc
    47 {
    48     [_dispatcher release];
    49     [super dealloc];
    50 }
    51 
    52 - (Class) readerClass                                       {return [BLIPReader class];}
    53 - (Class) writerClass                                       {return [BLIPWriter class];}
    54 - (id<BLIPConnectionDelegate>) delegate                     {return (id)_delegate;}
    55 - (void) setDelegate: (id<BLIPConnectionDelegate>)delegate  {_delegate = delegate;}
    56 
    57 
    58 #pragma mark -
    59 #pragma mark RECEIVING:
    60 
    61 
    62 - (BLIPDispatcher*) dispatcher
    63 {
    64     if( ! _dispatcher ) {
    65         _dispatcher = [[BLIPDispatcher alloc] init];
    66         _dispatcher.parent = ((BLIPListener*)self.server).dispatcher;
    67     }
    68     return _dispatcher;
    69 }
    70 
    71 
    72 - (void) _dispatchMetaRequest: (BLIPRequest*)request
    73 {
    74     NSString* profile = request.profile;
    75     if( [profile isEqualToString: kBLIPProfile_Bye] )
    76         [self _handleCloseRequest: request];
    77     else
    78         [request respondWithErrorCode: kBLIPError_NotFound message: @"Unknown meta profile"];
    79 }
    80 
    81 
    82 - (void) _dispatchRequest: (BLIPRequest*)request
    83 {
    84     LogTo(BLIP,@"Received all of %@",request.descriptionWithProperties);
    85     @try{
    86         if( request._flags & kBLIP_Meta )
    87             [self _dispatchMetaRequest: request];
    88         else if( ! [self.dispatcher dispatchMessage: request] )
    89             [self tellDelegate: @selector(connection:receivedRequest:) withObject: request];
    90         if( ! request.noReply && ! request.repliedTo ) {
    91             LogTo(BLIP,@"Returning default empty response to %@",request);
    92             [request respondWithData: nil contentType: nil];
    93         }
    94     }@catch( NSException *x ) {
    95         MYReportException(x,@"Dispatching BLIP request");
    96         [request respondWithException: x];
    97     }
    98 }
    99 
   100 - (void) _dispatchResponse: (BLIPResponse*)response
   101 {
   102     LogTo(BLIP,@"Received all of %@",response);
   103     [self tellDelegate: @selector(connection:receivedResponse:) withObject: response];
   104 }
   105 
   106 
   107 #pragma mark -
   108 #pragma mark SENDING:
   109 
   110 
   111 - (BLIPRequest*) request
   112 {
   113     return [[[BLIPRequest alloc] _initWithConnection: self body: nil properties: nil] autorelease];
   114 }
   115 
   116 - (BLIPRequest*) requestWithBody: (NSData*)body
   117                       properties: (NSDictionary*)properties
   118 {
   119     return [[[BLIPRequest alloc] _initWithConnection: self body: body properties: properties] autorelease];
   120 }
   121 
   122 - (BLIPResponse*) sendRequest: (BLIPRequest*)request
   123 {
   124     if (!request.isMine || request.sent) {
   125         // This was an incoming request that I'm being asked to forward or echo;
   126         // or it's an outgoing request being sent to multiple connections.
   127         // Since a particular BLIPRequest can only be sent once, make a copy of it to send:
   128         request = [[request mutableCopy] autorelease];
   129     }
   130     BLIPConnection *itsConnection = request.connection;
   131     if( itsConnection==nil )
   132         request.connection = self;
   133     else
   134         Assert(itsConnection==self,@"%@ is already assigned to a different BLIPConnection",request);
   135     return [request send];
   136 }
   137 
   138 
   139 #pragma mark -
   140 #pragma mark CLOSING:
   141 
   142 
   143 - (void) _beginClose
   144 {
   145     // Override of TCPConnection method. Instead of closing the socket, send a 'bye' request:
   146     if( ! _blipClosing ) {
   147         LogTo(BLIPVerbose,@"Sending close request...");
   148         BLIPRequest *r = [self request];
   149         [r _setFlag: kBLIP_Meta value: YES];
   150         r.profile = kBLIPProfile_Bye;
   151         BLIPResponse *response = [r send];
   152         response.onComplete = $target(self,_receivedCloseResponse:);
   153     }
   154     // Put the writer in close mode, to prevent client from sending any more requests:
   155     [self.writer close];
   156 }
   157 
   158 - (void) _receivedCloseResponse: (BLIPResponse*)response
   159 {
   160     NSError *error = response.error;
   161     LogTo(BLIPVerbose,@"Received close response: error=%@",error);
   162     if( error ) {
   163         [self _unclose];
   164         [self tellDelegate: @selector(connection:closeRequestFailedWithError:) withObject: error];
   165     } else {
   166         // Now finally close the socket:
   167         [super _beginClose];
   168     }
   169 }
   170 
   171 
   172 - (void) _handleCloseRequest: (BLIPRequest*)request
   173 {
   174     LogTo(BLIPVerbose,@"Received a close request");
   175     if( [_delegate respondsToSelector: @selector(connectionReceivedCloseRequest:)] )
   176         if( ! [_delegate connectionReceivedCloseRequest: self] ) {
   177             LogTo(BLIPVerbose,@"Responding with denial of close request");
   178             [request respondWithErrorCode: kBLIPError_Forbidden message: @"Close request denied"];
   179             return;
   180         }
   181     
   182     LogTo(BLIPVerbose,@"Close request accepted");
   183     _blipClosing = YES; // this prevents _beginClose from sending a close request back
   184     [self close];
   185 }
   186 
   187 
   188 @end
   189 
   190 
   191 
   192 
   193 #pragma mark -
   194 @implementation BLIPListener
   195 
   196 - (id) init
   197 {
   198     self = [super init];
   199     if (self != nil) {
   200         self.connectionClass = [BLIPConnection class];
   201     }
   202     return self;
   203 }
   204 
   205 - (void) dealloc
   206 {
   207     [_dispatcher release];
   208     [super dealloc];
   209 }
   210 
   211 - (BLIPDispatcher*) dispatcher
   212 {
   213     if( ! _dispatcher )
   214         _dispatcher = [[BLIPDispatcher alloc] init];
   215     return _dispatcher;
   216 }
   217 
   218 @end
   219 
   220 
   221 /*
   222  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   223  
   224  Redistribution and use in source and binary forms, with or without modification, are permitted
   225  provided that the following conditions are met:
   226  
   227  * Redistributions of source code must retain the above copyright notice, this list of conditions
   228  and the following disclaimer.
   229  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   230  and the following disclaimer in the documentation and/or other materials provided with the
   231  distribution.
   232  
   233  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   234  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   235  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   236  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   237  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   238   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   239  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   240  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   241  */