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 }