root/nntp/connection.hh

Revision ae733bf09b727f294ead488db5ac953eb50f20d8, 15.0 KB (checked in by Antti-Juhani Kaijanaho <antti-juhani@…>, 2 years ago)

[nntp::connection::tick] Kill idle connections (timeout at 2 hours)

Signed-off-by: Antti-Juhani Kaijanaho <antti-juhani@…>

  • Property mode set to 100644
Line 
1/*  This file is part of Alue, the multiprotocol Internet discussion daemon
2
3    Copyright © 2009, 2010 Antti-Juhani Kaijanaho
4
5    Alue is free software: you can redistribute it and/or modify it
6    under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    Alue is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with Alue.  If not, see <http://www.gnu.org/licenses/>.
17
18 */
19
20#ifndef GUARD_NNTP_CONNECTION_HH
21#define GUARD_NNTP_CONNECTION_HH
22
23#include "../db/article_number_type.hh"
24#include "../tls/session.hh"
25#include "../server.hh"
26
27#include <boost/asio.hpp>
28#include <boost/bind/protect.hpp>
29#include <boost/enable_shared_from_this.hpp>
30#include <boost/shared_ptr.hpp>
31#include <map>
32
33namespace db { class group; }
34namespace db { class user; }
35
36namespace nntp
37{
38        /** A connection object encapsulates a single NNTP client
39            connection from the server's point of view.  It starts in
40            the "waiting for a connection" state, and once a
41            connection has been enabled, enters the
42            respond-receive-dispatch loop.  Eventually, the connection
43            enters the terminal state in anticipation of object
44            destruction.
45
46            The respond-receive-dispatch loop consists of three steps,
47            mentioned in its name: first, the server's response (or,
48            initially, the server's greeting) is transmitted.  Then
49            the server waits for the client to send something
50            (depending on the server state, either a command or a
51            dot-stuffed text object).  Finally, the response is parsed
52            and the appropriate command handler (a subclass of
53            connection::command) is dispatched.  The command handler
54            queues a response and then (in the common case) passes
55            control back to the loop.
56
57            The object hides all the details of asynchronous
58            communication from the command implementations, providing
59            an illusion of sequential execution in the common cases.
60            The main exception to that illusion is a command (such as
61            POST) that requires the client to provide a dot-stuffed
62            text object; the command logic needs to be split into the
63            initial command and a continuation object that handles the
64            eventual text object.
65         */
66        class connection : public server::connection,
67                           public boost::enable_shared_from_this<connection>
68        {
69        public:
70                class factory : public server::connection_factory
71                {
72                        bool ssl;
73                public:
74                        factory(bool ssl) : ssl(ssl) {}
75                        boost::shared_ptr<server::connection>
76                        operator()(std::string port_name,
77                                   boost::asio::ip::tcp::acceptor &acc,
78                                   tls::init init,
79                                   server::conn_cb srv_cb) {
80                                boost::shared_ptr<server::connection> rv
81                                        (new connection(port_name, ssl,
82                                                        acc,
83                                                        init,
84                                                        srv_cb));
85                                return rv;
86                        }
87                };
88
89                std::string get_port_name() const { return port_name; }
90                boost::shared_ptr<server::connection_factory>
91                get_factory() const {
92                        boost::shared_ptr<server::connection_factory> rv
93                                (new factory(initial_ssl));
94                        return rv;
95                }
96
97               
98
99                class command;
100                typedef std::map<std::string, command *>::const_iterator
101                command_iterator;
102
103                class continuation : public boost::enable_shared_from_this
104                                     <continuation>
105                {
106                public:
107                        typedef boost::shared_ptr<continuation> ptr;
108                       
109                        virtual ~continuation() {}
110                        virtual void sent(ptr lifeline,
111                                          boost::system::error_code ec) = 0;
112                };
113
114                class read_handler : public boost::enable_shared_from_this
115                                     <read_handler>
116                {
117                public:
118                        typedef boost::shared_ptr<read_handler> ptr;
119                        virtual ~read_handler() {}
120                        virtual continuation::ptr operator()(std::string) = 0;
121                };
122
123                // interface to privates of connection presented to
124                // command classes
125                class cb
126                {
127                        boost::shared_ptr<connection> cp;
128                        cb(boost::shared_ptr<connection> cp) : cp(cp) {}
129                        friend class connection;
130                public:
131                        /* connection state access */
132                        server::conn_cb srv_cb() { return cp->srv_cb; }
133                        db::db &dbase() { return cp->srv_cb.dbase(); }
134                        std::string loghead() { return cp->loghead; }
135                        boost::shared_ptr<db::group> &current_group() {
136                                return cp->current_group;
137                        }
138                        db::article_number_type &current_article() {
139                                return cp->current_article;
140                        }
141                        bool &capabilities_used() {
142                                return cp->capabilities_used;
143                        }
144                        command_iterator cmds_begin() {
145                                return cp->cmds->begin();
146                        }
147                        command_iterator cmds_end() { return cp->cmds->end(); }
148                        boost::asio::ip::address peer() {
149                                return cp->ep.address();
150                        }
151
152                        bool tls_active() { return cp->tls_active; }
153
154                        bool &identified() { return cp->identified; }
155                        bool &authenticated() { return cp->authenticated; }
156                        boost::shared_ptr<db::user> &identity() {
157                                return cp->identity;
158                        }
159
160                        /* sending messages (these just queue, actual
161                         * send happens after the command has returned
162                         * a continuation) */
163
164                        void send_line(std::string s) {
165                                cp->send_line(s);
166                        }
167
168                        template <typename In>
169                        void send_raw(In begin, In end) {
170                                cp->send_raw(begin, end);
171                        }
172
173                        /* continuations */
174                        continuation::ptr terminate() {
175                                return cp->terminate;
176                        }
177                        continuation::ptr dispatch() { return cp->dispatch; }
178                        continuation::ptr read_data(read_handler::ptr h) {
179                                return cp->read_data(h);
180                        }
181                        continuation::ptr starttls() { return cp->starttls; }
182                        continuation::ptr delay_fail(std::string line) {
183                                return cp->delay_fail(line);
184                        }
185                };
186
187                class command
188                {
189                public:
190                        virtual ~command();
191                        virtual continuation::ptr
192                        perform(cb, const std::string args[], size_t nargs)
193                                const = 0;
194                        virtual void write_capability_line(cb) const
195                                {}
196                };
197
198                connection(std::string port_name,
199                           bool initial_ssl,
200                           boost::asio::ip::tcp::acceptor &acc,
201                           tls::init,
202                           server::conn_cb srv_cb);
203
204                ~connection();
205                         
206                static void register_command(std::string name,
207                                             command *cmd);
208
209                struct overview_fmt_type {
210                        std::string s;
211                        bool full;
212                };
213                static const overview_fmt_type overview_fmt[];
214                static const size_t overview_fmt_count;
215        protected:
216                server::conn_cb get_conn_cb() { return srv_cb; }
217        private:
218                const std::string port_name;
219                const bool initial_ssl;
220
221                /* connection state */
222                boost::shared_ptr<db::group> current_group;
223                db::article_number_type current_article;
224
225                boost::shared_ptr<db::user> identity;
226                bool identified;
227                bool authenticated;
228
229                /* connection statistics */
230                boost::posix_time::ptime startup_timestamp;
231                boost::posix_time::ptime client_activity_timestamp;
232                size_t num_commands_served;
233                size_t num_500_responses;
234                bool capabilities_used;
235
236                /* connection administration */
237                server::conn_cb srv_cb;
238
239                boost::asio::io_service &ios;
240                boost::asio::ip::tcp::endpoint ep;
241
242                boost::asio::ip::tcp::socket peer;
243                tls::session<boost::asio::ip::tcp::socket&> speer;
244
245                bool tls_active;
246
247                std::vector<unsigned char> writebuf;
248                boost::asio::streambuf readbuf;
249
250                void abort(boost::system::error_code ec);
251                void accept(boost::system::error_code ec);
252                void greet(boost::system::error_code ec);
253                void flush_writebuf(continuation::ptr cont);
254
255                void stats();
256                void tick(bool);
257                void kill();
258
259                std::string loghead;
260
261                /* command handlers */
262                typedef std::map<std::string, command *> cmds_type;
263                static cmds_type *cmds; /* must be a pointer, since we
264                                         * cannot know for sure that
265                                         * the map would be created
266                                         * before the command objects,
267                                         * otherwise */
268
269                /* messaging */
270                void send_line(std::string s) {
271                        writebuf.reserve(writebuf.size()+s.length()+2);
272                        for (size_t i = 0; i < s.length(); i++)
273                                writebuf.push_back(s[i]);
274                        writebuf.push_back('\r');
275                        writebuf.push_back('\n');
276                }
277                template <typename In> void send_raw(In begin, In end) {
278                        for (In it = begin; it != end; it++)
279                                writebuf.push_back(*it);
280                }
281
282                class terminator : public continuation
283                {
284                        boost::shared_ptr<connection> cp;
285
286                        void done(ptr, boost::system::error_code ec);
287                public:
288                        terminator(boost::shared_ptr<connection> cp)
289                                : cp(cp) {}
290                        void sent(ptr, boost::system::error_code ec);
291                };
292                continuation::ptr terminate;
293
294                class dispatcher : public continuation
295                {
296                        boost::shared_ptr<connection> cp;
297
298                        void received(ptr,
299                                      boost::system::error_code e,
300                                      std::size_t size);
301                public:
302                        dispatcher(boost::shared_ptr<connection> cp)
303                                : cp(cp) {}
304                        void sent(ptr, boost::system::error_code ec);
305                };
306                continuation::ptr dispatch;
307
308                class reader : public continuation
309                {
310                        boost::shared_ptr<connection> cp;
311                        read_handler::ptr h;
312
313                        void received(ptr, size_t scanned,
314                                      boost::system::error_code e,
315                                      std::size_t size);
316                public:
317                        reader(boost::shared_ptr<connection> cp,
318                               read_handler::ptr h)
319                                : cp(cp), h(h) {}
320                        void sent(ptr, boost::system::error_code ec);
321                };
322               
323                continuation::ptr read_data(read_handler::ptr h) {
324                        continuation::ptr rv(new reader(shared_from_this(), h));
325                        return rv;
326                }
327
328                class tls_starter : public continuation
329                {
330                        boost::shared_ptr<connection> cp;
331                public:
332                        tls_starter(boost::shared_ptr<connection> cp)
333                                : cp(cp) {}
334                        void sent(ptr, boost::system::error_code ec);
335                };
336                continuation::ptr starttls;
337
338                class failure_delayer : public continuation
339                {
340                        boost::shared_ptr<connection> cp;
341                        boost::asio::deadline_timer timer;
342                        std::string line;
343
344                        void waited(ptr, boost::system::error_code ec);
345                public:
346                        failure_delayer(boost::shared_ptr<connection> cp,
347                                        std::string line)
348                                : cp(cp), timer(cp->ios), line(line)
349                                {}
350                        void sent(ptr, boost::system::error_code ec);
351                };
352                continuation::ptr delay_fail(std::string line) {
353                        continuation::ptr rv
354                                (new failure_delayer(shared_from_this(), line));
355                        return rv;
356                }
357        };
358
359        inline void connection::register_command(std::string name,
360                                                 command *cmd)
361        {
362                if (cmds == 0) cmds = new cmds_type();
363                (*cmds)[name] = cmd;
364        }
365
366
367
368};
369
370#endif /* GUARD_NNTP_CONNECTION_HH */
Note: See TracBrowser for help on using the browser.