Kea 2.7.6
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 HttpConnectionPoolPtr 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_) {
141 }
142
143 tls_socket_->close();
144}
145
146void
149 if (tcp_socket_) {
150 if (use_external_) {
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 }
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_) {
219 }
220 tcp_socket_->close();
221 return;
222 }
223 if (tls_socket_) {
224 if (use_external_) {
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
237 auto connection_pool = connection_pool_.lock();
238 try {
242 if (connection_pool) {
243 connection_pool->shutdown(shared_from_this());
244 } else {
245 shutdown();
246 }
247 } catch (...) {
249 }
250}
251
252void
254 auto connection_pool = connection_pool_.lock();
255 try {
259 if (connection_pool) {
260 connection_pool->stop(shared_from_this());
261 } else {
262 close();
263 }
264 } catch (...) {
266 }
267}
268
269void
271 // Create instance of the callback. It is safe to pass the local instance
272 // of the callback, because the underlying boost functions make copies
273 // as needed.
275 shared_from_this(),
276 ph::_1); // error
277 try {
278 HttpsAcceptorPtr tls_acceptor =
279 boost::dynamic_pointer_cast<HttpsAcceptor>(acceptor_);
280 if (!tls_acceptor) {
281 if (!tcp_socket_) {
282 isc_throw(Unexpected, "internal error: TCP socket is null");
283 }
284 acceptor_->asyncAccept(*tcp_socket_, cb);
285 } else {
286 if (!tls_socket_) {
287 isc_throw(Unexpected, "internal error: TLS socket is null");
288 }
289 tls_acceptor->asyncAccept(*tls_socket_, cb);
290 }
291 } catch (const std::exception& ex) {
292 isc_throw(HttpConnectionError, "unable to start accepting TCP "
293 "connections: " << ex.what());
294 }
295}
296
297void
299 // Skip the handshake if the socket is not a TLS one.
300 if (!tls_socket_) {
301 doRead();
302 return;
303 }
304
305 // Create instance of the callback. It is safe to pass the local instance
306 // of the callback, because the underlying boost functions make copies
307 // as needed.
308 SocketCallback cb(std::bind(&HttpConnection::handshakeCallback,
309 shared_from_this(),
310 ph::_1)); // error
311 try {
312 tls_socket_->handshake(cb);
313 if (use_external_) {
315 }
316 } catch (const std::exception& ex) {
317 isc_throw(HttpConnectionError, "unable to perform TLS handshake: "
318 << ex.what());
319 }
320}
321
322void
324 try {
325 TCPEndpoint endpoint;
326
327 // Transaction hasn't been created if we are starting to read the
328 // new request.
329 if (!transaction) {
331 recordParameters(transaction->getRequest());
332 }
333
334 // Create instance of the callback. It is safe to pass the local instance
335 // of the callback, because the underlying std functions make copies
336 // as needed.
337 SocketCallback cb(std::bind(&HttpConnection::socketReadCallback,
338 shared_from_this(),
339 transaction,
340 ph::_1, // error
341 ph::_2)); //bytes_transferred
342 if (tcp_socket_) {
343 tcp_socket_->asyncReceive(static_cast<void*>(transaction->getInputBufData()),
344 transaction->getInputBufSize(),
345 0, &endpoint, cb);
346 return;
347 }
348 if (tls_socket_) {
349 tls_socket_->asyncReceive(static_cast<void*>(transaction->getInputBufData()),
350 transaction->getInputBufSize(),
351 0, &endpoint, cb);
352 return;
353 }
354 } catch (...) {
356 }
357}
358
359void
361 try {
362 if (transaction->outputDataAvail()) {
363 // Create instance of the callback. It is safe to pass the local instance
364 // of the callback, because the underlying std functions make copies
365 // as needed.
366 SocketCallback cb(std::bind(&HttpConnection::socketWriteCallback,
367 shared_from_this(),
368 transaction,
369 ph::_1, // error
370 ph::_2)); // bytes_transferred
371 if (tcp_socket_) {
372 tcp_socket_->asyncSend(transaction->getOutputBufData(),
373 transaction->getOutputBufSize(),
374 cb);
375 if (use_external_) {
377 }
378 return;
379 }
380 if (tls_socket_) {
381 tls_socket_->asyncSend(transaction->getOutputBufData(),
382 transaction->getOutputBufSize(),
383 cb);
384 if (use_external_) {
386 }
387 return;
388 }
389 } else {
390 // The isPersistent() function may throw if the request hasn't
391 // been created, i.e. the HTTP headers weren't parsed. We catch
392 // this exception below and close the connection since we're
393 // unable to tell if the connection should remain persistent
394 // or not. The default is to close it.
395 if (!transaction->getRequest()->isPersistent()) {
397
398 } else {
399 // The connection is persistent and we are done sending
400 // the previous response. Start listening for the next
401 // requests.
403 doRead();
404 }
405 }
406 } catch (...) {
408 }
409}
410
411void
413 TransactionPtr transaction) {
414 transaction->setOutputBuf(response->toString());
415 doWrite(transaction);
416}
417
418
419void
420HttpConnection::acceptorCallback(const boost::system::error_code& ec) {
421 if (!acceptor_->isOpen()) {
422 return;
423 }
424
425 if (ec) {
427 }
428
430
431 if (!ec) {
432 if (!tls_context_) {
436 .arg(static_cast<unsigned>(request_timeout_/1000));
437 } else {
441 .arg(static_cast<unsigned>(request_timeout_/1000));
442 }
443
444 if (use_external_) {
445 auto& iface_mgr = IfaceMgr::instance();
446 if (tcp_socket_) {
447 iface_mgr.addExternalSocket(tcp_socket_->getNative(), 0);
448 }
449 if (tls_socket_) {
450 iface_mgr.addExternalSocket(tls_socket_->getNative(), 0);
451 }
452 watch_socket_.reset(new WatchSocket());
453 iface_mgr.addExternalSocket(watch_socket_->getSelectFd(), 0);
454 }
455
457 doHandshake();
458 }
459}
460
461void
462HttpConnection::handshakeCallback(const boost::system::error_code& ec) {
463 if (use_external_) {
465 }
466 if (ec) {
469 .arg(ec.message());
471 } else {
475
476 doRead();
477 }
478}
479
480void
482 boost::system::error_code ec, size_t length) {
483 if (ec) {
484 // IO service has been stopped and the connection is probably
485 // going to be shutting down.
486 if (ec.value() == boost::asio::error::operation_aborted) {
487 return;
488
489 // EWOULDBLOCK and EAGAIN are special cases. Everything else is
490 // treated as fatal error.
491 } else if ((ec.value() != boost::asio::error::try_again) &&
492 (ec.value() != boost::asio::error::would_block)) {
494
495 // We got EWOULDBLOCK or EAGAIN which indicate that we may be able to
496 // read something from the socket on the next attempt. Just make sure
497 // we don't try to read anything now in case there is any garbage
498 // passed in length.
499 } else {
500 length = 0;
501 }
502 }
503
504 // Receiving is in progress, so push back the timeout.
505 setupRequestTimer(transaction);
506
507 if (length != 0) {
510 .arg(length)
512
513 transaction->getParser()->postBuffer(static_cast<void*>(transaction->getInputBufData()),
514 length);
515 transaction->getParser()->poll();
516 }
517
518 if (transaction->getParser()->needData()) {
519 // The parser indicates that the some part of the message being
520 // received is still missing, so continue to read.
521 doRead(transaction);
522
523 } else {
524 try {
525 // The whole message has been received, so let's finalize it.
526 transaction->getRequest()->finalize();
527
531
535 .arg(transaction->getParser()->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE));
536
537 } catch (const std::exception& ex) {
541 .arg(ex.what());
542
546 .arg(transaction->getParser()->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE));
547 }
548
549 // Don't want to timeout if creation of the response takes long.
551
552 // Create the response from the received request using the custom
553 // response creator.
554 HttpResponsePtr response = response_creator_->createHttpResponse(transaction->getRequest());
557 .arg(response->toBriefString())
559
563 .arg(HttpMessageParserBase::logFormatHttpMessage(response->toString(),
564 MAX_LOGGED_MESSAGE_SIZE));
565
566 // Response created. Activate the timer again.
567 setupRequestTimer(transaction);
568
569 // Start sending the response.
570 asyncSendResponse(response, transaction);
571 }
572}
573
574void
576 boost::system::error_code ec, size_t length) {
577 if (use_external_) {
579 }
580 if (ec) {
581 // IO service has been stopped and the connection is probably
582 // going to be shutting down.
583 if (ec.value() == boost::asio::error::operation_aborted) {
584 return;
585
586 // EWOULDBLOCK and EAGAIN are special cases. Everything else is
587 // treated as fatal error.
588 } else if ((ec.value() != boost::asio::error::try_again) &&
589 (ec.value() != boost::asio::error::would_block)) {
591
592 // We got EWOULDBLOCK or EAGAIN which indicate that we may be able to
593 // read something from the socket on the next attempt.
594 } else {
595 // Sending is in progress, so push back the timeout.
596 setupRequestTimer(transaction);
597
598 doWrite(transaction);
599 }
600 }
601
602 // Since each transaction has its own output buffer, it is not really
603 // possible that the number of bytes written is larger than the size
604 // of the buffer. But, let's be safe and set the length to the size
605 // of the buffer if that unexpected condition occurs.
606 if (length > transaction->getOutputBufSize()) {
607 length = transaction->getOutputBufSize();
608 }
609
610 if (length <= transaction->getOutputBufSize()) {
611 // Sending is in progress, so push back the timeout.
612 setupRequestTimer(transaction);
613 }
614
615 // Eat the 'length' number of bytes from the output buffer and only
616 // leave the part of the response that hasn't been sent.
617 transaction->consumeOutputBuf(length);
618
619 // Schedule the write of the unsent data.
620 doWrite(transaction);
621}
622
623void
625 // Pass raw pointer rather than shared_ptr to this object,
626 // because IntervalTimer already passes shared pointer to the
627 // IntervalTimerImpl to make sure that the callback remains
628 // valid.
630 this, transaction),
632}
633
634void
640
641void
646
647 // We need to differentiate the transactions between a normal response and the
648 // timeout. We create new transaction from the current transaction. It is
649 // to preserve the request we're responding to.
650 auto spawned_transaction = Transaction::spawn(response_creator_, transaction);
651
652 // The new transaction inherits the request from the original transaction
653 // if such transaction exists.
654 auto request = spawned_transaction->getRequest();
655
656 // Depending on when the timeout occurred, the HTTP version of the request
657 // may or may not be available. Therefore we check if the HTTP version is
658 // set in the request. If it is not available, we need to create a dummy
659 // request with the default HTTP/1.0 version. This version will be used
660 // in the response.
661 if (request->context()->http_version_major_ == 0) {
662 request.reset(new HttpRequest(HttpRequest::Method::HTTP_POST, "/",
664 HostHttpHeader("dummy")));
665 request->finalize();
666 }
667
668 // Create the timeout response.
669 HttpResponsePtr response =
670 response_creator_->createStockHttpResponse(request,
672
673 // Send the HTTP 408 status.
674 asyncSendResponse(response, spawned_transaction);
675}
676
677void
682 // In theory we should shutdown first and stop/close after but
683 // it is better to put the connection management responsibility
684 // on the client... so simply drop idle connections.
686}
687
688std::string
690 try {
691 if (tcp_socket_) {
692 if (tcp_socket_->getASIOSocket().is_open()) {
693 return (tcp_socket_->getASIOSocket().remote_endpoint().address().to_string());
694 }
695 } else if (tls_socket_) {
696 if (tls_socket_->getASIOSocket().is_open()) {
697 return (tls_socket_->getASIOSocket().remote_endpoint().address().to_string());
698 }
699 }
700 } catch (...) {
701 }
702 return ("(unknown address)");
703}
704
705} // end of namespace isc::http
706} // end of namespace isc
A generic exception that is thrown when an unexpected error condition occurs.
void deleteExternalSocket(int socketfd)
Deletes external socket.
Definition iface_mgr.cc:352
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
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.
void socketReadCallback(TransactionPtr transaction, boost::system::error_code ec, size_t length)
Callback invoked when new data is received over the socket.
std::weak_ptr< HttpConnectionPool > connection_pool_
Connection pool holding this connection.
Definition connection.h:444
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.
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
boost::shared_ptr< Transaction > TransactionPtr
Shared pointer to the Transaction.
Definition connection.h:87
void doWrite(TransactionPtr transaction)
Starts asynchronous write to the socket.
void setupRequestTimer(TransactionPtr transaction=TransactionPtr())
Reset timer for detecting request timeouts.
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.
HttpConnection(const asiolink::IOServicePtr &io_service, const HttpAcceptorPtr &acceptor, const asiolink::TlsContextPtr &tls_context, std::shared_ptr< HttpConnectionPool > connection_pool, const HttpResponseCreatorPtr &response_creator, const HttpAcceptorCallback &callback, const long request_timeout, const long idle_timeout)
Constructor.
Definition connection.cc:68
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
std::shared_ptr< HttpConnectionPool > HttpConnectionPoolPtr
Pointer to the HttpConnectionPool.
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
const isc::log::MessageID HTTP_CLIENT_REQUEST_RECEIVED_DETAILS
const isc::log::MessageID HTTP_CONNECTION_WATCH_SOCKET_CLEAR_ERROR
std::function< void(const boost::system::error_code &)> HttpAcceptorCallback
Type of the callback for the TCP acceptor used in this library.
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