Kea 2.7.4
connection.cc
Go to the documentation of this file.
1// Copyright (C) 2017-2024 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
10#include <dhcp/iface_mgr.h>
11#include <http/connection.h>
13#include <http/http_log.h>
14#include <http/http_messages.h>
15#include <boost/make_shared.hpp>
16#include <functional>
17
18using namespace isc::asiolink;
19using namespace isc::dhcp;
20using namespace isc::util;
21namespace ph = std::placeholders;
22
23namespace {
24
28constexpr size_t MAX_LOGGED_MESSAGE_SIZE = 1024;
29
30}
31
32namespace isc {
33namespace http {
34
36 const HttpRequestPtr& request)
37 : request_(request ? request : response_creator->createNewHttpRequest()),
38 parser_(new HttpRequestParser(*request_)),
39 input_buf_(),
40 output_buf_() {
41 parser_->initModel();
42}
43
46 return (boost::make_shared<Transaction>(response_creator));
47}
48
51 const TransactionPtr& transaction) {
52 if (transaction) {
53 return (boost::make_shared<Transaction>(response_creator,
54 transaction->getRequest()));
55 }
56 return (create(response_creator));
57}
58
59void
60HttpConnection::
61SocketCallback::operator()(boost::system::error_code ec, size_t length) {
62 if (ec.value() == boost::asio::error::operation_aborted) {
63 return;
64 }
65 callback_(ec, length);
66}
67
69 const HttpAcceptorPtr& acceptor,
70 const TlsContextPtr& tls_context,
71 HttpConnectionPool& connection_pool,
72 const HttpResponseCreatorPtr& response_creator,
73 const HttpAcceptorCallback& callback,
74 const long request_timeout,
75 const long idle_timeout)
76 : request_timer_(io_service),
77 request_timeout_(request_timeout),
78 tls_context_(tls_context),
79 idle_timeout_(idle_timeout),
82 acceptor_(acceptor),
83 connection_pool_(connection_pool),
84 response_creator_(response_creator),
85 acceptor_callback_(callback),
86 use_external_(false),
88 if (!tls_context) {
90 } else {
92 tls_context));
93 }
94}
95
99
100void
102 use_external_ = use_external;
103}
104
105void
107 if (!request) {
108 // Should never happen.
109 return;
110 }
111
112 // Record the remote address.
113 request->setRemote(getRemoteEndpointAddressAsText());
114
115 // Record TLS parameters.
116 if (!tls_socket_) {
117 return;
118 }
119
120 // The connection uses HTTPS aka HTTP over TLS.
121 request->setTls(true);
122
123 // Record the first commonName of the subjectName of the client
124 // certificate when wanted.
126 request->setSubject(tls_socket_->getTlsStream().getSubject());
127 }
128
129 // Record the first commonName of the issuerName of the client
130 // certificate when wanted.
132 request->setIssuer(tls_socket_->getTlsStream().getIssuer());
133 }
134}
135
136void
137HttpConnection::shutdownCallback(const boost::system::error_code&) {
138 if (use_external_) {
139 IfaceMgr::instance().deleteExternalSocket(tls_socket_->getNative());
141 }
142
143 tls_socket_->close();
144}
145
146void
149 if (tcp_socket_) {
150 if (use_external_) {
151 IfaceMgr::instance().deleteExternalSocket(tcp_socket_->getNative());
153 }
154 tcp_socket_->close();
155 return;
156 }
157 if (tls_socket_) {
158 // Create instance of the callback to close the socket.
159 SocketCallback cb(std::bind(&HttpConnection::shutdownCallback,
160 shared_from_this(),
161 ph::_1)); // error_code
162 tls_socket_->shutdown(cb);
163 return;
164 }
165 // Not reachable?
166 isc_throw(Unexpected, "internal error: unable to shutdown the socket");
167}
168
169void
171 if (!watch_socket_) {
173 return;
174 }
175 try {
176 watch_socket_->markReady();
177 } catch (const std::exception& ex) {
179 .arg(ex.what());
180 }
181}
182
183void
185 if (!watch_socket_) {
187 return;
188 }
189 try {
190 watch_socket_->clearReady();
191 } catch (const std::exception& ex) {
193 .arg(ex.what());
194 }
195}
196
197void
199 if (!watch_socket_) {
201 return;
202 }
203 IfaceMgr::instance().deleteExternalSocket(watch_socket_->getSelectFd());
204 // Close watch socket and log errors if occur.
205 std::string watch_error;
206 if (!watch_socket_->closeSocket(watch_error)) {
208 .arg(watch_error);
209 }
210}
211
212void
215 if (tcp_socket_) {
216 if (use_external_) {
217 IfaceMgr::instance().deleteExternalSocket(tcp_socket_->getNative());
219 }
220 tcp_socket_->close();
221 return;
222 }
223 if (tls_socket_) {
224 if (use_external_) {
225 IfaceMgr::instance().deleteExternalSocket(tls_socket_->getNative());
227 }
228 tls_socket_->close();
229 return;
230 }
231 // Not reachable?
232 isc_throw(Unexpected, "internal error: unable to close the socket");
233}
234
235void
246
247void
258
259void
261 // Create instance of the callback. It is safe to pass the local instance
262 // of the callback, because the underlying boost functions make copies
263 // as needed.
265 shared_from_this(),
266 ph::_1); // error
267 try {
268 HttpsAcceptorPtr tls_acceptor =
269 boost::dynamic_pointer_cast<HttpsAcceptor>(acceptor_);
270 if (!tls_acceptor) {
271 if (!tcp_socket_) {
272 isc_throw(Unexpected, "internal error: TCP socket is null");
273 }
274 acceptor_->asyncAccept(*tcp_socket_, cb);
275 } else {
276 if (!tls_socket_) {
277 isc_throw(Unexpected, "internal error: TLS socket is null");
278 }
279 tls_acceptor->asyncAccept(*tls_socket_, cb);
280 }
281 } catch (const std::exception& ex) {
282 isc_throw(HttpConnectionError, "unable to start accepting TCP "
283 "connections: " << ex.what());
284 }
285}
286
287void
289 // Skip the handshake if the socket is not a TLS one.
290 if (!tls_socket_) {
291 doRead();
292 return;
293 }
294
295 // Create instance of the callback. It is safe to pass the local instance
296 // of the callback, because the underlying boost functions make copies
297 // as needed.
298 SocketCallback cb(std::bind(&HttpConnection::handshakeCallback,
299 shared_from_this(),
300 ph::_1)); // error
301 try {
302 tls_socket_->handshake(cb);
303 if (use_external_) {
305 }
306 } catch (const std::exception& ex) {
307 isc_throw(HttpConnectionError, "unable to perform TLS handshake: "
308 << ex.what());
309 }
310}
311
312void
314 try {
315 TCPEndpoint endpoint;
316
317 // Transaction hasn't been created if we are starting to read the
318 // new request.
319 if (!transaction) {
321 recordParameters(transaction->getRequest());
322 }
323
324 // Create instance of the callback. It is safe to pass the local instance
325 // of the callback, because the underlying std functions make copies
326 // as needed.
327 SocketCallback cb(std::bind(&HttpConnection::socketReadCallback,
328 shared_from_this(),
329 transaction,
330 ph::_1, // error
331 ph::_2)); //bytes_transferred
332 if (tcp_socket_) {
333 tcp_socket_->asyncReceive(static_cast<void*>(transaction->getInputBufData()),
334 transaction->getInputBufSize(),
335 0, &endpoint, cb);
336 return;
337 }
338 if (tls_socket_) {
339 tls_socket_->asyncReceive(static_cast<void*>(transaction->getInputBufData()),
340 transaction->getInputBufSize(),
341 0, &endpoint, cb);
342 return;
343 }
344 } catch (...) {
346 }
347}
348
349void
351 try {
352 if (transaction->outputDataAvail()) {
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 SocketCallback cb(std::bind(&HttpConnection::socketWriteCallback,
357 shared_from_this(),
358 transaction,
359 ph::_1, // error
360 ph::_2)); // bytes_transferred
361 if (tcp_socket_) {
362 tcp_socket_->asyncSend(transaction->getOutputBufData(),
363 transaction->getOutputBufSize(),
364 cb);
365 if (use_external_) {
367 }
368 return;
369 }
370 if (tls_socket_) {
371 tls_socket_->asyncSend(transaction->getOutputBufData(),
372 transaction->getOutputBufSize(),
373 cb);
374 if (use_external_) {
376 }
377 return;
378 }
379 } else {
380 // The isPersistent() function may throw if the request hasn't
381 // been created, i.e. the HTTP headers weren't parsed. We catch
382 // this exception below and close the connection since we're
383 // unable to tell if the connection should remain persistent
384 // or not. The default is to close it.
385 if (!transaction->getRequest()->isPersistent()) {
387
388 } else {
389 // The connection is persistent and we are done sending
390 // the previous response. Start listening for the next
391 // requests.
393 doRead();
394 }
395 }
396 } catch (...) {
398 }
399}
400
401void
403 TransactionPtr transaction) {
404 transaction->setOutputBuf(response->toString());
405 doWrite(transaction);
406}
407
408
409void
410HttpConnection::acceptorCallback(const boost::system::error_code& ec) {
411 if (!acceptor_->isOpen()) {
412 return;
413 }
414
415 if (ec) {
417 }
418
420
421 if (!ec) {
422 if (!tls_context_) {
426 .arg(static_cast<unsigned>(request_timeout_/1000));
427 } else {
431 .arg(static_cast<unsigned>(request_timeout_/1000));
432 }
433
434 if (use_external_) {
435 auto& iface_mgr = IfaceMgr::instance();
436 if (tcp_socket_) {
437 iface_mgr.addExternalSocket(tcp_socket_->getNative(), 0);
438 }
439 if (tls_socket_) {
440 iface_mgr.addExternalSocket(tls_socket_->getNative(), 0);
441 }
442 watch_socket_.reset(new WatchSocket());
443 iface_mgr.addExternalSocket(watch_socket_->getSelectFd(), 0);
444 }
445
447 doHandshake();
448 }
449}
450
451void
452HttpConnection::handshakeCallback(const boost::system::error_code& ec) {
453 if (use_external_) {
455 }
456 if (ec) {
459 .arg(ec.message());
461 } else {
465
466 doRead();
467 }
468}
469
470void
472 boost::system::error_code ec, size_t length) {
473 if (ec) {
474 // IO service has been stopped and the connection is probably
475 // going to be shutting down.
476 if (ec.value() == boost::asio::error::operation_aborted) {
477 return;
478
479 // EWOULDBLOCK and EAGAIN are special cases. Everything else is
480 // treated as fatal error.
481 } else if ((ec.value() != boost::asio::error::try_again) &&
482 (ec.value() != boost::asio::error::would_block)) {
484
485 // We got EWOULDBLOCK or EAGAIN which indicate that we may be able to
486 // read something from the socket on the next attempt. Just make sure
487 // we don't try to read anything now in case there is any garbage
488 // passed in length.
489 } else {
490 length = 0;
491 }
492 }
493
494 // Receiving is in progress, so push back the timeout.
495 setupRequestTimer(transaction);
496
497 if (length != 0) {
500 .arg(length)
502
503 transaction->getParser()->postBuffer(static_cast<void*>(transaction->getInputBufData()),
504 length);
505 transaction->getParser()->poll();
506 }
507
508 if (transaction->getParser()->needData()) {
509 // The parser indicates that the some part of the message being
510 // received is still missing, so continue to read.
511 doRead(transaction);
512
513 } else {
514 try {
515 // The whole message has been received, so let's finalize it.
516 transaction->getRequest()->finalize();
517
521
525 .arg(transaction->getParser()->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE));
526
527 } catch (const std::exception& ex) {
531 .arg(ex.what());
532
536 .arg(transaction->getParser()->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE));
537 }
538
539 // Don't want to timeout if creation of the response takes long.
541
542 // Create the response from the received request using the custom
543 // response creator.
544 HttpResponsePtr response = response_creator_->createHttpResponse(transaction->getRequest());
547 .arg(response->toBriefString())
549
553 .arg(HttpMessageParserBase::logFormatHttpMessage(response->toString(),
554 MAX_LOGGED_MESSAGE_SIZE));
555
556 // Response created. Activate the timer again.
557 setupRequestTimer(transaction);
558
559 // Start sending the response.
560 asyncSendResponse(response, transaction);
561 }
562}
563
564void
566 boost::system::error_code ec, size_t length) {
567 if (use_external_) {
569 }
570 if (ec) {
571 // IO service has been stopped and the connection is probably
572 // going to be shutting down.
573 if (ec.value() == boost::asio::error::operation_aborted) {
574 return;
575
576 // EWOULDBLOCK and EAGAIN are special cases. Everything else is
577 // treated as fatal error.
578 } else if ((ec.value() != boost::asio::error::try_again) &&
579 (ec.value() != boost::asio::error::would_block)) {
581
582 // We got EWOULDBLOCK or EAGAIN which indicate that we may be able to
583 // read something from the socket on the next attempt.
584 } else {
585 // Sending is in progress, so push back the timeout.
586 setupRequestTimer(transaction);
587
588 doWrite(transaction);
589 }
590 }
591
592 // Since each transaction has its own output buffer, it is not really
593 // possible that the number of bytes written is larger than the size
594 // of the buffer. But, let's be safe and set the length to the size
595 // of the buffer if that unexpected condition occurs.
596 if (length > transaction->getOutputBufSize()) {
597 length = transaction->getOutputBufSize();
598 }
599
600 if (length <= transaction->getOutputBufSize()) {
601 // Sending is in progress, so push back the timeout.
602 setupRequestTimer(transaction);
603 }
604
605 // Eat the 'length' number of bytes from the output buffer and only
606 // leave the part of the response that hasn't been sent.
607 transaction->consumeOutputBuf(length);
608
609 // Schedule the write of the unsent data.
610 doWrite(transaction);
611}
612
613void
615 // Pass raw pointer rather than shared_ptr to this object,
616 // because IntervalTimer already passes shared pointer to the
617 // IntervalTimerImpl to make sure that the callback remains
618 // valid.
620 this, transaction),
622}
623
624void
630
631void
636
637 // We need to differentiate the transactions between a normal response and the
638 // timeout. We create new transaction from the current transaction. It is
639 // to preserve the request we're responding to.
640 auto spawned_transaction = Transaction::spawn(response_creator_, transaction);
641
642 // The new transaction inherits the request from the original transaction
643 // if such transaction exists.
644 auto request = spawned_transaction->getRequest();
645
646 // Depending on when the timeout occurred, the HTTP version of the request
647 // may or may not be available. Therefore we check if the HTTP version is
648 // set in the request. If it is not available, we need to create a dummy
649 // request with the default HTTP/1.0 version. This version will be used
650 // in the response.
651 if (request->context()->http_version_major_ == 0) {
652 request.reset(new HttpRequest(HttpRequest::Method::HTTP_POST, "/",
654 HostHttpHeader("dummy")));
655 request->finalize();
656 }
657
658 // Create the timeout response.
659 HttpResponsePtr response =
660 response_creator_->createStockHttpResponse(request,
662
663 // Send the HTTP 408 status.
664 asyncSendResponse(response, spawned_transaction);
665}
666
667void
672 // In theory we should shutdown first and stop/close after but
673 // it is better to put the connection management responsibility
674 // on the client... so simply drop idle connections.
676}
677
678std::string
680 try {
681 if (tcp_socket_) {
682 if (tcp_socket_->getASIOSocket().is_open()) {
683 return (tcp_socket_->getASIOSocket().remote_endpoint().address().to_string());
684 }
685 } else if (tls_socket_) {
686 if (tls_socket_->getASIOSocket().is_open()) {
687 return (tls_socket_->getASIOSocket().remote_endpoint().address().to_string());
688 }
689 }
690 } catch (...) {
691 }
692 return ("(unknown address)");
693}
694
695} // end of namespace isc::http
696} // end of namespace isc
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown when an unexpected error condition occurs.
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition iface_mgr.cc:54
Represents HTTP Host header.
Definition http_header.h:68
Generic error reported within HttpConnection class.
Definition connection.h:27
Pool of active HTTP connections.
void stop(const HttpConnectionPtr &connection)
Removes a connection from the pool and stops it.
void shutdown(const HttpConnectionPtr &connection)
Removes a connection from the pool and shutdown it.
static TransactionPtr create(const HttpResponseCreatorPtr &response_creator)
Creates new transaction instance.
Definition connection.cc:45
Transaction(const HttpResponseCreatorPtr &response_creator, const HttpRequestPtr &request=HttpRequestPtr())
Constructor.
Definition connection.cc:35
static TransactionPtr spawn(const HttpResponseCreatorPtr &response_creator, const TransactionPtr &transaction)
Creates new transaction from the current transaction.
Definition connection.cc:50
void closeWatchSocket()
Close the watch socket.
HttpConnectionPool & connection_pool_
Connection pool holding this connection.
Definition connection.h:444
void socketReadCallback(TransactionPtr transaction, boost::system::error_code ec, size_t length)
Callback invoked when new data is received over the socket.
asiolink::TlsContextPtr tls_context_
TLS context.
Definition connection.h:428
void markWatchSocketReady()
Mark the watch socket as ready.
void recordParameters(const HttpRequestPtr &request) const
Records connection parameters into the HTTP request.
void acceptorCallback(const boost::system::error_code &ec)
Local callback invoked when new connection is accepted.
boost::shared_ptr< Transaction > TransactionPtr
Shared pointer to the Transaction.
Definition connection.h:87
void setupIdleTimer()
Reset timer for detecting idle timeout in persistent connections.
void clearWatchSocket()
Clear the watch socket's ready marker.
virtual void socketWriteCallback(TransactionPtr transaction, boost::system::error_code ec, size_t length)
Callback invoked when data is sent over the socket.
void doHandshake()
Asynchronously performs TLS handshake.
void asyncSendResponse(const ConstHttpResponsePtr &response, TransactionPtr transaction)
Sends HTTP response asynchronously.
void doRead(TransactionPtr transaction=TransactionPtr())
Starts asynchronous read from the socket.
HttpAcceptorPtr acceptor_
Pointer to the TCP acceptor used to accept new connections.
Definition connection.h:441
std::unique_ptr< asiolink::TLSSocket< SocketCallback > > tls_socket_
TLS socket used by this connection.
Definition connection.h:438
void doWrite(TransactionPtr transaction)
Starts asynchronous write to the socket.
void setupRequestTimer(TransactionPtr transaction=TransactionPtr())
Reset timer for detecting request timeouts.
HttpConnection(const asiolink::IOServicePtr &io_service, const HttpAcceptorPtr &acceptor, const asiolink::TlsContextPtr &tls_context, HttpConnectionPool &connection_pool, const HttpResponseCreatorPtr &response_creator, const HttpAcceptorCallback &callback, const long request_timeout, const long idle_timeout)
Constructor.
Definition connection.cc:68
void shutdown()
Shutdown the socket.
void shutdownCallback(const boost::system::error_code &ec)
Callback invoked when TLS shutdown is performed.
void close()
Closes the socket.
void stopThisConnection()
Stops current connection.
std::string getRemoteEndpointAddressAsText() const
Returns remote address in textual form.
bool use_external_
Use external sockets flag.
Definition connection.h:454
void handshakeCallback(const boost::system::error_code &ec)
Local callback invoked when TLS handshake is performed.
HttpResponseCreatorPtr response_creator_
Pointer to the HttpResponseCreator object used to create HTTP responses.
Definition connection.h:448
long idle_timeout_
Timeout after which the persistent HTTP connection is shut down by the server.
Definition connection.h:432
void asyncAccept()
Asynchronously accepts new connection.
std::unique_ptr< asiolink::TCPSocket< SocketCallback > > tcp_socket_
TCP socket used by this connection.
Definition connection.h:435
void requestTimeoutCallback(TransactionPtr transaction)
Callback invoked when the HTTP Request Timeout occurs.
void addExternalSockets(bool use_external=false)
Use external sockets flag.
asiolink::IntervalTimer request_timer_
Timer used to detect Request Timeout.
Definition connection.h:422
HttpAcceptorCallback acceptor_callback_
External TCP acceptor callback.
Definition connection.h:451
util::WatchSocketPtr watch_socket_
Pointer to watch socket instance used to signal that the socket is ready for read or write when use e...
Definition connection.h:458
long request_timeout_
Configured Request Timeout in milliseconds.
Definition connection.h:425
void shutdownConnection()
Shuts down current connection.
virtual ~HttpConnection()
Destructor.
Definition connection.cc:96
static std::string logFormatHttpMessage(const std::string &message, const size_t limit=0)
Formats provided HTTP message for logging.
A generic parser for HTTP requests.
Represents HTTP request message.
Definition request.h:57
static bool recordIssuer_
Record issuer name.
Definition request.h:255
static bool recordSubject_
Access control parameters: Flags which indicate what information to record.
Definition request.h:252
Provides an IO "ready" semaphore for use with select() or poll() WatchSocket exposes a single open fi...
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition macros.h:20
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
const isc::log::MessageID HTTP_CONNECTION_SHUTDOWN
const isc::log::MessageID HTTP_CONNECTION_WATCH_SOCKET_CLOSE_ERROR
const isc::log::MessageID HTTP_CONNECTION_HANDSHAKE_START
const isc::log::MessageID HTTP_CONNECTION_SHUTDOWN_FAILED
boost::shared_ptr< const HttpResponse > ConstHttpResponsePtr
Pointer to the const HttpResponse object.
Definition response.h:84
const isc::log::MessageID HTTP_IDLE_CONNECTION_TIMEOUT_OCCURRED
const isc::log::MessageID HTTP_DATA_RECEIVED
const isc::log::MessageID HTTP_BAD_CLIENT_REQUEST_RECEIVED_DETAILS
const isc::log::MessageID HTTP_SERVER_RESPONSE_SEND_DETAILS
const isc::log::MessageID HTTP_CONNECTION_STOP_FAILED
isc::log::Logger http_logger("http")
Defines the logger used within libkea-http library.
Definition http_log.h:18
const isc::log::MessageID HTTP_CLIENT_REQUEST_RECEIVED
const isc::log::MessageID HTTP_CLIENT_REQUEST_TIMEOUT_OCCURRED
const isc::log::MessageID HTTPS_REQUEST_RECEIVE_START
const isc::log::MessageID HTTP_CONNECTION_STOP
const isc::log::MessageID HTTP_CONNECTION_HANDSHAKE_FAILED
boost::shared_ptr< HttpResponseCreator > HttpResponseCreatorPtr
Pointer to the HttpResponseCreator object.
boost::shared_ptr< HttpResponse > HttpResponsePtr
Pointer to the HttpResponse object.
Definition response.h:81
const isc::log::MessageID HTTP_REQUEST_RECEIVE_START
std::function< void(const boost::system::error_code &) HttpAcceptorCallback)
Type of the callback for the TCP acceptor used in this library.
const isc::log::MessageID HTTP_CLIENT_REQUEST_RECEIVED_DETAILS
const isc::log::MessageID HTTP_CONNECTION_WATCH_SOCKET_CLEAR_ERROR
boost::shared_ptr< HttpRequest > HttpRequestPtr
Pointer to the HttpRequest object.
Definition request.h:30
boost::shared_ptr< HttpAcceptor > HttpAcceptorPtr
Type of shared pointer to TCP acceptors.
boost::shared_ptr< HttpsAcceptor > HttpsAcceptorPtr
Type of shared pointer to TLS acceptors.
const isc::log::MessageID HTTP_CONNECTION_WATCH_SOCKET_MARK_READY_ERROR
const isc::log::MessageID HTTP_BAD_CLIENT_REQUEST_RECEIVED
const isc::log::MessageID HTTP_SERVER_RESPONSE_SEND
const int DBGLVL_TRACE_BASIC
Trace basic operations.
const int DBGLVL_TRACE_DETAIL_DATA
Trace data associated with detailed operations.
const int DBGLVL_TRACE_BASIC_DATA
Trace data associated with the basic operations.
const int DBGLVL_TRACE_DETAIL
Trace detailed operations.
Defines the logger used by the top-level component of kea-lfc.
static const HttpVersion & HTTP_10()
HTTP version 1.0.
Definition http_types.h:53