root/http/connection.cc

Revision 8fab37d78032b4009b127dc2d5c06b71a627ebd5, 14.9 KB (checked in by Antti-Juhani Kaijanaho <antti-juhani@…>, 21 months ago)

[http::connection] Kill idle connections.

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#include "bad_request.hh"
21#include "connection.hh"
22#include "request.hh"
23#include "resource.hh"
24#include "resource_exception.hh"
25#include "unsupported_version.hh"
26
27#include "../html/util.hh"
28#include "../logger/logline.hh"
29#include "../tls/session_impl.hh"
30#include "../util.hh"
31
32namespace http
33{
34        connection::connection(std::string port_name,
35                               bool https,
36                               boost::asio::ip::tcp::acceptor &acc,
37                               tls::init tls_init,
38                               server::conn_cb srv_cb)
39                : port_name(port_name)
40                , https(https)
41                , srv_cb(srv_cb)
42                , ios(acc.get_io_service())
43                , peer(ios)
44                , speer(peer, tls_init, tls::server)
45                , startup_timestamp
46                  (boost::posix_time::second_clock::universal_time())
47                , request_timestamp(startup_timestamp)
48                , num_requests(0)
49                , idle(false)
50        {
51                acc.async_accept(peer, ep,
52                                 boost::bind(&connection::accept,
53                                             this, _1));
54        }
55
56        connection::~connection() {}
57
58        void connection::abort(ptr self,
59                               boost::system::error_code ec)
60        {
61                logger::logline ll;
62                ll << self->loghead
63                   << "failure (" 
64                   << ec.message()
65                   << "), aborting connection";
66                ll.close();
67                logger::logger.flush();
68                kill(self);
69        }
70
71        void connection::kill(ptr self)
72        {
73                self->srv_cb.terminal(self);
74        }
75
76        void connection::accept(boost::system::error_code ec)
77        {
78                /* keep the object alive as long as there is action by
79                 passing around a shared pointer instead of using
80                 the implicit this pointer */
81                ptr self = shared_from_this();
82
83                if (ec) { abort(self, ec); return; }
84
85                self->startup_timestamp =
86                        boost::posix_time::second_clock::universal_time();
87
88                std::ostringstream ss;
89                ss << "httpd for " << self->ep << " ";
90                self->loghead = ss.str();
91
92                logger::logline ll;
93                ll << self->loghead << "starts";
94                ll.close();
95                self->srv_cb.active(self);
96                logger::logger.flush();
97
98                if (self->https)
99                {
100                        ll.open();
101                        ll << self->loghead << "starting TLS negotiation";
102                        ll.close();
103
104                        self->speer.async_handshake
105                                (bind(&connection::get_request, self, _1));
106                }
107                else
108                {
109                        get_request(self, ec);
110                }
111        }
112
113        void connection::get_request(ptr self,
114                                     boost::system::error_code ec)
115        {
116                using boost::asio::async_read_until;
117                using boost::bind;
118
119                if (ec) { abort(self, ec); return; }
120
121                self->idle = true;
122
123                async_read_until(self->speer, self->readbuf, "\r\n\r\n",
124                                 bind(&connection::got_request,
125                                      self, _1, _2));
126        }
127
128        void connection::got_request(ptr self,
129                                     boost::system::error_code ec,
130                                     std::size_t received_bytes)
131        {
132                using boost::asio::async_read;
133                using boost::asio::async_write;
134                using boost::asio::transfer_at_least;
135                using boost::bind;
136                using boost::shared_ptr;
137
138                self->idle = false;
139                ++self->num_requests;
140
141                if (ec == boost::asio::error::eof)
142                {
143                        logger::logline ll;
144                        ll << self->loghead
145                           << "client closed the connection";
146                        ll.close();
147                        logger::logger.flush();
148                        kill(self);
149                        return;
150                }
151                if (ec) { abort(self, ec); return; }
152
153                shared_ptr<request> req;
154                shared_ptr<response> resp;
155                response::factory respfac(self);
156                size_t clen = 0;
157
158                try
159                {
160                        std::string recv;
161                        recv.reserve(received_bytes);
162                        for (size_t i = 0; i < received_bytes; i++)
163                                recv += self->readbuf.sbumpc();
164                       
165                        if (self->srv_cb.do_log_protocol_details())
166                        {
167                                logger::logline ll;
168                                ll << self->loghead
169                                   << "got request (body excluded):\n"
170                                   << recv;
171                                ll.close();
172                        }
173                        self->srv_cb.active(self);
174
175                        req.reset(new request(recv, self->srv_cb,
176                                              self->ep.address()));
177                        respfac.set_v11(req->get_v11());
178                        respfac.set_close(req->get_close());
179                        respfac.set_no_body(req->get_method() == "HEAD");
180
181                        std::string clens = req->get_header("Content-Length");
182                        if (!req->get_header("Transfer-Encoding").empty())
183                        {
184                                logger::logline ll;
185                                ll << self->loghead
186                                   << "FIXME: request includes T-E body";
187                                ll.close();
188                                respfac.set_close(true);
189                        }
190                        else if (!clens.empty())
191                        {
192                                util::strip(clens);
193                                if (!util::is_number(clens))
194                                        throw bad_request("bad Content-Length");
195                                clen = boost::lexical_cast<size_t>(clens);
196                               
197                        }
198                }
199                catch (bad_request &e)
200                {
201                        respfac.set_close(true);
202                        resp = respfac("400 Bad request");
203                        std::ostream &os = resp->body("text/html; "
204                                                      "charset=utf-8");
205                        os << "<html>\n"
206                           << "<title>400 Bad request</title>\n"
207                           << "<h1>400 Bad request</h1>\n"
208                           << "<p>The browser sent a bad request: "
209                           << html::quote(e.what(),false)
210                           << "<p>\n"
211                           << "</html>";
212                }
213                catch (unsupported_version)
214                {
215                        respfac.set_close(true);
216                        resp = respfac("505 Unsupported HTTP version");
217                        std::ostream &os = resp->body("text/html; "
218                                                      "charset=utf-8");
219                        os << "<html>\n"
220                           << "<title>505 Unsupported HTTP version</title>\n"
221                           << "<h1>505 Unsupported HTTP version</h1>\n"
222                           << "<p>The browser sent a request using an "
223                           << "unsupported HTTP version.  This server "
224                           << "supports only HTTP/1.x.  NNTP access "
225                           << "is also provided on the customary port."
226                           << "<p>\n"
227                           << "</html>";
228                }               
229
230                reqcont(self, req, resp, respfac, clen, ec, 0);
231        }
232
233        void connection::reqcont(ptr self,
234                                 boost::shared_ptr<request> req,
235                                 boost::shared_ptr<response> resp,
236                                 response::factory respfac,
237                                 size_t clen,
238                                 boost::system::error_code ec,
239                                 std::size_t received_bytes)
240        {
241                using boost::asio::async_write;
242                using boost::bind;
243                using boost::shared_ptr;
244
245                if (ec) { abort(self, ec); return; }
246
247                if (clen > 0)
248                {
249                        std::string recv = util::streambuf_to_string
250                                (self->readbuf, received_bytes);
251                        req->body_append(recv);
252                        clen -= recv.length();
253                }
254                if (clen > 0)
255                {
256                        self->speer.async_read_some(self->readbuf.prepare(clen),
257                                                    bind(&connection::reqcont,
258                                                         self, req,
259                                                         resp, respfac,
260                                                         clen,
261                                                         _1, _2));
262                        return;
263                }
264
265                self->request_timestamp =
266                        boost::posix_time::microsec_clock::universal_time();
267
268                if (self->srv_cb.do_log_protocol_details())
269                {               
270                        logger::logline ll;
271                        ll << self->loghead << "request body:\n"
272                           << req->get_body();
273                        ll.close();
274                }
275
276                if (!resp)
277                {
278                        shared_ptr<resource> res;
279                        boost::shared_ptr<session> s;
280                        try {
281                                 res = self->srv_cb.get_http_resource
282                                         (req->get_host(), req->get_path());
283                        } catch (http::resource_exception &e) {
284                                res = e.r;
285                                if (e.s) s = e.s;
286                        }
287                        try
288                        {
289                        redo:
290                                try
291                                {
292                                        resp = (*res)(req, respfac);
293                                        if (s) resp->set_session(req, s);
294                                }
295                                catch (http::resource_exception &e)
296                                {
297                                        res = e.r;
298                                        s = e.s;
299                                        goto redo;
300                                }
301                        }
302                        catch (std::exception &e)
303                        {
304                                respfac.set_close(true);
305                                resp = respfac("500 Service unavailable");
306                                std::ostream &os = resp->body("text/html; "
307                                                              "charset=utf-8");
308                                os << "<html>\n"
309                                   << "<title>500 Service unavailable</title>\n"
310                                   << "<h1>500 Service unavailable</h1>\n"
311                                   << "<p>There was a problem fulfilling "
312                                   << "your request: "
313                                   << html::quote(e.what(), false)
314                                   << "<p>\n"
315                                   << "</html>";
316                                logger::logline ll;
317                                ll << self->loghead
318                                   << "exception caught:"
319                                   << e.what()
320                                   << " (" << typeid(e).name() << ")";
321                                ll.close();
322                        }
323                }
324               
325                logger::logline ll;
326                ll << self->loghead << (req ? req->logbit() : "") << " " 
327                   << resp->logbit()
328                   << "; "
329                   << boost::posix_time::microsec_clock::universal_time() -
330                        self->request_timestamp;
331                ll.close();
332
333                resp->complete();
334
335                if (self->srv_cb.do_log_protocol_details())
336                {
337                        ll.open();
338                        ll << "responding:\n" << resp->get_string();
339                        ll.close();
340                }
341
342                async_write(self->speer, resp->get_buffer(),
343                            bind(&connection::sent_response,
344                                 self, resp, _1, _2));
345        }
346
347        void connection::sent_response(ptr self,
348                                       boost::shared_ptr<response> resp,
349                                       boost::system::error_code ec,
350                                       size_t)
351        {
352                if (resp->get_close())
353                {
354                        kill(self);
355                }
356                else
357                {
358                        get_request(self, ec);
359                }
360        }
361
362        void connection::tick(bool stats)
363        {
364                using namespace boost::posix_time;
365                ptime now = microsec_clock::universal_time();
366                bool kill = idle && num_requests != 0 &&
367                        now - request_timestamp > minutes(10);
368
369                if (stats || kill)
370                {
371                        logger::logline ll;
372                        ll << loghead
373                           << " age "
374                           << now - startup_timestamp
375                           << "; "
376                           << (idle ? "idle " : "busy ")
377                           << now - request_timestamp
378                           << "; served " << num_requests << " requests";
379                        if (kill) ll << "; killing";
380                }
381                if (kill) speer.kill();
382        }
383
384}
Note: See TracBrowser for help on using the browser.