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