1 /******************************************************************************* 2 3 Base class for a connection handler for use with SelectListener. 4 5 Copyright: 6 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 7 All rights reserved. 8 9 License: 10 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 11 Alternatively, this file may be distributed under the terms of the Tango 12 3-Clause BSD License (see LICENSE_BSD.txt for details). 13 14 *******************************************************************************/ 15 16 module ocean.net.server.connection.IConnectionHandler; 17 18 19 20 21 import ocean.core.Verify; 22 23 import ocean.io.select.EpollSelectDispatcher; 24 25 import ocean.net.server.connection.IConnectionHandlerInfo; 26 import ocean.io.select.client.model.ISelectClient : IAdvancedSelectClient; 27 28 import ocean.io.select.protocol.generic.ErrnoIOException: SocketError; 29 30 import ocean.io.device.IODevice: IInputDevice, IOutputDevice; 31 32 import ocean.io.model.IConduit: ISelectable; 33 34 import ocean.sys.socket.model.ISocket; 35 36 debug ( ConnectionHandler ) import ocean.io.Stdout : Stderr; 37 38 39 /******************************************************************************* 40 41 Connection handler abstract base class. 42 43 *******************************************************************************/ 44 45 abstract class IConnectionHandler : IConnectionHandlerInfo, 46 IAdvancedSelectClient.IErrorReporter 47 { 48 /*************************************************************************** 49 50 Object pool index. 51 52 ***************************************************************************/ 53 54 public size_t object_pool_index; 55 56 /*************************************************************************** 57 58 Local aliases to avoid public imports. 59 60 ***************************************************************************/ 61 62 public alias .EpollSelectDispatcher EpollSelectDispatcher; 63 64 protected alias IAdvancedSelectClient.Event Event; 65 66 /*************************************************************************** 67 68 Client connection socket, exposed to subclasses downcast to Conduit. 69 70 ***************************************************************************/ 71 72 protected ISocket socket; 73 74 /*************************************************************************** 75 76 SocketError instance to throw on error and query the current socket 77 error status. 78 79 ***************************************************************************/ 80 81 protected SocketError socket_error; 82 83 /*************************************************************************** 84 85 Alias for a finalizer delegate, which can be specified externally and is 86 called when the connection is shut down. 87 88 ***************************************************************************/ 89 90 public alias void delegate ( typeof (this) instance ) FinalizeDg; 91 92 /*************************************************************************** 93 94 Finalizer delegate which can be specified externally and is called when 95 the connection is shut down. 96 97 ***************************************************************************/ 98 99 private FinalizeDg finalize_dg_ = null; 100 101 /*************************************************************************** 102 103 Alias for an error delegate, which can be specified externally and is 104 called when a connection error occurs. 105 106 ***************************************************************************/ 107 108 public alias void delegate ( Exception exception, Event event, 109 IConnectionHandlerInfo ) ErrorDg; 110 111 /*************************************************************************** 112 113 Error delegate, which can be specified externally and is called when a 114 connection error occurs. 115 116 ***************************************************************************/ 117 118 private ErrorDg error_dg_ = null; 119 120 /*************************************************************************** 121 122 Instance id number in debug builds. 123 124 ***************************************************************************/ 125 126 debug 127 { 128 static private uint connection_count; 129 public uint connection_id; 130 } 131 132 /*************************************************************************** 133 134 Constructor 135 136 Params: 137 socket = the socket 138 error_dg_ = optional user-specified error handler, called when a 139 connection error occurs 140 141 ***************************************************************************/ 142 143 protected this ( ISocket socket, scope ErrorDg error_dg_ = null ) 144 { 145 this(socket, null, error_dg_); 146 } 147 148 /*************************************************************************** 149 150 Constructor 151 152 Params: 153 socket = the socket 154 finalize_dg_ = optional user-specified finalizer, called when the 155 connection is shut down 156 error_dg_ = optional user-specified error handler, called when a 157 connection error occurs 158 159 ***************************************************************************/ 160 161 protected this ( ISocket socket, scope FinalizeDg finalize_dg_ = null, 162 scope ErrorDg error_dg_ = null ) 163 { 164 verify(socket !is null); 165 166 this.finalize_dg_ = finalize_dg_; 167 this.error_dg_ = error_dg_; 168 169 this.socket = socket; 170 171 this.socket_error = new SocketError(this.socket); 172 173 debug this.connection_id = connection_count++; 174 } 175 176 /*************************************************************************** 177 178 Sets the finalizer callback delegate which is called when the 179 connection is shut down. Setting to null disables the finalizer. 180 181 Params: 182 finalize_dg_ = finalizer callback delegate 183 184 Returns: 185 finalize_dg_ 186 187 ***************************************************************************/ 188 189 public FinalizeDg finalize_dg ( scope FinalizeDg finalize_dg_ ) 190 { 191 return this.finalize_dg_ = finalize_dg_; 192 } 193 194 /*************************************************************************** 195 196 Sets the error handler callback delegate which is called when a 197 connection error occurs. Setting to null disables the error handler. 198 199 Params: 200 error_dg_ = error callback delegate 201 202 Returns: 203 error_dg_ 204 205 ***************************************************************************/ 206 207 public ErrorDg error_dg ( scope ErrorDg error_dg_ ) 208 { 209 return this.error_dg_ = error_dg_; 210 } 211 212 /*************************************************************************** 213 214 Returns: 215 true if a client connection is currently established or false if 216 not. 217 218 ***************************************************************************/ 219 220 public bool connected ( ) 221 { 222 return this.socket.fileHandle >= 0; 223 } 224 225 /*************************************************************************** 226 227 Returns: 228 I/O device instance (file descriptor under linux) 229 230 ***************************************************************************/ 231 232 public ISelectable.Handle fileHandle ( ) 233 { 234 return this.socket.fileHandle; 235 } 236 237 /*************************************************************************** 238 239 Accepts a pending connection from listening_socket and assigns it to the 240 socket of this instance. 241 242 Params: 243 listening_socket = the listening server socket for which a client 244 connection is pending 245 246 ***************************************************************************/ 247 248 public void assign ( ISelectable listening_socket ) 249 { 250 verify(!this.connected, 251 "client connection was open before assigning"); 252 253 debug ( ConnectionHandler ) Stderr.formatln("[{}]: New connection", this.connection_id); 254 255 if (this.socket.accept(listening_socket, true) < 0) 256 { 257 this.error(this.socket_error.setSock("error accepting connection", __FILE__, __LINE__)); 258 } 259 } 260 261 /*************************************************************************** 262 263 Called by the select listener right after the client connection has been 264 assigned. 265 If this method throws an exception, error() and finalize() will be 266 called by the select listener. 267 268 ***************************************************************************/ 269 270 public abstract void handleConnection ( ); 271 272 /*************************************************************************** 273 274 Must be called by the subclass when finished handling the connection. 275 Will be automatically called by the select listener if assign() or 276 handleConnection() throws an exception. 277 278 The closure of the socket after handling a connection is quite 279 sensitive. If a connection has actually been assigned, the socket must 280 be shut down *unless* an I/O error has been reported for the socket 281 because then it will already have been shut down automatically. The 282 abstract io_error() method is used to determine whether the an I/O error 283 was reported for the socket or not. 284 285 ***************************************************************************/ 286 287 public void finalize ( ) 288 { 289 if ( this.connected ) 290 { 291 debug ( ConnectionHandler ) Stderr.formatln("[{}]: Closing connection", this.connection_id); 292 293 if (this.io_error) if (this.socket.shutdown()) 294 { 295 this.error(this.socket_error.setSock("error closing connection", __FILE__, __LINE__)); 296 } 297 298 // The socket is closed below. However, this does not always mean 299 // that it is unregistered from epoll -- in situations where the 300 // process has forked, the fork's reference to the underlying kernel 301 // file description will prevent it from being unregistered until 302 // the fork exits. Therefore, to be certain that the socket will not 303 // fire again in epoll, we need to explicitly unregister it. 304 this.unregisterSocket(); 305 306 this.socket.close(); 307 } 308 309 if ( this.finalize_dg_ ) try 310 { 311 this.finalize_dg_(this); 312 } 313 catch ( Exception e ) 314 { 315 this.error(e); 316 } 317 } 318 319 /*************************************************************************** 320 321 IAdvancedSelectClient.IErrorReporter interface method. Called when a 322 connection error occurs. 323 324 Params: 325 exception = exception which caused the error 326 event = epoll select event during which error occurred, if any 327 328 ***************************************************************************/ 329 330 public void error ( Exception exception, Event event = Event.init ) 331 { 332 debug ( ConnectionHandler ) try if ( this.io_error ) 333 { 334 Stderr.formatln("[{}]: Caught io exception while handling connection: '{}' @ {}:{}", 335 this.connection_id, exception.toString(), exception.file, exception.line); 336 } 337 else 338 { 339 debug ( ConnectionHandler ) Stderr.formatln("[{}]: Caught non-io exception while handling connection: '{}' @ {}:{}", 340 this.connection_id, exception.toString(), exception.file, exception.line); 341 } 342 catch { /* Theoretically io_error() could throw. */ } 343 344 if ( this.error_dg_ ) 345 { 346 this.error_dg_(exception, event, this); 347 } 348 } 349 350 /*************************************************************************** 351 352 Formats information about the connection into the provided buffer. This 353 method is called from the SelectListener in order to log information 354 about the state of all connections in the pool. 355 356 We format the following here: 357 * the file descriptor of the socket of this connection 358 * the remote ip and port of the socket 359 * whether an I/O error has occurred for the socket since the last 360 call to assign() 361 362 Params: 363 buf = buffer to format into 364 365 ***************************************************************************/ 366 367 public void formatInfo ( ref char[] buf ) 368 { 369 this.socket.formatInfo(buf, this.io_error); 370 } 371 372 /*************************************************************************** 373 374 Called by `finalize` to unregister the connection socket from epoll 375 before closing it. This is done because closing a socket does not always 376 mean that it is unregistered from epoll -- in situations where the 377 process has forked, the fork's reference to the underlying kernel file 378 description will prevent it from being unregistered until the fork 379 exits. Therefore, to be certain that the socket will not fire again in 380 epoll, we need to explicitly unregister it. 381 382 ***************************************************************************/ 383 384 abstract protected void unregisterSocket ( ); 385 386 /*************************************************************************** 387 388 Tells whether an I/O error has been reported for the socket since the 389 last assign() call. 390 391 Returns: 392 true if an I/O error has been reported for the socket or false 393 otherwise. 394 395 ***************************************************************************/ 396 397 protected abstract bool io_error ( ); 398 }