Commit 329c1c9a authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[3807] Improved log messages for the DHCPv6 server.

Also added a few minor corrections to the DHCPv4 server and implemented
new Option6StatusCode.
parent 5597704e
......@@ -404,9 +404,12 @@ The second argument includes the error string.
% DHCP4_PACKET_RECEIVED %1: %2 (type %3) received from %4 to %5 on interface %6
A debug message noting that the server has received the specified type of
packet on the specified interface. Note that a packet marked as UNKNOWN
may well be a valid DHCP packet, just a type not expected by the server
(e.g. it will report a received OFFER packet as UNKNOWN).
packet on the specified interface. The first argument specifies the
client and transaction identification information. The second and third
argument specify the name of the DHCPv4 message and its numeric type
respectively. The remaining arguments specify the source address,
destination IP address and the name of the interface on which the
message has been received.
% DHCP4_PACKET_SEND %1: sending packet %2 (type %3) from %4:%5 to %6:%7 on interface %8
This debug message is issued when the server is sending the response to
......@@ -584,7 +587,7 @@ It lists some information about the parameters with which the server
is running.
% DHCP4_SUBNET_DATA %1: the selected subnet details: %2
This debug message includes the details of the subnet selected fot
This debug message includes the details of the subnet selected for
the client. The first argument includes the client and the
transaction identification information. The second arguments
includes the subnet details.
......
......@@ -364,7 +364,7 @@ Dhcpv4Srv::run() {
// This is not an error because we might have received a SIGTERM,
// SIGINT or SIGHUP which are handled by the server. For signals
// that are not handled by the server we rely on the default
// behavior of the system, but there is nothing we should log here.
// behavior of the system.
LOG_DEBUG(packet_logger, DBG_DHCP4_DETAIL, DHCP4_BUFFER_WAIT_SIGNAL)
.arg(signal_set_->getNext());
} catch (const std::exception& e) {
......
......@@ -25,26 +25,39 @@ namespace dhcp {
/// @name Constants defining debug levels for logging in DHCPv6 server.
//@{
// Debug levels used to log information during startup and shutdown.
/// @brief Debug level used to log information during server startup.
const int DBG_DHCP6_START = DBGLVL_START_SHUT;
/// @brief Debug level used to log information during server shutdown.
const int DBG_DHCP6_SHUT = DBGLVL_START_SHUT;
// Debug level used to log setting information (such as configuration changes).
/// @brief Debug level used to log receiving commands.
const int DBG_DHCP6_COMMAND = DBGLVL_COMMAND;
// Trace basic operations within the code.
/// @brief Debug level used to trace basic operations within the code.
const int DBG_DHCP6_BASIC = DBGLVL_TRACE_BASIC;
// Trace hook related operations
/// @brief Debug level used to trace hook related operations
const int DBG_DHCP6_HOOKS = DBGLVL_TRACE_BASIC;
// Trace detailed operations, including errors raised when processing invalid
// packets. (These are not logged at severities of WARN or higher for fear
// that a set of deliberately invalid packets set to the server could overwhelm
// the logging.)
/// @brief Debug level used to log the traces with some basic data.
///
/// The basic data includes summary information, e.g. summary of the
/// information returned by a particular function. It may also include
/// more detailed information in cases when it is warranted and the
/// extraction of the data doesn't impact the server's performance
/// significantly.
const int DBG_DHCP6_BASIC_DATA = DBGLVL_TRACE_BASIC_DATA;
/// @brief Debug level used to trace detailed errors.
///
/// Trace detailed operations, including errors raised when processing invalid
/// packets. (These are not logged at severities of WARN or higher for fear
/// that a set of deliberately invalid packets set to the server could overwhelm
/// the logging.)
const int DBG_DHCP6_DETAIL = DBGLVL_TRACE_DETAIL;
// This level is used to log the contents of packets received and sent.
/// @brief This level is used to log the contents of packets received and sent.
const int DBG_DHCP6_DETAIL_DATA = DBGLVL_TRACE_DETAIL_DATA;
//@}
......
This diff is collapsed.
This diff is collapsed.
......@@ -255,13 +255,6 @@ protected:
/// @return Reply message to be sent to the client.
Pkt6Ptr processInfRequest(const Pkt6Ptr& inf_request);
/// @brief Creates status-code option.
///
/// @param code status code value (see RFC3315)
/// @param text textual explanation (will be sent in status code option)
/// @return status-code option
OptionPtr createStatusCode(uint16_t code, const std::string& text);
/// @brief Selects a subnet for a given client's packet.
///
/// @param question client's message
......@@ -530,9 +523,12 @@ protected:
/// own.
/// If ddns updates are disabled, this method returns immediately.
///
/// @param query A pointer to the packet sent by the client for which the
/// name change request should be sent.
/// @param lease A lease for which the the removal of corresponding DNS
/// records will be performed.
void createRemovalNameChangeRequest(const Lease6Ptr& lease);
void createRemovalNameChangeRequest(const Pkt6Ptr& query,
const Lease6Ptr& lease);
/// @brief Attempts to extend the lifetime of IAs.
///
......@@ -728,13 +724,14 @@ private:
/// If there are any differences (different fwd or rev flags, or different
/// hostname) a DNS update for removing entry will be generated.
///
/// @param query a pointer to the client's message
/// @param old_lease old version of the lease
/// @param new_lease new version of the lease (may be NULL)
/// @param hostname specifies hostname (for printing purposes)
/// @param do_fwd specifies if reverse updates are enabled (for printing purposes)
/// @param do_rev specifies if reverse updates are enabled (for printing purposes)
void conditionalNCRRemoval(Lease6Ptr& old_lease, Lease6Ptr& new_lease,
const std::string& hostname,
void conditionalNCRRemoval(const Pkt6Ptr& query, Lease6Ptr& old_lease,
Lease6Ptr& new_lease, const std::string& hostname,
bool do_fwd, bool do_rev);
/// @brief Allocation Engine.
......
......@@ -17,6 +17,7 @@
#include <dhcp/option_custom.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_status_code.h>
#include <dhcp/option_int_array.h>
#include <dhcp/pkt6.h>
#include <dhcpsrv/lease.h>
......@@ -143,10 +144,10 @@ Dhcp6Client::applyRcvdConfiguration(const Pkt6Ptr& reply) {
{
// Check if the server has sent status code. If no status
// code, assume the status code to be 0.
OptionCustomPtr status_code = boost::dynamic_pointer_cast<
OptionCustom>(ia->getOption(D6O_STATUS_CODE));
Option6StatusCodePtr status_code = boost::dynamic_pointer_cast<
Option6StatusCode>(ia->getOption(D6O_STATUS_CODE));
lease_info.status_code_ =
status_code ? status_code->readInteger<uint16_t>(0) : 0;
status_code ? status_code->getStatusCode() : 0;
}
break;
......@@ -159,13 +160,13 @@ Dhcp6Client::applyRcvdConfiguration(const Pkt6Ptr& reply) {
}
// Get the global status code.
OptionCustomPtr status_code = boost::dynamic_pointer_cast<
OptionCustom>(reply->getOption(D6O_STATUS_CODE));
Option6StatusCodePtr status_code = boost::dynamic_pointer_cast<
Option6StatusCode>(reply->getOption(D6O_STATUS_CODE));
// If status code has been sent, we override the default status code:
// Success and record that we have received the status code.
if (status_code) {
config_.received_status_code_ = true;
config_.status_code_ = status_code->readInteger<uint16_t>(0);
config_.status_code_ = status_code->getStatusCode();
}
}
......
......@@ -1019,30 +1019,6 @@ TEST_F(Dhcpv6SrvTest, pdReleaseReject) {
testReleaseReject(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"));
}
// This test verifies if the status code option is generated properly.
TEST_F(Dhcpv6SrvTest, StatusCode) {
NakedDhcpv6Srv srv(0);
// a dummy content for client-id
uint8_t expected[] = {
0x0, 0xD, // option code = 13
0x0, 0x7, // option length = 7
0x0, 0x3, // status code = 3
0x41, 0x42, 0x43, 0x44, 0x45 // string value ABCDE
};
// Create the option.
OptionPtr status = srv.createStatusCode(3, "ABCDE");
// Allocate an output buffer. We will store the option
// in wire format here.
OutputBuffer buf(sizeof(expected));
// Prepare the wire format.
ASSERT_NO_THROW(status->pack(buf));
// Check that the option buffer has valid length (option header + data).
ASSERT_EQ(sizeof(expected), buf.getLength());
// Verify the contents of the option.
EXPECT_EQ(0, memcmp(expected, buf.getData(), sizeof(expected)));
}
// This test verifies if the sanityCheck() really checks options presence.
TEST_F(Dhcpv6SrvTest, sanityCheck) {
NakedDhcpv6Srv srv(0);
......
......@@ -14,6 +14,7 @@
#include <config.h>
#include <gtest/gtest.h>
#include <dhcp/option6_status_code.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
#include <dhcp6/json_config_parser.h>
#include <config/ccsession.h>
......@@ -829,8 +830,8 @@ NakedDhcpv6SrvTest::checkIA_NAStatusCode(
EXPECT_EQ(expected_t1, ia->getT1());
EXPECT_EQ(expected_t2, ia->getT2());
isc::dhcp::OptionCustomPtr status =
boost::dynamic_pointer_cast<isc::dhcp::OptionCustom>
isc::dhcp::Option6StatusCodePtr status =
boost::dynamic_pointer_cast<isc::dhcp::Option6StatusCode>
(ia->getOption(D6O_STATUS_CODE));
// It is ok to not include status success as this is the default
......@@ -847,7 +848,7 @@ NakedDhcpv6SrvTest::checkIA_NAStatusCode(
// status code option content is just a text explanation
// what went wrong.
EXPECT_EQ(static_cast<uint16_t>(expected_status_code),
status->readInteger<uint16_t>(0));
status->getStatusCode());
}
}
......
......@@ -25,6 +25,7 @@
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_iaprefix.h>
#include <dhcp/option6_status_code.h>
#include <dhcp/option_int_array.h>
#include <dhcp/option_custom.h>
#include <dhcp/option.h>
......@@ -101,7 +102,6 @@ public:
using Dhcpv6Srv::processClientFqdn;
using Dhcpv6Srv::createNameChangeRequests;
using Dhcpv6Srv::createRemovalNameChangeRequest;
using Dhcpv6Srv::createStatusCode;
using Dhcpv6Srv::selectSubnet;
using Dhcpv6Srv::testServerID;
using Dhcpv6Srv::testUnicast;
......@@ -256,8 +256,8 @@ public:
void checkMsgStatusCode(const isc::dhcp::Pkt6Ptr& msg,
uint16_t expected_status)
{
isc::dhcp::OptionCustomPtr status =
boost::dynamic_pointer_cast<isc::dhcp::OptionCustom>
isc::dhcp::Option6StatusCodePtr status =
boost::dynamic_pointer_cast<isc::dhcp::Option6StatusCode>
(msg->getOption(D6O_STATUS_CODE));
// It is ok to not include status success as this is the default
......@@ -273,7 +273,7 @@ public:
// status code option content is just a text explanation
// what went wrong.
EXPECT_EQ(static_cast<uint16_t>(expected_status),
status->readInteger<uint16_t>(0));
status->getStatusCode());
}
}
......
......@@ -22,6 +22,7 @@
#include <dhcp/option6_client_fqdn.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_status_code.h>
#include <dhcp/option_int_array.h>
#include <dhcpsrv/lease.h>
#include <dhcp/tests/iface_mgr_test_config.h>
......@@ -256,16 +257,9 @@ public:
/// Status Code option.
///
/// @return An object representing the Status Code option.
OptionCustomPtr createStatusCode(const uint16_t code,
Option6StatusCodePtr createStatusCode(const uint16_t code,
const std::string& msg) {
OptionDefinition def("status-code", D6O_STATUS_CODE, "record");
def.addRecordField("uint16");
def.addRecordField("string");
OptionCustomPtr opt_status(new OptionCustom(def, Option::V6));
opt_status->writeInteger(code);
if (!msg.empty()) {
opt_status->writeString(msg, 1);
}
Option6StatusCodePtr opt_status(new Option6StatusCode(code, msg));
return (opt_status);
}
......@@ -731,7 +725,8 @@ TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestFwdRev) {
// as if we typed domain-name in lower case.
lease_->hostname_ = "MYHOST.example.com.";
ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(lease_));
Pkt6Ptr pkt(new Pkt6(DHCPREQUEST, 1234));
ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(pkt, lease_));
ASSERT_EQ(1, d2_mgr_.getQueueSize());
verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
......@@ -756,7 +751,8 @@ TEST_F(FqdnDhcpv6SrvTest, noRemovalsWhenDisabled) {
lease_->hostname_ = "MYHOST.example.com.";
// When DDNS is disabled an attempt to send a request will throw.
ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(lease_));
Pkt6Ptr pkt(new Pkt6(DHCPREQUEST, 1234));
ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(pkt, lease_));
}
......@@ -767,7 +763,8 @@ TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestRev) {
lease_->fqdn_rev_ = true;
lease_->hostname_ = "myhost.example.com.";
ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(lease_));
Pkt6Ptr pkt(new Pkt6(DHCPREQUEST, 1234));
ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(pkt, lease_));
ASSERT_EQ(1, d2_mgr_.getQueueSize());
......@@ -785,7 +782,8 @@ TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoUpdate) {
lease_->fqdn_fwd_ = false;
lease_->fqdn_rev_ = false;
ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(lease_));
Pkt6Ptr pkt(new Pkt6(DHCPREQUEST, 1234));
ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(pkt, lease_));
ASSERT_EQ(0, d2_mgr_.getQueueSize());
......@@ -798,7 +796,8 @@ TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoHostname) {
lease_->fqdn_rev_ = true;
lease_->hostname_ = "";
ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(lease_));
Pkt6Ptr pkt(new Pkt6(DHCPREQUEST, 1234));
ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(pkt, lease_));
ASSERT_EQ(0, d2_mgr_.getQueueSize());
......@@ -812,7 +811,8 @@ TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestWrongHostname) {
lease_->fqdn_rev_ = true;
lease_->hostname_ = "myhost..example.com.";
ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(lease_));
Pkt6Ptr pkt(new Pkt6(DHCPREQUEST, 1234));
ASSERT_NO_THROW(srv_->createRemovalNameChangeRequest(pkt, lease_));
ASSERT_EQ(0, d2_mgr_.getQueueSize());
......
......@@ -32,6 +32,7 @@ libkea_dhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
libkea_dhcp___la_SOURCES += option6_iaprefix.cc option6_iaprefix.h
libkea_dhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h
libkea_dhcp___la_SOURCES += option6_client_fqdn.cc option6_client_fqdn.h
libkea_dhcp___la_SOURCES += option6_status_code.cc option6_status_code.h
libkea_dhcp___la_SOURCES += option_vendor.cc option_vendor.h
libkea_dhcp___la_SOURCES += option_vendor_class.cc option_vendor_class.h
libkea_dhcp___la_SOURCES += option_int.h
......
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <dhcp/dhcp6.h>
#include <dhcp/option6_status_code.h>
#include <util/io_utilities.h>
#include <iterator>
#include <sstream>
using namespace isc;
using namespace isc::dhcp;
namespace {
/// @brief Minimum length of the option (when status message is empty).
const size_t OPTION6_STATUS_CODE_MIN_LEN = sizeof(uint16_t);
}; // end of anonymous namespace
namespace isc {
namespace dhcp {
Option6StatusCode::Option6StatusCode(const uint16_t status_code,
const std::string& status_message)
: Option(Option::V6, D6O_STATUS_CODE),
status_code_(status_code), status_message_(status_message) {
}
Option6StatusCode::Option6StatusCode(OptionBufferConstIter begin,
OptionBufferConstIter end)
: Option(Option::V6, D6O_STATUS_CODE),
status_code_(STATUS_Success), status_message_() {
// Parse data
unpack(begin, end);
}
void
Option6StatusCode::pack(isc::util::OutputBuffer& buf) {
// Pack option header.
packHeader(buf);
// Write numeric status code.
buf.writeUint16(getStatusCode());
// If there is any status message, write it.
if (!status_message_.empty()) {
buf.writeData(&status_message_[0], status_message_.size());
}
// Status code has no options, so leave here.
}
void
Option6StatusCode::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
// Make sure that the option is not truncated.
if (std::distance(begin, end) < OPTION6_STATUS_CODE_MIN_LEN) {
isc_throw(OutOfRange, "Status Code option ("
<< D6O_STATUS_CODE << ") truncated");
}
status_code_ = util::readUint16(&(*begin), std::distance(begin, end));
begin += sizeof(uint16_t);
status_message_.assign(begin, end);
}
uint16_t
Option6StatusCode::len() {
return (getHeaderLen() + sizeof(uint16_t) + status_message_.size());
}
std::string
Option6StatusCode::toText(int indent) {
std::ostringstream output;
output << headerToText(indent) << ": "
<< getStatusCodeName() << "(" << getStatusCode() << ") "
<< dataToText();
return (output.str());
}
std::string
Option6StatusCode::dataToText() const {
std::ostringstream output;
// Add status code name and numeric status code.
output << getStatusCodeName() << "(" << getStatusCode() << ") ";
// Include status message in quotes if status code is
// non-empty.
if (!status_message_.empty()) {
output << "\"" << status_message_ << "\"";
} else {
output << "(no status message)";
}
return (output.str());
}
std::string
Option6StatusCode::getStatusCodeName() const {
switch (getStatusCode()) {
case STATUS_Success:
return ("Success");
case STATUS_UnspecFail:
return ("UnspecFail");
case STATUS_NoAddrsAvail:
return ("NoAddrsAvail");
case STATUS_NoBinding:
return ("NoBinding");
case STATUS_NotOnLink:
return ("NotOnLink");
case STATUS_UseMulticast:
return ("UseMulticast");
case STATUS_NoPrefixAvail:
return ("NoPrefixAvail");
case STATUS_UnknownQueryType:
return ("UnknownQueryType");
case STATUS_MalformedQuery:
return ("MalformedQuery");
case STATUS_NotConfigured:
return ("NotConfigured");
case STATUS_NotAllowed:
return ("NotAllowed");
default:
;
}
return ("(unknown status code)");
}
} // end of namespace isc::dhcp
} // end of namespace isc
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef OPTION6_STATUS_CODE_H
#define OPTION6_STATUS_CODE_H
#include <dhcp/option.h>
#include <boost/shared_ptr.hpp>
#include <stdint.h>
#include <string>
namespace isc {
namespace dhcp {
class Option6StatusCode;
/// @brief Pointer to the @c isc::dhcp::Option6StatusCode.
typedef boost::shared_ptr<Option6StatusCode> Option6StatusCodePtr;
/// @brief This class represents Status Code option (13) from RFC3315.
class Option6StatusCode: public Option {
public:
/// @brief Constructor, used for options constructed (during transmission).
///
/// @param status_code Numeric status code, e.g. STATUS_NoAddrsAvail.
/// @param status_message Textual message for the statuscode.
Option6StatusCode(const uint16_t status_code, const std::string& status_message);
/// @brief Constructor, used for received options.
///
/// @throw OutOfRange if specified option is truncated
///
/// @param begin Iterator to first byte of option data
/// @param end Iterator to end of option data (first byte after option end).
Option6StatusCode(OptionBufferConstIter begin, OptionBufferConstIter end);
/// @brief Writes option in wire-format.
///
/// Writes option in wire-format to buf, returns pointer to first unused
/// byte after stored option.
///
/// @param [out] buf Pointer to the output buffer.
virtual void pack(isc::util::OutputBuffer& buf);
/// @brief Parses received buffer.
///
/// @param begin Iterator to first byte of option data
/// @param end Iterator to end of option data (first byte after option end)
virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end);
/// @brief Returns total length of the option.
///
/// The returned length is a sum of the option header and data fields.
virtual uint16_t len();
/// @brief Returns textual representation of the option.
///
/// @param indent Number of spaces before printing text.
virtual std::string toText(int indent = 0);
/// @brief Returns textual representation of the option data.
///
/// This method returns only the status code and the status
/// message. It excludes the option header.
std::string dataToText() const;
/// @brief Returns numeric status code.
uint16_t getStatusCode() const {
return (status_code_);
}
/// @brief Returns the name of the status code.
std::string getStatusCodeName() const;
/// @brief Sets new numeric status code.
///
/// @param status_code New numeric status code.
void setStatusCode(const uint16_t status_code) {
status_code_ = status_code;
}
/// @brief Returns status message.
const std::string& getStatusMessage() const {
return (status_message_);
}
/// @brief Sets new status message.
///
/// @param status_message New status message (empty string is allowed).
void setStatusMessage(const std::string& status_message) {
status_message_ = status_message;
}
private:
/// @brief Numeric status code.
uint16_t status_code_;
/// @brief Textual message.
std::string status_message_;