Coverage Report

Created: 2026-07-14 08:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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