Implemented new close protocol with 'bye' meta-message.
5 // Created by Jens Alfke on 5/10/08.
6 // Copyright 2008 Jens Alfke. All rights reserved.
9 #import "BLIPConnection.h"
10 #import "BLIP_Internal.h"
11 #import "BLIPReader.h"
12 #import "BLIPWriter.h"
13 #import "BLIPDispatcher.h"
17 #import "ExceptionUtils.h"
21 NSString* const BLIPErrorDomain = @"BLIP";
23 NSError *BLIPMakeError( int errorCode, NSString *message, ... )
26 va_start(args,message);
27 message = [[NSString alloc] initWithFormat: message arguments: args];
29 LogTo(BLIP,@"BLIPError #%i: %@",errorCode,message);
30 NSDictionary *userInfo = [NSDictionary dictionaryWithObject: message
31 forKey: NSLocalizedDescriptionKey];
33 return [NSError errorWithDomain: BLIPErrorDomain code: errorCode userInfo: userInfo];
37 @interface BLIPConnection ()
38 - (void) _handleCloseRequest: (BLIPRequest*)request;
42 @implementation BLIPConnection
47 [_dispatcher release];
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;}
58 #pragma mark RECEIVING:
61 - (BLIPDispatcher*) dispatcher
64 _dispatcher = [[BLIPDispatcher alloc] init];
65 _dispatcher.parent = ((BLIPListener*)self.server).dispatcher;
71 - (void) _dispatchMetaRequest: (BLIPRequest*)request
73 NSString* profile = request.profile;
74 if( [profile isEqualToString: kBLIPProfile_Bye] )
75 [self _handleCloseRequest: request];
77 [request respondWithErrorCode: kBLIPError_NotFound message: @"Unknown meta profile"];
81 - (void) _dispatchRequest: (BLIPRequest*)request
83 LogTo(BLIP,@"Received all of %@",request.descriptionWithProperties);
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];
93 }@catch( NSException *x ) {
94 MYReportException(x,@"Dispatching BLIP request");
95 [request respondWithException: x];
99 - (void) _dispatchResponse: (BLIPResponse*)response
101 LogTo(BLIP,@"Received all of %@",response);
102 [self tellDelegate: @selector(connection:receivedResponse:) withObject: response];
107 #pragma mark SENDING:
110 - (BLIPRequest*) request
112 return [[[BLIPRequest alloc] _initWithConnection: self body: nil properties: nil] autorelease];
115 - (BLIPRequest*) requestWithBody: (NSData*)body
116 properties: (NSDictionary*)properties
118 return [[[BLIPRequest alloc] _initWithConnection: self body: body properties: properties] autorelease];
121 - (BLIPResponse*) sendRequest: (BLIPRequest*)request
123 BLIPConnection *itsConnection = request.connection;
124 if( itsConnection==nil )
125 request.connection = self;
127 Assert(itsConnection==self,@"%@ is already assigned to a different BLIPConnection",request);
128 return [request send];
133 #pragma mark CLOSING:
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:);
147 // Put the writer in close mode, to prevent client from sending any more requests:
151 - (void) _receivedCloseResponse: (BLIPResponse*)response
153 NSError *error = response.error;
154 LogTo(BLIPVerbose,@"Received close response: error=%@",error);
156 if( [_delegate respondsToSelector: @selector(connection:closeRequestFailedWithError:)] )
157 [_delegate connection: self closeRequestFailedWithError: error];
159 // Now finally close the socket:
165 - (void) _handleCloseRequest: (BLIPRequest*)request
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"];
175 LogTo(BLIPVerbose,@"Close request accepted");
176 _blipClosing = YES; // this prevents _beginClose from sending a close request back
187 @implementation BLIPListener
189 - (id) initWithPort: (UInt16)port
191 self = [super initWithPort: port];
193 self.connectionClass = [BLIPConnection class];
200 [_dispatcher release];
204 - (BLIPDispatcher*) dispatcher
207 _dispatcher = [[BLIPDispatcher alloc] init];
215 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
217 Redistribution and use in source and binary forms, with or without modification, are permitted
218 provided that the following conditions are met:
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
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.