Files
unison/src/applications/test/three-gpp-http-client-server-test.cc

890 lines
35 KiB
C++

/*
* Copyright (c) 2015 Magister Solutions
*
* SPDX-License-Identifier: GPL-2.0-only
*
* Author: Budiarto Herman <budiarto.herman@magister.fi>
*
*/
#include <ns3/basic-data-calculators.h>
#include <ns3/config.h>
#include <ns3/error-model.h>
#include <ns3/integer.h>
#include <ns3/internet-stack-helper.h>
#include <ns3/ipv4-address-helper.h>
#include <ns3/ipv6-address-helper.h>
#include <ns3/log.h>
#include <ns3/mac48-address.h>
#include <ns3/node.h>
#include <ns3/nstime.h>
#include <ns3/packet.h>
#include <ns3/ptr.h>
#include <ns3/simple-channel.h>
#include <ns3/simple-net-device.h>
#include <ns3/tcp-congestion-ops.h>
#include <ns3/tcp-l4-protocol.h>
#include <ns3/test.h>
#include <ns3/three-gpp-http-client.h>
#include <ns3/three-gpp-http-header.h>
#include <ns3/three-gpp-http-helper.h>
#include <ns3/three-gpp-http-server.h>
#include <list>
#include <sstream>
NS_LOG_COMPONENT_DEFINE("ThreeGppHttpClientServerTest");
using namespace ns3;
// HTTP OBJECT TEST CASE //////////////////////////////////////////////////////
/**
* \ingroup http
* \ingroup applications-test
* \ingroup tests
* A test class which verifies that each HTTP object sent is also received the
* same size.
*
* The test uses a minimalist scenario of one HTTP server and one HTTP client,
* connected through a SimpleChannel. The simulation runs until 3 web pages
* have been successfully downloaded by the client.
*
* The test also collects some statistical information from the simulation for
* informational or debugging purpose. This can be seen by enabling LOG_INFO.
*/
class ThreeGppHttpObjectTestCase : public TestCase
{
public:
/**
* \param name A textual label to briefly describe the test.
* \param rngRun Run index to be used, intended to affect the values produced
* by random number generators throughout the test.
* \param tcpType Type of TCP algorithm to be used by the connection between
* the client and the server. Must be a child type of
* ns3::TcpSocketFactory.
* \param channelDelay Transmission delay between the client and the server
* (and vice versa) which is due to the channel.
* \param bitErrorRate The probability of transmission error between the
* client and the server (and vice versa) in the unit of
* bits.
* \param mtuSize Maximum transmission unit (in bytes) to be used by the
* server model.
* \param useIpv6 If true, IPv6 will be used to address both client and
* server. Otherwise, IPv4 will be used.
*/
ThreeGppHttpObjectTestCase(const std::string& name,
uint32_t rngRun,
const TypeId& tcpType,
const Time& channelDelay,
double bitErrorRate,
uint32_t mtuSize,
bool useIpv6);
private:
/**
* Creates a Node, complete with a TCP/IP stack and address assignment.
* #m_tcpType determines the TCP algorithm installed at the TCP stack.
* #m_useIpv6 determines whether to use IPv4 addressing or IPv6 addressing.
*
* \param[in] channel Pointer to a channel which the node's device will be
* attached to.
* \param[out] assignedAddress The resulting address of the node.
* \return Pointer to the newly created node.
*/
Ptr<Node> CreateSimpleInternetNode(Ptr<SimpleChannel> channel, Address& assignedAddress);
// Inherited from TestCase base class.
void DoRun() override;
void DoTeardown() override;
/**
* \internal
* Internal class used by ThreeGppHttpObjectTestCase. Keep track of the number
* of object and bytes that have been sent and received in the simulation by
* listening to the relevant trace sources.
*/
class ThreeGppHttpObjectTracker
{
public:
/// Creates a new instance with all counters begin at zero.
ThreeGppHttpObjectTracker();
/**
* Shall be invoked when a whole object has been transmitted.
* \param size Size of the whole object (in bytes).
*/
void ObjectSent(uint32_t size);
/**
* Shall be invoked when an object part has been received.
* \param size Size of the object part (in bytes). This amount will be
* accumulated until ObjectReceived() is invoked.
*/
void PartReceived(uint32_t size);
/**
* Shall be invoked after all parts of a complete object have been
* received.
* \param[out] txSize Size of the whole object (in bytes) when it was
* transmitted.
* \param[out] rxSize Size of the whole object (in bytes) received.
* \return True if this receive operation has a matching transmission
* operation (ObjectSent()), otherwise false. Both arguments are
* guaranteed to be replaced with initialized values if the return
* value is true.
*/
bool ObjectReceived(uint32_t& txSize, uint32_t& rxSize);
/// \return True if zero object is currently tracked.
bool IsEmpty() const;
/// \return Number of whole objects that have been received so far.
uint16_t GetNumOfObjectsReceived() const;
private:
/**
* Each entry is the size (in bytes) of object transmitted. A new entry is
* pushed to the back when a new object is transmitted. The frontmost entry
* is then removed when a whole object is received, i.e., it's logically a
* first-in-first-out queue data structure.
*/
std::list<uint32_t> m_objectsSize;
/// The accumulated size (in bytes) of parts of a whole object.
uint32_t m_rxBuffer;
/// Number of whole objects that have been received so far.
uint16_t m_numOfObjectsReceived;
};
// The following defines one tracker for each HTTP object type.
ThreeGppHttpObjectTracker m_requestObjectTracker; ///< Tracker of request objects.
ThreeGppHttpObjectTracker m_mainObjectTracker; ///< Tracker of main objects.
ThreeGppHttpObjectTracker m_embeddedObjectTracker; ///< Tracker of embedded objects.
// CALLBACK TO TRACE SOURCES.
/**
* Connected with `TxMainObjectRequest` trace source of the client.
* Updates #m_requestObjectTracker.
* \param packet The packet of main object sent.
*/
void ClientTxMainObjectRequestCallback(Ptr<const Packet> packet);
/**
* Connected with `TxEmbeddedObjectRequest` trace source of the client.
* Updates #m_requestObjectTracker.
* \param packet The packet of embedded object sent.
*/
void ClientTxEmbeddedObjectRequestCallback(Ptr<const Packet> packet);
/**
* Connected with `Rx` trace source of the server.
* Updates #m_requestObjectTracker and perform some tests on the packet and
* the size of the object.
* \param packet The packet received.
* \param from The address where the packet originates from.
*/
void ServerRxCallback(Ptr<const Packet> packet, const Address& from);
/**
* Connected with `MainObject` trace source of the server.
* Updates #m_mainObjectTracker.
* \param size Size of the generated main object (in bytes).
*/
void ServerMainObjectCallback(uint32_t size);
/**
* Connected with `RxMainObjectPacket` trace source of the client.
* Updates #m_mainObjectTracker and perform some tests on the packet.
* \param packet The packet received.
*/
void ClientRxMainObjectPacketCallback(Ptr<const Packet> packet);
/**
* Connected with `RxMainObject` trace source of the client. Updates
* #m_mainObjectTracker and perform some tests on the size of the object.
* \param httpClient Pointer to the application.
* \param packet Full packet received by application.
*/
void ClientRxMainObjectCallback(Ptr<const ThreeGppHttpClient> httpClient,
Ptr<const Packet> packet);
/**
* Connected with `EmbeddedObject` trace source of the server.
* Updates #m_embeddedObjectTracker.
* \param size Size of the generated embedded object (in bytes).
*/
void ServerEmbeddedObjectCallback(uint32_t size);
/**
* Connected with `RxEmbeddedObjectPacket` trace source of the client.
* Updates #m_embeddedObjectTracker and perform some tests on the packet.
* \param packet The packet received.
*/
void ClientRxEmbeddedObjectPacketCallback(Ptr<const Packet> packet);
/**
* Connected with `RxEmbeddedObject` trace source of the client. Updates
* #m_embeddedObjectTracker and perform some tests on the size of the object.
* \param httpClient Pointer to the application.
* \param packet Full packet received by application.
*/
void ClientRxEmbeddedObjectCallback(Ptr<const ThreeGppHttpClient> httpClient,
Ptr<const Packet> packet);
/**
* Connected with `StateTransition` trace source of the client.
* Increments #m_numOfPagesReceived when the client enters READING state.
* \param oldState The name of the previous state.
* \param newState The name of the current state.
*/
void ClientStateTransitionCallback(const std::string& oldState, const std::string& newState);
/**
* Connected with `RxDelay` trace source of the client.
* Updates the statistics in #m_delayCalculator.
* \param delay The packet one-trip delay time.
* \param from The address of the device where the packet originates from.
*/
void ClientRxDelayCallback(const Time& delay, const Address& from);
/**
* Connected with `RxRtt` trace source of the client.
* Updates the statistics in #m_rttCalculator.
* \param rtt The packet round trip delay time.
* \param from The address of the device where the packet originates from.
*/
void ClientRxRttCallback(const Time& rtt, const Address& from);
/**
* Connected with `PhyRxDrop` trace source of both the client's and server's
* devices. Increments #m_numOfPacketDrops.
* \param packet Pointer to the packet being dropped.
*/
void DeviceDropCallback(Ptr<const Packet> packet);
/**
* Dummy event
*/
void ProgressCallback();
// THE PARAMETERS OF THE TEST CASE.
uint32_t m_rngRun; ///< Determines the set of random values generated.
TypeId m_tcpType; ///< TCP algorithm used.
Time m_channelDelay; ///< %Time needed by a packet to propagate.
uint32_t m_mtuSize; ///< Maximum transmission unit (in bytes).
bool m_useIpv6; ///< Whether to use IPv6 or IPv4.
// OTHER MEMBER VARIABLES.
/// Receive error model to be attached to the devices of both directions.
Ptr<RateErrorModel> m_errorModel;
/// Begins with 0. Simulation stops if this reaches 3.
uint16_t m_numOfPagesReceived;
/// Number of packets dropped because of #m_errorModel.
uint16_t m_numOfPacketDrops;
/// Installs TCP/IP stack on the nodes.
InternetStackHelper m_internetStackHelper;
/// Assigns IPv4 addresses to the nodes.
Ipv4AddressHelper m_ipv4AddressHelper;
/// Assigns IPv6 addresses to the nodes.
Ipv6AddressHelper m_ipv6AddressHelper;
/// Keeps statistical information of one-trip delays (in seconds).
Ptr<MinMaxAvgTotalCalculator<double>> m_delayCalculator;
/// Keeps statistical information of round-trip delays (in seconds).
Ptr<MinMaxAvgTotalCalculator<double>> m_rttCalculator;
}; // end of `class HttpClientServerTestCase`
ThreeGppHttpObjectTestCase::ThreeGppHttpObjectTestCase(const std::string& name,
uint32_t rngRun,
const TypeId& tcpType,
const Time& channelDelay,
double bitErrorRate,
uint32_t mtuSize,
bool useIpv6)
: TestCase(name),
m_rngRun(rngRun),
m_tcpType(tcpType),
m_channelDelay(channelDelay),
m_mtuSize(mtuSize),
m_useIpv6(useIpv6),
m_numOfPagesReceived(0),
m_numOfPacketDrops(0)
{
NS_LOG_FUNCTION(this << GetName());
// NS_ASSERT (tcpType.IsChildOf (TypeId::LookupByName ("ns3::TcpSocketBase")));
NS_ASSERT(channelDelay.IsPositive());
m_errorModel = CreateObject<RateErrorModel>();
m_errorModel->SetRate(bitErrorRate);
m_errorModel->SetUnit(RateErrorModel::ERROR_UNIT_BIT);
m_ipv4AddressHelper.SetBase(Ipv4Address("10.0.0.0"),
Ipv4Mask("255.0.0.0"),
Ipv4Address("0.0.0.1"));
m_ipv6AddressHelper.SetBase(Ipv6Address("2001:1::"), Ipv6Prefix(64), Ipv6Address("::1"));
m_delayCalculator = CreateObject<MinMaxAvgTotalCalculator<double>>();
m_rttCalculator = CreateObject<MinMaxAvgTotalCalculator<double>>();
}
Ptr<Node>
ThreeGppHttpObjectTestCase::CreateSimpleInternetNode(Ptr<SimpleChannel> channel,
Address& assignedAddress)
{
NS_LOG_FUNCTION(this << channel);
Ptr<SimpleNetDevice> dev = CreateObject<SimpleNetDevice>();
dev->SetAddress(Mac48Address::Allocate());
dev->SetChannel(channel);
dev->SetReceiveErrorModel(m_errorModel);
Ptr<Node> node = CreateObject<Node>();
node->AddDevice(dev);
m_internetStackHelper.Install(node);
// Assign IP address according to the selected Ip version.
const auto httpPort = 80;
if (m_useIpv6)
{
Ipv6InterfaceContainer ipv6Ifs = m_ipv6AddressHelper.Assign(NetDeviceContainer(dev));
NS_ASSERT(ipv6Ifs.GetN() == 1);
assignedAddress = Inet6SocketAddress(ipv6Ifs.GetAddress(0, 0), httpPort);
}
else
{
Ipv4InterfaceContainer ipv4Ifs = m_ipv4AddressHelper.Assign(NetDeviceContainer(dev));
NS_ASSERT(ipv4Ifs.GetN() == 1);
assignedAddress = InetSocketAddress(ipv4Ifs.GetAddress(0, 0), httpPort);
}
NS_LOG_DEBUG(this << " node is assigned to " << assignedAddress << ".");
// Set the TCP algorithm.
Ptr<TcpL4Protocol> tcp = node->GetObject<TcpL4Protocol>();
tcp->SetAttribute("SocketType", TypeIdValue(m_tcpType));
// Connect with the trace source that informs about packet drop due to error.
dev->TraceConnectWithoutContext(
"PhyRxDrop",
MakeCallback(&ThreeGppHttpObjectTestCase::DeviceDropCallback, this));
return node;
}
void
ThreeGppHttpObjectTestCase::DoRun()
{
NS_LOG_FUNCTION(this << GetName());
Config::SetGlobal("RngRun", UintegerValue(m_rngRun));
NS_LOG_INFO(this << " Running test case " << GetName());
/*
* Create topology:
*
* Server Node Client Node
* +-----------------+ +-----------------+
* | HTTP Server | | HTTP Client |
* | Application | | Application |
* +-----------------+ +-----------------+
* | TCP | | TCP |
* +-----------------+ +-----------------+
* | IPv4/v6 | | IPv4/v6 |
* +-----------------+ +-----------------+
* | Simple NetDev | | Simple NetDev |
* +-----------------+ +-----------------+
* | |
* | |
* +----------------------------+
* Simple Channel
*/
// Channel.
Ptr<SimpleChannel> channel = CreateObject<SimpleChannel>();
channel->SetAttribute("Delay", TimeValue(m_channelDelay));
// Server node.
Address serverAddress;
Ptr<Node> serverNode = CreateSimpleInternetNode(channel, serverAddress);
ThreeGppHttpServerHelper serverHelper(serverAddress);
ApplicationContainer serverApplications = serverHelper.Install(serverNode);
NS_TEST_ASSERT_MSG_EQ(serverApplications.GetN(),
1,
"Invalid number of HTTP servers has been installed");
Ptr<ThreeGppHttpServer> httpServer = serverApplications.Get(0)->GetObject<ThreeGppHttpServer>();
NS_TEST_ASSERT_MSG_NE(httpServer,
nullptr,
"HTTP server installation fails to produce a proper type");
httpServer->SetMtuSize(m_mtuSize);
// Client node.
Address clientAddress;
Ptr<Node> clientNode = CreateSimpleInternetNode(channel, clientAddress);
ThreeGppHttpClientHelper clientHelper(serverAddress);
ApplicationContainer clientApplications = clientHelper.Install(clientNode);
NS_TEST_ASSERT_MSG_EQ(clientApplications.GetN(),
1,
"Invalid number of HTTP clients has been installed");
Ptr<ThreeGppHttpClient> httpClient = clientApplications.Get(0)->GetObject<ThreeGppHttpClient>();
NS_TEST_ASSERT_MSG_NE(httpClient,
nullptr,
"HTTP client installation fails to produce a proper type");
// Uplink (requests) trace sources.
bool traceSourceConnected = httpClient->TraceConnectWithoutContext(
"TxMainObjectRequest",
MakeCallback(&ThreeGppHttpObjectTestCase::ClientTxMainObjectRequestCallback, this));
NS_ASSERT(traceSourceConnected);
traceSourceConnected = httpClient->TraceConnectWithoutContext(
"TxEmbeddedObjectRequest",
MakeCallback(&ThreeGppHttpObjectTestCase::ClientTxEmbeddedObjectRequestCallback, this));
NS_ASSERT(traceSourceConnected);
traceSourceConnected = httpServer->TraceConnectWithoutContext(
"Rx",
MakeCallback(&ThreeGppHttpObjectTestCase::ServerRxCallback, this));
NS_ASSERT(traceSourceConnected);
// Downlink (main objects) trace sources.
traceSourceConnected = httpServer->TraceConnectWithoutContext(
"MainObject",
MakeCallback(&ThreeGppHttpObjectTestCase::ServerMainObjectCallback, this));
NS_ASSERT(traceSourceConnected);
traceSourceConnected = httpClient->TraceConnectWithoutContext(
"RxMainObjectPacket",
MakeCallback(&ThreeGppHttpObjectTestCase::ClientRxMainObjectPacketCallback, this));
NS_ASSERT(traceSourceConnected);
traceSourceConnected = httpClient->TraceConnectWithoutContext(
"RxMainObject",
MakeCallback(&ThreeGppHttpObjectTestCase::ClientRxMainObjectCallback, this));
NS_ASSERT(traceSourceConnected);
// Downlink (embedded objects) trace sources.
traceSourceConnected = httpServer->TraceConnectWithoutContext(
"EmbeddedObject",
MakeCallback(&ThreeGppHttpObjectTestCase::ServerEmbeddedObjectCallback, this));
NS_ASSERT(traceSourceConnected);
traceSourceConnected = httpClient->TraceConnectWithoutContext(
"RxEmbeddedObjectPacket",
MakeCallback(&ThreeGppHttpObjectTestCase::ClientRxEmbeddedObjectPacketCallback, this));
NS_ASSERT(traceSourceConnected);
traceSourceConnected = httpClient->TraceConnectWithoutContext(
"RxEmbeddedObject",
MakeCallback(&ThreeGppHttpObjectTestCase::ClientRxEmbeddedObjectCallback, this));
NS_ASSERT(traceSourceConnected);
// Other trace sources.
traceSourceConnected = httpClient->TraceConnectWithoutContext(
"StateTransition",
MakeCallback(&ThreeGppHttpObjectTestCase::ClientStateTransitionCallback, this));
NS_ASSERT(traceSourceConnected);
traceSourceConnected = httpClient->TraceConnectWithoutContext(
"RxDelay",
MakeCallback(&ThreeGppHttpObjectTestCase::ClientRxDelayCallback, this));
NS_ASSERT(traceSourceConnected);
traceSourceConnected = httpClient->TraceConnectWithoutContext(
"RxRtt",
MakeCallback(&ThreeGppHttpObjectTestCase::ClientRxRttCallback, this));
NS_ASSERT(traceSourceConnected);
Simulator::Schedule(Seconds(1.0), &ThreeGppHttpObjectTestCase::ProgressCallback, this);
/*
* Here we don't set the simulation stop time. During the run, the simulation
* will stop immediately after the client has completely received the third
* web page.
*/
Simulator::Run();
// Dump some statistical information about the simulation.
NS_LOG_INFO(this << " Total request objects received: "
<< m_requestObjectTracker.GetNumOfObjectsReceived() << " object(s).");
NS_LOG_INFO(this << " Total main objects received: "
<< m_mainObjectTracker.GetNumOfObjectsReceived() << " object(s).");
NS_LOG_INFO(this << " Total embedded objects received: "
<< m_embeddedObjectTracker.GetNumOfObjectsReceived() << " object(s).");
NS_LOG_INFO(this << " One-trip delays:"
<< " average=" << m_delayCalculator->getMean() << " min="
<< m_delayCalculator->getMin() << " max=" << m_delayCalculator->getMax());
NS_LOG_INFO(this << " Round-trip delays:"
<< " average=" << m_rttCalculator->getMean() << " min="
<< m_rttCalculator->getMin() << " max=" << m_rttCalculator->getMax());
NS_LOG_INFO(this << " Number of packets dropped by the devices: " << m_numOfPacketDrops
<< " packet(s).");
// Some post-simulation tests.
NS_TEST_EXPECT_MSG_EQ(m_numOfPagesReceived, 3, "Unexpected number of web pages processed.");
NS_TEST_EXPECT_MSG_EQ(m_requestObjectTracker.IsEmpty(),
true,
"Tracker of request objects detected irrelevant packet(s).");
NS_TEST_EXPECT_MSG_EQ(m_mainObjectTracker.IsEmpty(),
true,
"Tracker of main objects detected irrelevant packet(s).");
NS_TEST_EXPECT_MSG_EQ(m_embeddedObjectTracker.IsEmpty(),
true,
"Tracker of embedded objects detected irrelevant packet(s).");
Simulator::Destroy();
} // end of `void HttpClientServerTestCase::DoRun ()`
void
ThreeGppHttpObjectTestCase::DoTeardown()
{
NS_LOG_FUNCTION(this << GetName());
}
ThreeGppHttpObjectTestCase::ThreeGppHttpObjectTracker::ThreeGppHttpObjectTracker()
: m_rxBuffer(0),
m_numOfObjectsReceived(0)
{
NS_LOG_FUNCTION(this);
}
void
ThreeGppHttpObjectTestCase::ThreeGppHttpObjectTracker::ObjectSent(uint32_t size)
{
NS_LOG_FUNCTION(this << size);
m_objectsSize.push_back(size);
}
void
ThreeGppHttpObjectTestCase::ThreeGppHttpObjectTracker::PartReceived(uint32_t size)
{
NS_LOG_FUNCTION(this << size);
m_rxBuffer += size;
}
bool
ThreeGppHttpObjectTestCase::ThreeGppHttpObjectTracker::ObjectReceived(uint32_t& txSize,
uint32_t& rxSize)
{
NS_LOG_FUNCTION(this);
if (m_objectsSize.empty())
{
return false;
}
// Set output values.
txSize = m_objectsSize.front();
rxSize = m_rxBuffer;
// Reset counters.
m_objectsSize.pop_front();
m_rxBuffer = 0;
m_numOfObjectsReceived++;
return true;
}
bool
ThreeGppHttpObjectTestCase::ThreeGppHttpObjectTracker::IsEmpty() const
{
return (m_objectsSize.empty() && (m_rxBuffer == 0));
}
uint16_t
ThreeGppHttpObjectTestCase::ThreeGppHttpObjectTracker::GetNumOfObjectsReceived() const
{
return m_numOfObjectsReceived;
}
void
ThreeGppHttpObjectTestCase::ClientTxMainObjectRequestCallback(Ptr<const Packet> packet)
{
NS_LOG_FUNCTION(this << packet << packet->GetSize());
m_requestObjectTracker.ObjectSent(packet->GetSize());
}
void
ThreeGppHttpObjectTestCase::ClientTxEmbeddedObjectRequestCallback(Ptr<const Packet> packet)
{
NS_LOG_FUNCTION(this << packet << packet->GetSize());
m_requestObjectTracker.ObjectSent(packet->GetSize());
}
void
ThreeGppHttpObjectTestCase::ServerRxCallback(Ptr<const Packet> packet, const Address& from)
{
NS_LOG_FUNCTION(this << packet << packet->GetSize() << from);
// Check the header in packet
Ptr<Packet> copy = packet->Copy();
ThreeGppHttpHeader httpHeader;
NS_TEST_ASSERT_MSG_EQ(copy->RemoveHeader(httpHeader),
22,
"Error finding ThreeGppHttpHeader in a packet received by the server");
NS_TEST_ASSERT_MSG_GT(httpHeader.GetClientTs(),
Seconds(0.0),
"Request object's client TS is unexpectedly non-positive");
m_requestObjectTracker.PartReceived(packet->GetSize());
/*
* Request objects are assumed to be small and to not typically split. So we
* immediately follow by concluding the receive of a whole request object.
*/
uint32_t txSize = 0;
uint32_t rxSize = 0;
bool isSent = m_requestObjectTracker.ObjectReceived(txSize, rxSize);
NS_TEST_ASSERT_MSG_EQ(isSent, true, "Server receives one too many request object");
NS_TEST_ASSERT_MSG_EQ(txSize,
rxSize,
"Transmitted size and received size of request object differ");
}
void
ThreeGppHttpObjectTestCase::ServerMainObjectCallback(uint32_t size)
{
NS_LOG_FUNCTION(this << size);
m_mainObjectTracker.ObjectSent(size);
}
void
ThreeGppHttpObjectTestCase::ClientRxMainObjectPacketCallback(Ptr<const Packet> packet)
{
NS_LOG_FUNCTION(this << packet << packet->GetSize());
m_mainObjectTracker.PartReceived(packet->GetSize());
}
void
ThreeGppHttpObjectTestCase::ClientRxMainObjectCallback(Ptr<const ThreeGppHttpClient> httpClient,
Ptr<const Packet> packet)
{
NS_LOG_FUNCTION(this << httpClient << httpClient->GetNode()->GetId());
// Verify the header in the packet.
Ptr<Packet> copy = packet->Copy();
ThreeGppHttpHeader httpHeader;
NS_TEST_ASSERT_MSG_EQ(copy->RemoveHeader(httpHeader),
22,
"Error finding ThreeGppHttpHeader in a packet received by the server");
NS_TEST_ASSERT_MSG_EQ(httpHeader.GetContentType(),
ThreeGppHttpHeader::MAIN_OBJECT,
"Invalid content type in the received packet");
NS_TEST_ASSERT_MSG_GT(httpHeader.GetClientTs(),
Seconds(0.0),
"Main object's client TS is unexpectedly non-positive");
NS_TEST_ASSERT_MSG_GT(httpHeader.GetServerTs(),
Seconds(0.0),
"Main object's server TS is unexpectedly non-positive");
uint32_t txSize = 0;
uint32_t rxSize = 0;
bool isSent = m_mainObjectTracker.ObjectReceived(txSize, rxSize);
NS_TEST_ASSERT_MSG_EQ(isSent, true, "Client receives one too many main object");
NS_TEST_ASSERT_MSG_EQ(txSize,
rxSize,
"Transmitted size and received size of main object differ");
NS_TEST_ASSERT_MSG_EQ(httpHeader.GetContentLength(),
rxSize,
"Actual main object packet size and received size of main object differ");
}
void
ThreeGppHttpObjectTestCase::ServerEmbeddedObjectCallback(uint32_t size)
{
NS_LOG_FUNCTION(this << size);
m_embeddedObjectTracker.ObjectSent(size);
}
void
ThreeGppHttpObjectTestCase::ClientRxEmbeddedObjectPacketCallback(Ptr<const Packet> packet)
{
NS_LOG_FUNCTION(this << packet << packet->GetSize());
m_embeddedObjectTracker.PartReceived(packet->GetSize());
}
void
ThreeGppHttpObjectTestCase::ClientRxEmbeddedObjectCallback(Ptr<const ThreeGppHttpClient> httpClient,
Ptr<const Packet> packet)
{
NS_LOG_FUNCTION(this << httpClient << httpClient->GetNode()->GetId());
// Verify the header in the packet.
Ptr<Packet> copy = packet->Copy();
ThreeGppHttpHeader httpHeader;
NS_TEST_ASSERT_MSG_EQ(copy->RemoveHeader(httpHeader),
22,
"Error finding ThreeGppHttpHeader in a packet received by the server");
NS_TEST_ASSERT_MSG_EQ(httpHeader.GetContentType(),
ThreeGppHttpHeader::EMBEDDED_OBJECT,
"Invalid content type in the received packet");
NS_TEST_ASSERT_MSG_GT(httpHeader.GetClientTs(),
Seconds(0.0),
"Embedded object's client TS is unexpectedly non-positive");
NS_TEST_ASSERT_MSG_GT(httpHeader.GetServerTs(),
Seconds(0.0),
"Embedded object's server TS is unexpectedly non-positive");
uint32_t txSize = 0;
uint32_t rxSize = 0;
bool isSent = m_embeddedObjectTracker.ObjectReceived(txSize, rxSize);
NS_TEST_ASSERT_MSG_EQ(isSent, true, "Client receives one too many embedded object");
NS_TEST_ASSERT_MSG_EQ(txSize,
rxSize,
"Transmitted size and received size of embedded object differ");
NS_TEST_ASSERT_MSG_EQ(
httpHeader.GetContentLength(),
rxSize,
"Actual embedded object packet size and received size of embedded object differ");
}
void
ThreeGppHttpObjectTestCase::ClientStateTransitionCallback(const std::string& oldState,
const std::string& newState)
{
NS_LOG_FUNCTION(this << oldState << newState);
if (newState == "READING")
{
m_numOfPagesReceived++;
if (m_numOfPagesReceived >= 3)
{
// We have processed 3 web pages and that should be enough for this test.
NS_LOG_LOGIC(this << " Test is stopping now.");
Simulator::Stop();
}
}
}
void
ThreeGppHttpObjectTestCase::ProgressCallback()
{
NS_LOG_INFO("Simulator time now: " << Simulator::Now().As(Time::S) << ".");
Simulator::Schedule(Seconds(1.0), &ThreeGppHttpObjectTestCase::ProgressCallback, this);
}
void
ThreeGppHttpObjectTestCase::ClientRxDelayCallback(const Time& delay, const Address& from)
{
NS_LOG_FUNCTION(this << delay.As(Time::S) << from);
m_delayCalculator->Update(delay.GetSeconds());
}
void
ThreeGppHttpObjectTestCase::ClientRxRttCallback(const Time& rtt, const Address& from)
{
NS_LOG_FUNCTION(this << rtt.As(Time::S) << from);
m_rttCalculator->Update(rtt.GetSeconds());
}
void
ThreeGppHttpObjectTestCase::DeviceDropCallback(Ptr<const Packet> packet)
{
NS_LOG_FUNCTION(this << packet << packet->GetSize());
m_numOfPacketDrops++;
}
// TEST SUITE /////////////////////////////////////////////////////////////////
/**
* \ingroup http
* \ingroup applications-test
* \ingroup tests
* A test class for running several system tests which validate the web
* browsing traffic model.
*
* The tests cover the combinations of the following parameters:
* - the use of NewReno (ns-3's default)
* - various lengths of channel delay: 3 ms, 30 ms, and 300 ms;
* - the existence of transmission error;
* - different MTU (maximum transmission unit) sizes;
* - IPv4 and IPv6; and
* - the use of different set of random numbers.
*
* The _fullness_ parameter specified when running the test framework will
* determine the number of test cases created by this test suite.
*/
class ThreeGppHttpClientServerTestSuite : public TestSuite
{
public:
/// Instantiate the test suite.
ThreeGppHttpClientServerTestSuite()
: TestSuite("applications-three-gpp-http-client-server", Type::SYSTEM)
{
// LogComponentEnable ("ThreeGppHttpClientServerTest", LOG_INFO);
// LogComponentEnable ("ThreeGppHttpClient", LOG_INFO);
// LogComponentEnable ("ThreeGppHttpServer", LOG_INFO);
// LogComponentEnableAll (LOG_PREFIX_ALL);
Time channelDelay[] = {MilliSeconds(3), MilliSeconds(30), MilliSeconds(300)};
double bitErrorRate[] = {0.0, 5.0e-6};
uint32_t mtuSize[] = {536, 1460};
uint32_t run = 1;
while (run <= 100)
{
for (uint32_t i1 = 0; i1 < 3; i1++)
{
for (uint32_t i2 = 0; i2 < 2; i2++)
{
for (uint32_t i3 = 0; i3 < 2; i3++)
{
AddHttpObjectTestCase(run++,
channelDelay[i1],
bitErrorRate[i2],
mtuSize[i3],
false);
AddHttpObjectTestCase(run++,
channelDelay[i1],
bitErrorRate[i2],
mtuSize[i3],
true);
}
}
}
}
}
private:
/**
* Creates a test case with the given parameters.
*
* \param rngRun Run index to be used, intended to affect the values produced
* by random number generators throughout the test.
* \param channelDelay Transmission delay between the client and the server
* (and vice versa) which is due to the channel.
* \param bitErrorRate The probability of transmission error between the
* client and the server (and vice versa) in the unit of
* bits.
* \param mtuSize Maximum transmission unit (in bytes) to be used by the
* server model.
* \param useIpv6 If true, IPv6 will be used to address both client and
* server. Otherwise, IPv4 will be used.
*/
void AddHttpObjectTestCase(uint32_t rngRun,
const Time& channelDelay,
double bitErrorRate,
uint32_t mtuSize,
bool useIpv6)
{
std::ostringstream name;
name << "Run #" << rngRun;
name << " delay=" << channelDelay.As(Time::MS);
name << " ber=" << bitErrorRate;
name << " mtu=" << mtuSize;
if (useIpv6)
{
name << " IPv6";
}
else
{
name << " IPv4";
}
// Assign higher fullness for tests with higher RngRun.
TestCase::Duration testDuration = TestCase::Duration::QUICK;
if (rngRun > 20)
{
testDuration = TestCase::Duration::EXTENSIVE;
}
if (rngRun > 50)
{
testDuration = TestCase::Duration::TAKES_FOREVER;
}
AddTestCase(new ThreeGppHttpObjectTestCase(name.str(),
rngRun,
TcpNewReno::GetTypeId(),
channelDelay,
bitErrorRate,
mtuSize,
useIpv6),
testDuration);
}
}; // end of class `ThreeGppHttpClientServerTestSuite`
/// The global instance of the `three-gpp-http-client-server` system test.
static ThreeGppHttpClientServerTestSuite g_httpClientServerTestSuiteInstance;