/src/kea/src/lib/http/connection.cc
Line | Count | Source |
1 | | // Copyright (C) 2017-2026 Internet Systems Consortium, Inc. ("ISC") |
2 | | // |
3 | | // This Source Code Form is subject to the terms of the Mozilla Public |
4 | | // License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | // file, You can obtain one at http://mozilla.org/MPL/2.0/. |
6 | | |
7 | | #include <config.h> |
8 | | |
9 | | #include <asiolink/asio_wrapper.h> |
10 | | #include <dhcp/iface_mgr.h> |
11 | | #include <http/connection.h> |
12 | | #include <http/connection_pool.h> |
13 | | #include <http/http_log.h> |
14 | | #include <http/http_messages.h> |
15 | | #include <boost/make_shared.hpp> |
16 | | #include <functional> |
17 | | |
18 | | using namespace isc::asiolink; |
19 | | using namespace isc::dhcp; |
20 | | using namespace isc::util; |
21 | | namespace ph = std::placeholders; |
22 | | |
23 | | namespace { |
24 | | |
25 | | /// @brief Maximum size of the HTTP message that can be logged. |
26 | | /// |
27 | | /// The part of the HTTP message beyond this value is truncated. |
28 | | constexpr size_t MAX_LOGGED_MESSAGE_SIZE = 1024; |
29 | | |
30 | | } |
31 | | |
32 | | namespace isc { |
33 | | namespace http { |
34 | | |
35 | | HttpConnection::Transaction::Transaction(const HttpResponseCreatorPtr& response_creator, |
36 | | const HttpRequestPtr& request) |
37 | 446 | : request_(request ? request : response_creator->createNewHttpRequest()), |
38 | 446 | parser_(new HttpRequestParser(*request_)), |
39 | 446 | input_buf_(), |
40 | 446 | output_buf_() { |
41 | 446 | parser_->initModel(); |
42 | 446 | } |
43 | | |
44 | | HttpConnection::TransactionPtr |
45 | 446 | HttpConnection::Transaction::create(const HttpResponseCreatorPtr& response_creator) { |
46 | 446 | return (boost::make_shared<Transaction>(response_creator)); |
47 | 446 | } |
48 | | |
49 | | HttpConnection::TransactionPtr |
50 | | HttpConnection::Transaction::spawn(const HttpResponseCreatorPtr& response_creator, |
51 | 0 | const TransactionPtr& transaction) { |
52 | 0 | if (transaction) { |
53 | 0 | return (boost::make_shared<Transaction>(response_creator, |
54 | 0 | transaction->getRequest())); |
55 | 0 | } |
56 | 0 | return (create(response_creator)); |
57 | 0 | } |
58 | | |
59 | | void |
60 | | HttpConnection:: |
61 | 1.16k | SocketCallback::operator()(boost::system::error_code ec, size_t length) { |
62 | 1.16k | if (ec.value() == boost::asio::error::operation_aborted) { |
63 | 0 | return; |
64 | 0 | } |
65 | 1.16k | callback_(ec, length); |
66 | 1.16k | } |
67 | | |
68 | | HttpConnection::HttpConnection(const asiolink::IOServicePtr& io_service, |
69 | | const HttpAcceptorPtr& acceptor, |
70 | | const TlsContextPtr& tls_context, |
71 | | HttpConnectionPoolPtr connection_pool, |
72 | | const HttpResponseCreatorPtr& response_creator, |
73 | | const HttpAcceptorCallback& callback, |
74 | | const long request_timeout, |
75 | | const long idle_timeout) |
76 | 446 | : io_service_(io_service), request_timer_(io_service_), |
77 | 446 | request_timeout_(request_timeout), tls_context_(tls_context), |
78 | 446 | idle_timeout_(idle_timeout), tcp_socket_(), tls_socket_(), |
79 | 446 | acceptor_(acceptor), connection_pool_(connection_pool), |
80 | 446 | response_creator_(response_creator), acceptor_callback_(callback), |
81 | 446 | use_external_(false), watch_socket_(), defer_shutdown_(false), |
82 | 446 | closed_(false) { |
83 | 446 | if (!tls_context) { |
84 | 446 | tcp_socket_.reset(new asiolink::TCPSocket<SocketCallback>(io_service)); |
85 | 446 | } else { |
86 | 0 | tls_socket_.reset(new asiolink::TLSSocket<SocketCallback>(io_service, |
87 | 0 | tls_context)); |
88 | 0 | } |
89 | 446 | } |
90 | | |
91 | 446 | HttpConnection::~HttpConnection() { |
92 | 446 | close(); |
93 | 446 | } |
94 | | |
95 | | void |
96 | 446 | HttpConnection::addExternalSockets(bool use_external) { |
97 | 446 | use_external_ = use_external; |
98 | 446 | } |
99 | | |
100 | | void |
101 | 446 | HttpConnection::recordParameters(const HttpRequestPtr& request) const { |
102 | 446 | if (!request) { |
103 | | // Should never happen. |
104 | 0 | return; |
105 | 0 | } |
106 | | |
107 | | // Record the remote address. |
108 | 446 | request->setRemote(getRemoteEndpointAddressAsText()); |
109 | | |
110 | | // Record TLS parameters. |
111 | 446 | if (!tls_socket_) { |
112 | 446 | return; |
113 | 446 | } |
114 | | |
115 | | // The connection uses HTTPS aka HTTP over TLS. |
116 | 0 | request->setTls(true); |
117 | | |
118 | | // Record the first commonName of the subjectName of the client |
119 | | // certificate when wanted. |
120 | 0 | if (HttpRequest::recordSubject_) { |
121 | 0 | request->setSubject(tls_socket_->getTlsStream().getSubject()); |
122 | 0 | } |
123 | | |
124 | | // Record the first commonName of the issuerName of the client |
125 | | // certificate when wanted. |
126 | 0 | if (HttpRequest::recordIssuer_) { |
127 | 0 | request->setIssuer(tls_socket_->getTlsStream().getIssuer()); |
128 | 0 | } |
129 | 0 | } |
130 | | |
131 | | void |
132 | 0 | HttpConnection::shutdownCallback(const boost::system::error_code&) { |
133 | 0 | if (closed_) { |
134 | 0 | return; |
135 | 0 | } |
136 | 0 | if (use_external_) { |
137 | 0 | IfaceMgr::instance().deleteExternalSocket(tls_socket_->getNative()); |
138 | 0 | closeWatchSocket(); |
139 | 0 | use_external_ = false; |
140 | 0 | } |
141 | |
|
142 | 0 | tls_socket_->close(); |
143 | 0 | closed_ = true; |
144 | 0 | } |
145 | | |
146 | | void |
147 | 0 | HttpConnection::shutdown() { |
148 | 0 | if (closed_) { |
149 | 0 | return; |
150 | 0 | } |
151 | 0 | request_timer_.cancel(); |
152 | 0 | if (tcp_socket_) { |
153 | 0 | if (use_external_) { |
154 | 0 | IfaceMgr::instance().deleteExternalSocket(tcp_socket_->getNative()); |
155 | 0 | closeWatchSocket(); |
156 | 0 | use_external_ = false; |
157 | 0 | } |
158 | 0 | tcp_socket_->close(); |
159 | 0 | closed_ = true; |
160 | 0 | return; |
161 | 0 | } |
162 | 0 | if (tls_socket_) { |
163 | | // Create instance of the callback to close the socket. |
164 | 0 | SocketCallback cb(std::bind(&HttpConnection::shutdownCallback, |
165 | 0 | shared_from_this(), |
166 | 0 | ph::_1)); // error_code |
167 | 0 | tls_socket_->shutdown(cb); |
168 | 0 | return; |
169 | 0 | } |
170 | | // Not reachable? |
171 | 0 | isc_throw(Unexpected, "internal error: unable to shutdown the socket"); |
172 | 0 | } |
173 | | |
174 | | void |
175 | 223 | HttpConnection::markWatchSocketReady() { |
176 | 223 | if (!watch_socket_) { |
177 | | /// Should not happen... |
178 | 0 | return; |
179 | 0 | } |
180 | 223 | try { |
181 | 223 | watch_socket_->markReady(); |
182 | 223 | } catch (const std::exception& ex) { |
183 | 0 | LOG_ERROR(http_logger, HTTP_CONNECTION_WATCH_SOCKET_MARK_READY_ERROR) |
184 | 0 | .arg(ex.what()); |
185 | 0 | } |
186 | 223 | } |
187 | | |
188 | | void |
189 | 223 | HttpConnection::clearWatchSocket() { |
190 | 223 | if (!watch_socket_) { |
191 | | /// Should not happen... |
192 | 0 | return; |
193 | 0 | } |
194 | 223 | try { |
195 | 223 | watch_socket_->clearReady(); |
196 | 223 | } catch (const std::exception& ex) { |
197 | 0 | LOG_ERROR(http_logger, HTTP_CONNECTION_WATCH_SOCKET_CLEAR_ERROR) |
198 | 0 | .arg(ex.what()); |
199 | 0 | } |
200 | 223 | } |
201 | | |
202 | | void |
203 | 446 | HttpConnection::closeWatchSocket() { |
204 | 446 | if (!watch_socket_) { |
205 | | /// Should not happen... |
206 | 223 | return; |
207 | 223 | } |
208 | 223 | IfaceMgr::instance().deleteExternalSocket(watch_socket_->getSelectFd()); |
209 | | // Close watch socket and log errors if occur. |
210 | 223 | std::string watch_error; |
211 | 223 | if (!watch_socket_->closeSocket(watch_error)) { |
212 | 0 | LOG_ERROR(http_logger, HTTP_CONNECTION_WATCH_SOCKET_CLOSE_ERROR) |
213 | 0 | .arg(watch_error); |
214 | 0 | } |
215 | 223 | } |
216 | | |
217 | | void |
218 | 1.11k | HttpConnection::close() { |
219 | 1.11k | if (defer_shutdown_) { |
220 | 0 | io_service_->post(std::bind([](HttpConnectionPtr c) { c->close(); }, shared_from_this())); |
221 | 0 | return; |
222 | 0 | } |
223 | 1.11k | if (closed_) { |
224 | 669 | return; |
225 | 669 | } |
226 | 446 | request_timer_.cancel(); |
227 | 446 | if (tcp_socket_) { |
228 | 446 | if (use_external_) { |
229 | 446 | IfaceMgr::instance().deleteExternalSocket(tcp_socket_->getNative()); |
230 | 446 | closeWatchSocket(); |
231 | 446 | use_external_ = false; |
232 | 446 | } |
233 | 446 | tcp_socket_->close(); |
234 | 446 | closed_ = true; |
235 | 446 | return; |
236 | 446 | } |
237 | 0 | if (tls_socket_) { |
238 | 0 | if (use_external_) { |
239 | 0 | IfaceMgr::instance().deleteExternalSocket(tls_socket_->getNative()); |
240 | 0 | closeWatchSocket(); |
241 | 0 | use_external_ = false; |
242 | 0 | } |
243 | 0 | tls_socket_->close(); |
244 | 0 | closed_ = true; |
245 | 0 | return; |
246 | 0 | } |
247 | | // Not reachable? |
248 | 0 | isc_throw(Unexpected, "internal error: unable to close the socket"); |
249 | 0 | } |
250 | | |
251 | | void |
252 | 0 | HttpConnection::shutdownConnection() { |
253 | 0 | auto connection_pool = connection_pool_.lock(); |
254 | 0 | try { |
255 | 0 | LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_BASIC, |
256 | 0 | HTTP_CONNECTION_SHUTDOWN) |
257 | 0 | .arg(getRemoteEndpointAddressAsText()); |
258 | 0 | if (connection_pool) { |
259 | 0 | connection_pool->shutdown(shared_from_this()); |
260 | 0 | } else { |
261 | 0 | shutdown(); |
262 | 0 | } |
263 | 0 | } catch (...) { |
264 | 0 | LOG_ERROR(http_logger, HTTP_CONNECTION_SHUTDOWN_FAILED); |
265 | 0 | } |
266 | 0 | } |
267 | | |
268 | | void |
269 | 446 | HttpConnection::stopThisConnection() { |
270 | 446 | auto connection_pool = connection_pool_.lock(); |
271 | 446 | if (closed_ && !connection_pool) { |
272 | 0 | return; |
273 | 0 | } |
274 | 446 | try { |
275 | 446 | LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_BASIC, |
276 | 0 | HTTP_CONNECTION_STOP) |
277 | 0 | .arg(getRemoteEndpointAddressAsText()); |
278 | 446 | if (connection_pool) { |
279 | 446 | connection_pool->stop(shared_from_this()); |
280 | 446 | } else { |
281 | 0 | close(); |
282 | 0 | } |
283 | 446 | } catch (...) { |
284 | 0 | LOG_ERROR(http_logger, HTTP_CONNECTION_STOP_FAILED); |
285 | 0 | } |
286 | 446 | } |
287 | | |
288 | | void |
289 | 446 | HttpConnection::asyncAccept() { |
290 | | // Create instance of the callback. It is safe to pass the local instance |
291 | | // of the callback, because the underlying boost functions make copies |
292 | | // as needed. |
293 | 446 | HttpAcceptorCallback cb = std::bind(&HttpConnection::acceptorCallback, |
294 | 446 | shared_from_this(), |
295 | 446 | ph::_1); // error |
296 | 446 | try { |
297 | 446 | HttpsAcceptorPtr tls_acceptor = |
298 | 446 | boost::dynamic_pointer_cast<HttpsAcceptor>(acceptor_); |
299 | 446 | if (!tls_acceptor) { |
300 | 446 | if (!tcp_socket_) { |
301 | 0 | isc_throw(Unexpected, "internal error: TCP socket is null"); |
302 | 0 | } |
303 | 446 | acceptor_->asyncAccept(*tcp_socket_, cb); |
304 | 446 | } else { |
305 | 0 | if (!tls_socket_) { |
306 | 0 | isc_throw(Unexpected, "internal error: TLS socket is null"); |
307 | 0 | } |
308 | 0 | tls_acceptor->asyncAccept(*tls_socket_, cb); |
309 | 0 | } |
310 | 446 | } catch (const std::exception& ex) { |
311 | 0 | isc_throw(HttpConnectionError, "unable to start accepting TCP " |
312 | 0 | "connections: " << ex.what()); |
313 | 0 | } |
314 | 446 | } |
315 | | |
316 | | void |
317 | 223 | HttpConnection::doHandshake() { |
318 | | // Skip the handshake if the socket is not a TLS one. |
319 | 223 | if (!tls_socket_) { |
320 | 223 | doRead(); |
321 | 223 | return; |
322 | 223 | } |
323 | | |
324 | | // Create instance of the callback. It is safe to pass the local instance |
325 | | // of the callback, because the underlying boost functions make copies |
326 | | // as needed. |
327 | 0 | SocketCallback cb(std::bind(&HttpConnection::handshakeCallback, |
328 | 0 | shared_from_this(), |
329 | 0 | ph::_1)); // error |
330 | 0 | try { |
331 | 0 | tls_socket_->handshake(cb); |
332 | 0 | if (use_external_) { |
333 | 0 | markWatchSocketReady(); |
334 | 0 | } |
335 | 0 | } catch (const std::exception& ex) { |
336 | 0 | isc_throw(HttpConnectionError, "unable to perform TLS handshake: " |
337 | 0 | << ex.what()); |
338 | 0 | } |
339 | 0 | } |
340 | | |
341 | | void |
342 | 1.16k | HttpConnection::doRead(TransactionPtr transaction) { |
343 | 1.16k | try { |
344 | 1.16k | TCPEndpoint endpoint; |
345 | | |
346 | | // Transaction hasn't been created if we are starting to read the |
347 | | // new request. |
348 | 1.16k | if (!transaction) { |
349 | 446 | transaction = Transaction::create(response_creator_); |
350 | 446 | recordParameters(transaction->getRequest()); |
351 | 446 | } |
352 | | |
353 | | // Create instance of the callback. It is safe to pass the local instance |
354 | | // of the callback, because the underlying std functions make copies |
355 | | // as needed. |
356 | 1.16k | SocketCallback cb(std::bind(&HttpConnection::socketReadCallback, |
357 | 1.16k | shared_from_this(), |
358 | 1.16k | transaction, |
359 | 1.16k | ph::_1, // error |
360 | 1.16k | ph::_2)); //bytes_transferred |
361 | 1.16k | if (tcp_socket_) { |
362 | 1.16k | tcp_socket_->asyncReceive(static_cast<void*>(transaction->getInputBufData()), |
363 | 1.16k | transaction->getInputBufSize(), |
364 | 1.16k | 0, &endpoint, cb); |
365 | 1.16k | return; |
366 | 1.16k | } |
367 | 0 | if (tls_socket_) { |
368 | 0 | tls_socket_->asyncReceive(static_cast<void*>(transaction->getInputBufData()), |
369 | 0 | transaction->getInputBufSize(), |
370 | 0 | 0, &endpoint, cb); |
371 | 0 | return; |
372 | 0 | } |
373 | 223 | } catch (...) { |
374 | 223 | stopThisConnection(); |
375 | 223 | } |
376 | 1.16k | } |
377 | | |
378 | | void |
379 | 446 | HttpConnection::doWrite(HttpConnection::TransactionPtr transaction) { |
380 | 446 | try { |
381 | 446 | if (transaction->outputDataAvail()) { |
382 | | // Create instance of the callback. It is safe to pass the local instance |
383 | | // of the callback, because the underlying std functions make copies |
384 | | // as needed. |
385 | 223 | SocketCallback cb(std::bind(&HttpConnection::socketWriteCallback, |
386 | 223 | shared_from_this(), |
387 | 223 | transaction, |
388 | 223 | ph::_1, // error |
389 | 223 | ph::_2)); // bytes_transferred |
390 | 223 | if (tcp_socket_) { |
391 | 223 | tcp_socket_->asyncSend(transaction->getOutputBufData(), |
392 | 223 | transaction->getOutputBufSize(), |
393 | 223 | cb); |
394 | 223 | if (use_external_) { |
395 | 223 | markWatchSocketReady(); |
396 | 223 | } |
397 | 223 | return; |
398 | 223 | } |
399 | 0 | if (tls_socket_) { |
400 | 0 | tls_socket_->asyncSend(transaction->getOutputBufData(), |
401 | 0 | transaction->getOutputBufSize(), |
402 | 0 | cb); |
403 | 0 | if (use_external_) { |
404 | 0 | markWatchSocketReady(); |
405 | 0 | } |
406 | 0 | return; |
407 | 0 | } |
408 | 223 | } else { |
409 | | // The isPersistent() function may throw if the request hasn't |
410 | | // been created, i.e. the HTTP headers weren't parsed. We catch |
411 | | // this exception below and close the connection since we're |
412 | | // unable to tell if the connection should remain persistent |
413 | | // or not. The default is to close it. |
414 | 223 | if (!transaction->getRequest()->isPersistent()) { |
415 | 0 | stopThisConnection(); |
416 | |
|
417 | 223 | } else { |
418 | | // The connection is persistent and we are done sending |
419 | | // the previous response. Start listening for the next |
420 | | // requests. |
421 | 223 | setupIdleTimer(); |
422 | 223 | doRead(); |
423 | 223 | } |
424 | 223 | } |
425 | 446 | } catch (...) { |
426 | 0 | stopThisConnection(); |
427 | 0 | } |
428 | 446 | } |
429 | | |
430 | | void |
431 | | HttpConnection::asyncSendResponse(const ConstHttpResponsePtr& response, |
432 | 223 | TransactionPtr transaction) { |
433 | 223 | transaction->setOutputBuf(response->toString()); |
434 | 223 | doWrite(transaction); |
435 | 223 | } |
436 | | |
437 | | void |
438 | 446 | HttpConnection::acceptorCallback(const boost::system::error_code& ec) { |
439 | 446 | if (!acceptor_->isOpen()) { |
440 | 223 | return; |
441 | 223 | } |
442 | | |
443 | 223 | if (ec) { |
444 | 0 | stopThisConnection(); |
445 | 0 | } |
446 | | |
447 | 223 | acceptor_callback_(ec); |
448 | | |
449 | 223 | if (!ec) { |
450 | 223 | if (!tls_context_) { |
451 | 223 | LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_DETAIL, |
452 | 0 | HTTP_REQUEST_RECEIVE_START) |
453 | 0 | .arg(getRemoteEndpointAddressAsText()) |
454 | 0 | .arg(static_cast<unsigned>(request_timeout_/1000)); |
455 | 223 | } else { |
456 | 0 | LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_DETAIL, |
457 | 0 | HTTP_CONNECTION_HANDSHAKE_START) |
458 | 0 | .arg(getRemoteEndpointAddressAsText()) |
459 | 0 | .arg(static_cast<unsigned>(request_timeout_/1000)); |
460 | 0 | } |
461 | | |
462 | 223 | if (use_external_) { |
463 | 223 | auto& iface_mgr = IfaceMgr::instance(); |
464 | 223 | if (tcp_socket_) { |
465 | 223 | iface_mgr.addExternalSocket(tcp_socket_->getNative(), 0); |
466 | 223 | } |
467 | 223 | if (tls_socket_) { |
468 | 0 | iface_mgr.addExternalSocket(tls_socket_->getNative(), 0); |
469 | 0 | } |
470 | 223 | watch_socket_.reset(new WatchSocket()); |
471 | 223 | iface_mgr.addExternalSocket(watch_socket_->getSelectFd(), 0); |
472 | 223 | } |
473 | | |
474 | 223 | setupRequestTimer(); |
475 | 223 | doHandshake(); |
476 | 223 | } |
477 | 223 | } |
478 | | |
479 | | void |
480 | 0 | HttpConnection::handshakeCallback(const boost::system::error_code& ec) { |
481 | 0 | if (use_external_) { |
482 | 0 | clearWatchSocket(); |
483 | 0 | } |
484 | 0 | if (ec) { |
485 | 0 | LOG_INFO(http_logger, HTTP_CONNECTION_HANDSHAKE_FAILED) |
486 | 0 | .arg(getRemoteEndpointAddressAsText()) |
487 | 0 | .arg(ec.message()); |
488 | 0 | stopThisConnection(); |
489 | 0 | } else { |
490 | 0 | LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_DETAIL, |
491 | 0 | HTTPS_REQUEST_RECEIVE_START) |
492 | 0 | .arg(getRemoteEndpointAddressAsText()); |
493 | |
|
494 | 0 | doRead(); |
495 | 0 | } |
496 | 0 | } |
497 | | |
498 | | void |
499 | | HttpConnection::socketReadCallback(HttpConnection::TransactionPtr transaction, |
500 | 938 | boost::system::error_code ec, size_t length) { |
501 | 938 | if (ec) { |
502 | | // IO service has been stopped and the connection is probably |
503 | | // going to be shutting down. |
504 | 223 | if (ec.value() == boost::asio::error::operation_aborted) { |
505 | 0 | return; |
506 | | |
507 | | // EWOULDBLOCK and EAGAIN are special cases. Everything else is |
508 | | // treated as fatal error. |
509 | 223 | } else if ((ec.value() != boost::asio::error::try_again) && |
510 | 223 | (ec.value() != boost::asio::error::would_block)) { |
511 | 223 | stopThisConnection(); |
512 | | |
513 | | // We got EWOULDBLOCK or EAGAIN which indicate that we may be able to |
514 | | // read something from the socket on the next attempt. Just make sure |
515 | | // we don't try to read anything now in case there is any garbage |
516 | | // passed in length. |
517 | 223 | } else { |
518 | 0 | length = 0; |
519 | 0 | } |
520 | 223 | } |
521 | | |
522 | | // Receiving is in progress, so push back the timeout. |
523 | 938 | setupRequestTimer(transaction); |
524 | | |
525 | 938 | if (length != 0) { |
526 | 715 | LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_DETAIL_DATA, |
527 | 0 | HTTP_DATA_RECEIVED) |
528 | 0 | .arg(length) |
529 | 0 | .arg(getRemoteEndpointAddressAsText()); |
530 | | |
531 | 715 | transaction->getParser()->postBuffer(static_cast<void*>(transaction->getInputBufData()), |
532 | 715 | length); |
533 | 715 | transaction->getParser()->poll(); |
534 | 715 | } |
535 | | |
536 | 938 | if (transaction->getParser()->needData()) { |
537 | | // The parser indicates that the some part of the message being |
538 | | // received is still missing, so continue to read. |
539 | 715 | doRead(transaction); |
540 | | |
541 | 715 | } else { |
542 | 223 | try { |
543 | | // The whole message has been received, so let's finalize it. |
544 | 223 | transaction->getRequest()->finalize(); |
545 | | |
546 | 223 | LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_BASIC, |
547 | 208 | HTTP_CLIENT_REQUEST_RECEIVED) |
548 | 208 | .arg(getRemoteEndpointAddressAsText()); |
549 | | |
550 | 223 | LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_BASIC_DATA, |
551 | 208 | HTTP_CLIENT_REQUEST_RECEIVED_DETAILS) |
552 | 208 | .arg(getRemoteEndpointAddressAsText()) |
553 | 208 | .arg(transaction->getParser()->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE)); |
554 | | |
555 | 223 | } catch (const std::exception& ex) { |
556 | 208 | LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_BASIC, |
557 | 0 | HTTP_BAD_CLIENT_REQUEST_RECEIVED) |
558 | 0 | .arg(getRemoteEndpointAddressAsText()) |
559 | 0 | .arg(ex.what()); |
560 | | |
561 | 208 | LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_BASIC_DATA, |
562 | 0 | HTTP_BAD_CLIENT_REQUEST_RECEIVED_DETAILS) |
563 | 0 | .arg(getRemoteEndpointAddressAsText()) |
564 | 0 | .arg(transaction->getParser()->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE)); |
565 | 208 | } |
566 | | |
567 | | // Don't want to timeout if creation of the response takes long. |
568 | 223 | request_timer_.cancel(); |
569 | | |
570 | 223 | defer_shutdown_ = true; |
571 | | |
572 | 223 | std::unique_ptr<HttpConnection, void (*)(HttpConnection*)> p(this, [](HttpConnection* c) { |
573 | 223 | c->defer_shutdown_ = false; |
574 | 223 | }); |
575 | | |
576 | | // Create the response from the received request using the custom |
577 | | // response creator. |
578 | 223 | HttpResponsePtr response = response_creator_->createHttpResponse(transaction->getRequest()); |
579 | 223 | LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_BASIC, |
580 | 0 | HTTP_SERVER_RESPONSE_SEND) |
581 | 0 | .arg(response->toBriefString()) |
582 | 0 | .arg(getRemoteEndpointAddressAsText()); |
583 | | |
584 | 223 | LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_BASIC_DATA, |
585 | 0 | HTTP_SERVER_RESPONSE_SEND_DETAILS) |
586 | 0 | .arg(getRemoteEndpointAddressAsText()) |
587 | 0 | .arg(HttpMessageParserBase::logFormatHttpMessage(response->toString(), |
588 | 0 | MAX_LOGGED_MESSAGE_SIZE)); |
589 | | |
590 | | // Response created. Activate the timer again. |
591 | 223 | setupRequestTimer(transaction); |
592 | | |
593 | | // Start sending the response. |
594 | 223 | asyncSendResponse(response, transaction); |
595 | 223 | } |
596 | 938 | } |
597 | | |
598 | | void |
599 | | HttpConnection::socketWriteCallback(HttpConnection::TransactionPtr transaction, |
600 | 223 | boost::system::error_code ec, size_t length) { |
601 | 223 | if (use_external_) { |
602 | 223 | clearWatchSocket(); |
603 | 223 | } |
604 | 223 | if (ec) { |
605 | | // IO service has been stopped and the connection is probably |
606 | | // going to be shutting down. |
607 | 0 | if (ec.value() == boost::asio::error::operation_aborted) { |
608 | 0 | return; |
609 | | |
610 | | // EWOULDBLOCK and EAGAIN are special cases. Everything else is |
611 | | // treated as fatal error. |
612 | 0 | } else if ((ec.value() != boost::asio::error::try_again) && |
613 | 0 | (ec.value() != boost::asio::error::would_block)) { |
614 | 0 | stopThisConnection(); |
615 | | |
616 | | // We got EWOULDBLOCK or EAGAIN which indicate that we may be able to |
617 | | // read something from the socket on the next attempt. |
618 | 0 | } else { |
619 | | // Sending is in progress, so push back the timeout. |
620 | 0 | setupRequestTimer(transaction); |
621 | |
|
622 | 0 | doWrite(transaction); |
623 | 0 | } |
624 | 0 | } |
625 | | |
626 | | // Since each transaction has its own output buffer, it is not really |
627 | | // possible that the number of bytes written is larger than the size |
628 | | // of the buffer. But, let's be safe and set the length to the size |
629 | | // of the buffer if that unexpected condition occurs. |
630 | 223 | if (length > transaction->getOutputBufSize()) { |
631 | 0 | length = transaction->getOutputBufSize(); |
632 | 0 | } |
633 | | |
634 | 223 | if (length <= transaction->getOutputBufSize()) { |
635 | | // Sending is in progress, so push back the timeout. |
636 | 223 | setupRequestTimer(transaction); |
637 | 223 | } |
638 | | |
639 | | // Eat the 'length' number of bytes from the output buffer and only |
640 | | // leave the part of the response that hasn't been sent. |
641 | 223 | transaction->consumeOutputBuf(length); |
642 | | |
643 | | // Schedule the write of the unsent data. |
644 | 223 | doWrite(transaction); |
645 | 223 | } |
646 | | |
647 | | void |
648 | 1.60k | HttpConnection::setupRequestTimer(TransactionPtr transaction) { |
649 | | // Pass raw pointer rather than shared_ptr to this object, |
650 | | // because IntervalTimer already passes shared pointer to the |
651 | | // IntervalTimerImpl to make sure that the callback remains |
652 | | // valid. |
653 | 1.60k | request_timer_.setup(std::bind(&HttpConnection::requestTimeoutCallback, |
654 | 1.60k | this, transaction), |
655 | 1.60k | request_timeout_, IntervalTimer::ONE_SHOT); |
656 | 1.60k | } |
657 | | |
658 | | void |
659 | 223 | HttpConnection::setupIdleTimer() { |
660 | 223 | request_timer_.setup(std::bind(&HttpConnection::idleTimeoutCallback, |
661 | 223 | this), |
662 | 223 | idle_timeout_, IntervalTimer::ONE_SHOT); |
663 | 223 | } |
664 | | |
665 | | void |
666 | 0 | HttpConnection::requestTimeoutCallback(TransactionPtr transaction) { |
667 | 0 | LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_DETAIL, |
668 | 0 | HTTP_CLIENT_REQUEST_TIMEOUT_OCCURRED) |
669 | 0 | .arg(getRemoteEndpointAddressAsText()); |
670 | | |
671 | | // We need to differentiate the transactions between a normal response and the |
672 | | // timeout. We create new transaction from the current transaction. It is |
673 | | // to preserve the request we're responding to. |
674 | 0 | auto spawned_transaction = Transaction::spawn(response_creator_, transaction); |
675 | | |
676 | | // The new transaction inherits the request from the original transaction |
677 | | // if such transaction exists. |
678 | 0 | auto request = spawned_transaction->getRequest(); |
679 | | |
680 | | // Depending on when the timeout occurred, the HTTP version of the request |
681 | | // may or may not be available. Therefore we check if the HTTP version is |
682 | | // set in the request. If it is not available, we need to create a dummy |
683 | | // request with the default HTTP/1.0 version. This version will be used |
684 | | // in the response. |
685 | 0 | if (request->context()->http_version_major_ == 0) { |
686 | 0 | request.reset(new HttpRequest(HttpRequest::Method::HTTP_POST, "/", |
687 | 0 | HttpVersion::HTTP_10(), |
688 | 0 | HostHttpHeader("dummy"))); |
689 | 0 | request->finalize(); |
690 | 0 | } |
691 | | |
692 | | // Create the timeout response. |
693 | 0 | HttpResponsePtr response = |
694 | 0 | response_creator_->createStockHttpResponse(request, |
695 | 0 | HttpStatusCode::REQUEST_TIMEOUT); |
696 | | |
697 | | // Send the HTTP 408 status. |
698 | 0 | asyncSendResponse(response, spawned_transaction); |
699 | 0 | } |
700 | | |
701 | | void |
702 | 0 | HttpConnection::idleTimeoutCallback() { |
703 | 0 | LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_DETAIL, |
704 | 0 | HTTP_IDLE_CONNECTION_TIMEOUT_OCCURRED) |
705 | 0 | .arg(getRemoteEndpointAddressAsText()); |
706 | | // In theory we should shutdown first and stop/close after but |
707 | | // it is better to put the connection management responsibility |
708 | | // on the client... so simply drop idle connections. |
709 | 0 | stopThisConnection(); |
710 | 0 | } |
711 | | |
712 | | std::string |
713 | 446 | HttpConnection::getRemoteEndpointAddressAsText() const { |
714 | 446 | try { |
715 | 446 | if (tcp_socket_) { |
716 | 446 | if (tcp_socket_->getASIOSocket().is_open()) { |
717 | 446 | return (tcp_socket_->getASIOSocket().remote_endpoint().address().to_string()); |
718 | 446 | } |
719 | 446 | } else if (tls_socket_) { |
720 | 0 | if (tls_socket_->getASIOSocket().is_open()) { |
721 | 0 | return (tls_socket_->getASIOSocket().remote_endpoint().address().to_string()); |
722 | 0 | } |
723 | 0 | } |
724 | 446 | } catch (...) { |
725 | 0 | } |
726 | 0 | return ("(unknown address)"); |
727 | 446 | } |
728 | | |
729 | | } // end of namespace isc::http |
730 | | } // end of namespace isc |