1 /******************************************************************************* 2 3 Base class for a connection handler for use with SelectListener, using 4 Fibers. 5 6 Copyright: 7 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 8 All rights reserved. 9 10 License: 11 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 12 Alternatively, this file may be distributed under the terms of the Tango 13 3-Clause BSD License (see LICENSE_BSD.txt for details). 14 15 *******************************************************************************/ 16 17 module ocean.net.server.connection.IFiberConnectionHandler; 18 19 20 import ocean.io.select.protocol.fiber.model.IFiberSelectProtocol, 21 ocean.io.select.protocol.fiber.FiberSelectReader, 22 ocean.io.select.protocol.fiber.FiberSelectWriter, 23 ocean.io.select.protocol.fiber.BufferedFiberSelectWriter, 24 ocean.io.select.protocol.generic.ErrnoIOException: IOWarning; 25 26 import ocean.net.server.connection.IConnectionHandler; 27 28 import ocean.sys.socket.model.ISocket; 29 import ocean.sys.socket.AddressIPSocket; 30 import ocean.sys.socket.model.IAddressIPSocketInfo; 31 32 import ocean.io.select.fiber.SelectFiber; 33 import ocean.util.container.pool.model.IResettable; 34 35 import ocean.sys.Epoll : epoll_event_t; 36 37 import ocean.text.convert.Formatter; 38 39 debug ( ConnectionHandler ) import ocean.io.Stdout : Stderr; 40 41 42 /******************************************************************************* 43 44 Fiber connection handler base class -- creates a socket and a fiber 45 internally, but does not contain reader / writer instances. 46 47 *******************************************************************************/ 48 49 abstract class IFiberConnectionHandlerBase : IConnectionHandler 50 { 51 /*************************************************************************** 52 53 Default fiber stack size (16K in 64-bit builds, 8K in 32-bit builds). 54 55 ***************************************************************************/ 56 57 public static size_t default_stack_size = size_t.sizeof * 2 * 1024; 58 59 /*************************************************************************** 60 61 Exception type alias. If handle() catches exceptions, it must rethrow 62 these. 63 64 ***************************************************************************/ 65 66 protected alias SelectFiber.KilledException KilledException; 67 68 /*************************************************************************** 69 70 Fiber to handle an single connection. 71 72 ***************************************************************************/ 73 74 protected SelectFiber fiber; 75 76 /*************************************************************************** 77 78 Constructor 79 80 Connects the socket, the asynchronous reader and writer, and the 81 provided epoll select dispatcher. 82 83 Params: 84 epoll = epoll select dispatcher 85 stack_size = fiber stack size 86 socket = the socket 87 finalize_dg = user-specified finalizer, called when the connection 88 is shut down 89 error_dg = user-specified error handler, called when a connection 90 error occurs 91 92 ***************************************************************************/ 93 94 protected this ( EpollSelectDispatcher epoll, 95 size_t stack_size, 96 ISocket socket, 97 scope FinalizeDg finalize_dg = null, 98 scope ErrorDg error_dg = null ) 99 { 100 super(socket, finalize_dg, error_dg); 101 102 this.fiber = new SelectFiber(epoll, &this.handleConnection_, stack_size); 103 } 104 105 /*************************************************************************** 106 107 Constructor, uses the default fiber stack size. 108 109 Connects the socket, the asynchronous reader and writer, and the 110 provided epoll select dispatcher. 111 112 Params: 113 epoll = epoll select dispatcher 114 socket = the socket 115 finalize_dg = user-specified finalizer, called when the connection 116 is shut down 117 error_dg = user-specified error handler, called when a connection 118 error occurs 119 120 ***************************************************************************/ 121 122 protected this ( EpollSelectDispatcher epoll, ISocket socket, 123 scope FinalizeDg finalize_dg = null, scope ErrorDg error_dg = null ) 124 { 125 this(epoll, this.default_stack_size, socket, finalize_dg, error_dg); 126 } 127 128 /*************************************************************************** 129 130 Called by the select listener right after the client connection has been 131 assigned. 132 133 Note: fiber.start() may throw an exception if an exception instance is 134 passed to the first suspend() call (e.g. the select reader encounters a 135 socket error). In this case the select listener will call error() and 136 finalize() which are usually called in handleConnection_() below. 137 138 ***************************************************************************/ 139 140 public override void handleConnection ( ) 141 { 142 this.fiber.start(); 143 } 144 145 146 /*************************************************************************** 147 148 Formats information about the connection into the provided buffer. This 149 method is called from the SelectListener in order to log information 150 about the state of all connections in the pool. 151 152 In addition to the information formatted by the super class, we also 153 format the following here: 154 * the events which the ISelectClient is registered with in epoll (in 155 debug builds these are printed in a human-readable format) 156 * (in debug builds) the id of the ISelectClient (a description 157 string) 158 159 Params: 160 buf = buffer to format into 161 162 ***************************************************************************/ 163 164 override public void formatInfo ( ref char[] buf ) 165 { 166 super.formatInfo(buf); 167 168 auto client = this.fiber.registered_client; 169 auto events = client ? client.events : 0; 170 171 debug 172 { 173 auto id = client ? client.id : "none"; 174 buf ~= ", events="; 175 176 foreach ( event, name; epoll_event_t.event_to_name ) 177 { 178 if ( events & event ) 179 { 180 buf ~= name; 181 } 182 } 183 184 sformat(buf, ", id={}", id); 185 } 186 else 187 { 188 sformat(buf, ", events={}", events); 189 } 190 } 191 192 /*************************************************************************** 193 194 Called by `finalize` to unregister the connection socket from epoll 195 before closing it. This is done because closing a socket does not always 196 mean that it is unregistered from epoll -- in situations where the 197 process has forked, the fork's reference to the underlying kernel file 198 description will prevent it from being unregistered until the fork 199 exits. Therefore, to be certain that the socket will not fire again in 200 epoll, we need to explicitly unregister it. 201 202 ***************************************************************************/ 203 204 override protected void unregisterSocket ( ) 205 { 206 if ( auto registered_client = this.fiber.registered_client ) 207 if ( this.socket.fd == registered_client.fileHandle() ) 208 this.fiber.unregister(); 209 } 210 211 /*************************************************************************** 212 213 Connection handler method. If it catches exceptions, it must rethrow 214 those of type KilledException. 215 216 ***************************************************************************/ 217 218 abstract protected void handle ( ); 219 220 /*************************************************************************** 221 222 Actual fiber method, started by handleConnection(). 223 224 ***************************************************************************/ 225 226 private void handleConnection_ ( ) 227 { 228 try 229 { 230 debug ( ConnectionHandler ) Stderr.formatln("[{}]: Handling connection", this.connection_id); 231 232 this.handle(); 233 } 234 catch ( Exception e ) 235 { 236 this.error(e); 237 } 238 finally 239 { 240 this.finalize(); 241 } 242 } 243 } 244 245 246 /******************************************************************************* 247 248 Standard fiber connection handler class using the basic FiberSelectReader 249 and FiberSelectWriter. 250 251 *******************************************************************************/ 252 253 abstract class IFiberConnectionHandler : IFiberConnectionHandlerBase, Resettable 254 { 255 /*************************************************************************** 256 257 If true, a buffered writer is used by default. 258 259 ***************************************************************************/ 260 261 public static bool use_buffered_writer_by_default = false; 262 263 /*************************************************************************** 264 265 Local aliases for SelectReader and SelectWriter. 266 267 ***************************************************************************/ 268 269 public alias .FiberSelectReader SelectReader; 270 public alias .FiberSelectWriter SelectWriter; 271 272 /*************************************************************************** 273 274 SelectReader and SelectWriter used for asynchronous protocol i/o. 275 276 ***************************************************************************/ 277 278 protected SelectReader reader; 279 protected SelectWriter writer; 280 281 /*************************************************************************** 282 283 IOWarning exception instance used by the reader and writer. 284 285 ***************************************************************************/ 286 287 protected IOWarning io_warning; 288 289 /*************************************************************************** 290 291 Constructor 292 293 Connects the socket, the asynchronous reader and writer, and the 294 provided epoll select dispatcher. 295 296 Params: 297 epoll = epoll select dispatcher which this connection 298 should use for i/o 299 stack_size = fiber stack size 300 buffered_writer = set to true to use the buffered writer 301 socket = the socket 302 finalize_dg = user-specified finalizer, called when the 303 connection is shut down 304 error_dg = user-specified error handler, called when a 305 connection error occurs 306 307 ***************************************************************************/ 308 309 protected this ( EpollSelectDispatcher epoll, 310 size_t stack_size, bool buffered_writer, ISocket socket, 311 scope FinalizeDg finalize_dg = null, scope ErrorDg error_dg = null ) 312 { 313 this(epoll, buffered_writer? 314 new BufferedFiberSelectWriter(this.socket, this.fiber, this.io_warning, this.socket_error) : 315 new FiberSelectWriter(this.socket, this.fiber, this.io_warning, this.socket_error), 316 socket, finalize_dg, error_dg, stack_size); 317 } 318 319 /*************************************************************************** 320 321 Constructor, uses the default fiber stack size. 322 323 Connects the socket, the asynchronous reader and writer, and the 324 provided epoll select dispatcher. 325 326 Params: 327 epoll = epoll select dispatcher which this connection 328 should use for i/o 329 buffered_writer = set to true to use the buffered writer 330 socket = the socket 331 finalize_dg = user-specified finalizer, called when the 332 connection is shut down 333 error_dg = user-specified error handler, called when a 334 connection error occurs 335 336 ***************************************************************************/ 337 338 protected this ( EpollSelectDispatcher epoll, bool buffered_writer, 339 ISocket socket, 340 scope FinalizeDg finalize_dg = null, scope ErrorDg error_dg = null ) 341 { 342 this(epoll, this.default_stack_size, buffered_writer, socket, 343 finalize_dg, error_dg); 344 } 345 346 /*************************************************************************** 347 348 Constructor, uses the default setting for buffered socket writing. 349 350 Connects the socket, the asynchronous reader and writer, and the 351 provided epoll select dispatcher. 352 353 Params: 354 epoll = epoll select dispatcher which this connection 355 should use for i/o 356 stack_size = fiber stack size 357 socket = the socket 358 finalize_dg = user-specified finalizer, called when the 359 connection is shut down 360 error_dg = user-specified error handler, called when a 361 connection error occurs 362 363 ***************************************************************************/ 364 365 protected this ( EpollSelectDispatcher epoll, size_t stack_size, 366 ISocket socket, 367 scope FinalizeDg finalize_dg = null, scope ErrorDg error_dg = null ) 368 { 369 this(epoll, stack_size, 370 this.use_buffered_writer_by_default, socket, finalize_dg, error_dg); 371 } 372 373 /*************************************************************************** 374 375 Constructor, uses the default fiber stack size and the default setting 376 for buffered socket writing. 377 378 Connects the socket, the asynchronous reader and writer, and the 379 provided epoll select dispatcher. 380 381 Params: 382 epoll = epoll select dispatcher which this connection 383 should use for i/o 384 socket = the socket 385 finalize_dg = user-specified finalizer, called when the 386 connection is shut down 387 error_dg = user-specified error handler, called when a 388 connection error occurs 389 390 ***************************************************************************/ 391 392 protected this ( EpollSelectDispatcher epoll, ISocket socket, 393 scope FinalizeDg finalize_dg = null, scope ErrorDg error_dg = null ) 394 { 395 this(epoll, this.use_buffered_writer_by_default, socket, 396 finalize_dg, error_dg); 397 } 398 399 /*************************************************************************** 400 401 Constructor 402 403 Connects the socket, the asynchronous reader and writer, and the 404 provided epoll select dispatcher. 405 406 Params: 407 epoll = epoll select dispatcher which this connection should 408 use for i/o 409 writer = SelectWriter instance to use 410 socket = the socket 411 finalize_dg = user-specified finalizer, called when the connection 412 is shut down 413 error_dg = user-specified error handler, called when a connection 414 error occurs 415 416 Note that writer must be lazy because it must be newed _after_ the super 417 constructor has been called. 418 419 ***************************************************************************/ 420 421 private this ( EpollSelectDispatcher epoll, lazy SelectWriter writer, 422 ISocket socket, scope FinalizeDg finalize_dg, scope ErrorDg error_dg, 423 size_t stack_size ) 424 { 425 super(epoll, stack_size, socket, finalize_dg, error_dg); 426 427 this.io_warning = new IOWarning(this.socket); 428 429 this.reader = new SelectReader(this.socket, this.fiber, this.io_warning, this.socket_error); 430 this.writer = writer; 431 432 this.reader.error_reporter = this; 433 this.writer.error_reporter = this; 434 } 435 436 /************************************************************************** 437 438 Called by IConnectionHandler.finalize(), in order to determine if an I/O 439 error was reported for the connection conduit which made the connection 440 automatically being closed. 441 (See comment for IConnectionHandler.finalize() method.) 442 443 Returns: 444 true if an I/O error was reported to the reader or the writer for 445 the connection conduit which made the connection automatically being 446 closed or false otherwise. 447 448 **************************************************************************/ 449 450 protected override bool io_error ( ) 451 { 452 return this.reader.io_error || this.writer.io_error; 453 } 454 455 /************************************************************************** 456 457 Resettable interface method, resets the reader. 458 459 **************************************************************************/ 460 461 public void reset ( ) 462 { 463 this.reader.reset(); 464 } 465 }