BLIP/BLIPConnection.m
author Jens Alfke <jens@mooseyard.com>
Mon Jun 23 14:02:31 2008 -0700 (2008-06-23)
changeset 19 16454d63d4c2
parent 18 3be241de1630
child 49 20cccc7c26ee
permissions -rw-r--r--
Implemented BLIP 1.1 (explicit 'bye' 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 "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     BLIPConnection *itsConnection = request.connection;
   125     if( itsConnection==nil )
   126         request.connection = self;
   127     else
   128         Assert(itsConnection==self,@"%@ is already assigned to a different BLIPConnection",request);
   129     return [request send];
   130 }
   131 
   132 
   133 #pragma mark -
   134 #pragma mark CLOSING:
   135 
   136 
   137 - (void) _beginClose
   138 {
   139     // Override of TCPConnection method. Instead of closing the socket, send a 'bye' request:
   140     if( ! _blipClosing ) {
   141         LogTo(BLIPVerbose,@"Sending close request...");
   142         BLIPRequest *r = [self request];
   143         [r _setFlag: kBLIP_Meta value: YES];
   144         r.profile = kBLIPProfile_Bye;
   145         BLIPResponse *response = [r send];
   146         response.onComplete = $target(self,_receivedCloseResponse:);
   147     }
   148     // Put the writer in close mode, to prevent client from sending any more requests:
   149     [self.writer close];
   150 }
   151 
   152 - (void) _receivedCloseResponse: (BLIPResponse*)response
   153 {
   154     NSError *error = response.error;
   155     LogTo(BLIPVerbose,@"Received close response: error=%@",error);
   156     if( error ) {
   157         [self _unclose];
   158         [self tellDelegate: @selector(connection:closeRequestFailedWithError:) withObject: error];
   159     } else {
   160         // Now finally close the socket:
   161         [super _beginClose];
   162     }
   163 }
   164 
   165 
   166 - (void) _handleCloseRequest: (BLIPRequest*)request
   167 {
   168     LogTo(BLIPVerbose,@"Received a close request");
   169     if( [_delegate respondsToSelector: @selector(connectionReceivedCloseRequest:)] )
   170         if( ! [_delegate connectionReceivedCloseRequest: self] ) {
   171             LogTo(BLIPVerbose,@"Responding with denial of close request");
   172             [request respondWithErrorCode: kBLIPError_Forbidden message: @"Close request denied"];
   173             return;
   174         }
   175     
   176     LogTo(BLIPVerbose,@"Close request accepted");
   177     _blipClosing = YES; // this prevents _beginClose from sending a close request back
   178     [self close];
   179 }
   180 
   181 
   182 @end
   183 
   184 
   185 
   186 
   187 #pragma mark -
   188 @implementation BLIPListener
   189 
   190 - (id) initWithPort: (UInt16)port
   191 {
   192     self = [super initWithPort: port];
   193     if (self != nil) {
   194         self.connectionClass = [BLIPConnection class];
   195     }
   196     return self;
   197 }
   198 
   199 - (void) dealloc
   200 {
   201     [_dispatcher release];
   202     [super dealloc];
   203 }
   204 
   205 - (BLIPDispatcher*) dispatcher
   206 {
   207     if( ! _dispatcher )
   208         _dispatcher = [[BLIPDispatcher alloc] init];
   209     return _dispatcher;
   210 }
   211 
   212 @end
   213 
   214 
   215 /*
   216  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   217  
   218  Redistribution and use in source and binary forms, with or without modification, are permitted
   219  provided that the following conditions are met:
   220  
   221  * Redistributions of source code must retain the above copyright notice, this list of conditions
   222  and the following disclaimer.
   223  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   224  and the following disclaimer in the documentation and/or other materials provided with the
   225  distribution.
   226  
   227  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   228  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   229  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   230  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   231  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   232   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   233  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   234  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   235  */