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 moduleocean.net.server.connection.IFiberConnectionHandler;
18 19 20 importocean.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 importocean.net.server.connection.IConnectionHandler;
27 28 importocean.sys.socket.model.ISocket;
29 importocean.sys.socket.AddressIPSocket;
30 importocean.sys.socket.model.IAddressIPSocketInfo;
31 32 importocean.io.select.fiber.SelectFiber;
33 importocean.util.container.pool.model.IResettable;
34 35 importocean.sys.Epoll : epoll_event_t;
36 37 importocean.text.convert.Formatter;
38 39 debug ( ConnectionHandler ) importocean.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 abstractclassIFiberConnectionHandlerBase : IConnectionHandler50 {
51 /***************************************************************************
52 53 Default fiber stack size (16K in 64-bit builds, 8K in 32-bit builds).
54 55 ***************************************************************************/56 57 publicstaticsize_tdefault_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 protectedaliasSelectFiber.KilledExceptionKilledException;
67 68 /***************************************************************************
69 70 Fiber to handle an single connection.
71 72 ***************************************************************************/73 74 protectedSelectFiberfiber;
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 protectedthis ( EpollSelectDispatcherepoll,
95 size_tstack_size,
96 ISocketsocket,
97 scopeFinalizeDgfinalize_dg = null,
98 scopeErrorDgerror_dg = null )
99 {
100 super(socket, finalize_dg, error_dg);
101 102 this.fiber = newSelectFiber(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 protectedthis ( EpollSelectDispatcherepoll, ISocketsocket,
123 scopeFinalizeDgfinalize_dg = null, scopeErrorDgerror_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 publicoverridevoidhandleConnection ( )
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 overridepublicvoidformatInfo ( refchar[] buf )
165 {
166 super.formatInfo(buf);
167 168 autoclient = this.fiber.registered_client;
169 autoevents = client ? client.events : 0;
170 171 debug172 {
173 autoid = 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 else187 {
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 overrideprotectedvoidunregisterSocket ( )
205 {
206 if ( autoregistered_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 abstractprotectedvoidhandle ( );
219 220 /***************************************************************************
221 222 Actual fiber method, started by handleConnection().
223 224 ***************************************************************************/225 226 privatevoidhandleConnection_ ( )
227 {
228 try229 {
230 debug ( ConnectionHandler ) Stderr.formatln("[{}]: Handling connection", this.connection_id);
231 232 this.handle();
233 }
234 catch ( Exceptione )
235 {
236 this.error(e);
237 }
238 finally239 {
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 abstractclassIFiberConnectionHandler : IFiberConnectionHandlerBase, Resettable254 {
255 /***************************************************************************
256 257 If true, a buffered writer is used by default.
258 259 ***************************************************************************/260 261 publicstaticbooluse_buffered_writer_by_default = false;
262 263 /***************************************************************************
264 265 Local aliases for SelectReader and SelectWriter.
266 267 ***************************************************************************/268 269 publicalias .FiberSelectReaderSelectReader;
270 publicalias .FiberSelectWriterSelectWriter;
271 272 /***************************************************************************
273 274 SelectReader and SelectWriter used for asynchronous protocol i/o.
275 276 ***************************************************************************/277 278 protectedSelectReaderreader;
279 protectedSelectWriterwriter;
280 281 /***************************************************************************
282 283 IOWarning exception instance used by the reader and writer.
284 285 ***************************************************************************/286 287 protectedIOWarningio_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 protectedthis ( EpollSelectDispatcherepoll,
310 size_tstack_size, boolbuffered_writer, ISocketsocket,
311 scopeFinalizeDgfinalize_dg = null, scopeErrorDgerror_dg = null )
312 {
313 this(epoll, buffered_writer?
314 newBufferedFiberSelectWriter(this.socket, this.fiber, this.io_warning, this.socket_error) :
315 newFiberSelectWriter(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 protectedthis ( EpollSelectDispatcherepoll, boolbuffered_writer,
339 ISocketsocket,
340 scopeFinalizeDgfinalize_dg = null, scopeErrorDgerror_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 protectedthis ( EpollSelectDispatcherepoll, size_tstack_size,
366 ISocketsocket,
367 scopeFinalizeDgfinalize_dg = null, scopeErrorDgerror_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 protectedthis ( EpollSelectDispatcherepoll, ISocketsocket,
393 scopeFinalizeDgfinalize_dg = null, scopeErrorDgerror_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 privatethis ( EpollSelectDispatcherepoll, lazySelectWriterwriter,
422 ISocketsocket, scopeFinalizeDgfinalize_dg, scopeErrorDgerror_dg,
423 size_tstack_size )
424 {
425 super(epoll, stack_size, socket, finalize_dg, error_dg);
426 427 this.io_warning = newIOWarning(this.socket);
428 429 this.reader = newSelectReader(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 protectedoverrideboolio_error ( )
451 {
452 returnthis.reader.io_error || this.writer.io_error;
453 }
454 455 /**************************************************************************
456 457 Resettable interface method, resets the reader.
458 459 **************************************************************************/460 461 publicvoidreset ( )
462 {
463 this.reader.reset();
464 }
465 }